Skip to content

[Docs] Set correct document url #13

[Docs] Set correct document url

[Docs] Set correct document url #13

Workflow file for this run

name: Flutter CI/CD Pipeline
on:
push:
branches: [develop]
pull_request:
branches: [develop]
jobs:
build:
runs-on: macos-latest
env:
ANDROID: ${{ secrets.ANDROID_HOME }}
ANDROID_SDK: ${{ secrets.ANDROID_HOME }}
ARCHIVE_DIR: untracked-files-archive
steps:
# 1. CHECKOUT ──────────────────────────────
# Checkout repository code
- name: 1.1 Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
- name: Validate required secrets
run: |
set -euo pipefail
missing=0
check() { [ -z "$1" ] && echo "Missing secret: $2" && missing=1 || true; }
check "${{ secrets.KEYSTORE_BASE64 }}" KEYSTORE_BASE64
[ "$missing" -eq 0 ] || exit 1
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
cache: gradle
# 2. FLUTTER SDK SETUP ─────────────────────
# Set up the Flutter toolchain (add fail-fast)
- name: 2.1 Set up Flutter SDK
uses: subosito/flutter-action@v2.18.0
with:
flutter-version: '3.32.0'
# 2.2 Tool Sanity Check
# Confirm Flutter and Dart are installed properly (fail early)
- name: Sanity check Flutter & Dart
run: |
set -euo pipefail
flutter --version
dart --version
# 3. ENVIRONMENT & DEPENDENCIES ────────────
# (A) Cache Dart & Flutter pub packages
- name: Cache Flutter packages
uses: actions/cache@v4
with:
path: |
~/.pub-cache
.dart_tool
key: ${{ runner.os }}-pub-${{ hashFiles('**/pubspec.lock') }}
restore-keys: |
${{ runner.os }}-pub-
# (B) Cache CocoaPods
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: |
ios/Pods
~/.cocoapods
~/Library/Caches/CocoaPods
key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}
restore-keys: |
${{ runner.os }}-pods-
# Create .env file for API endpoints (add fail-fast)
- name: 3.1 Create .env file
run: |
set -euo pipefail
echo "DEV_API_BASE_URL=${{ secrets.DEV_API_BASE_URL }}" > .env
echo "STAGE_API_BASE_URL=${{ secrets.STAGE_API_BASE_URL }}" >> .env
echo "PROD_API_BASE_URL=${{ secrets.PROD_API_BASE_URL }}" >> .env
# 4. ANDROID + COMMON BUILD ────────────────
# Clean previous builds (fail-fast)
- name: 4.1 Flutter clean
run: set -euo pipefail; flutter clean
# Install dependencies and run code generation (fail-fast)
- name: 4.2 Pub get & code generation
run: |
set -euo pipefail
flutter pub get
dart run build_runner build --delete-conflicting-outputs
# Run all unit and widget tests (including goldens)
- name: 4.3 Run unit and widget tests
run: set -euo pipefail; flutter test
env:
CI: true
# # Archive failed golden images if 'flutter test' fails
# - name: Archive failed golden images if any
# if: failure()
# run: |
# set -euo pipefail
# mkdir -p failed-goldens-archive
# files=$(find test -type d -name failures -exec find {} -type f \;)
# if [ -n "$files" ]; then
# tar -czf failed-golden-images.tar.gz --exclude-vcs $files
# fi
# Upload the archive as an artifact on failure
- name: Upload failed golden images
if: failure()
uses: actions/upload-artifact@v4.6.2
with:
name: failed-golden-images
# path: failed-golden-images.tar.gz
path: test/**/failures/**
if-no-files-found: ignore
# Install Flutterfire CLI for Firebase integration (fail-fast)
- name: 4.4 Install Flutterfire CLI
run: |
set -euo pipefail
dart pub global activate flutterfire_cli
echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
# 5. SonarQube Analysis ─────────────
# Run Flutter test with coverage
- name: Run tests and collect coverage
run: flutter test --coverage
# Run SonarQube Scanner for code quality analysis and upload report to sonarqube server
- name: 4.5 Run SonarQube Scanner
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: https://sonarqube.solz.me
with:
args: >
-Dsonar.projectKey=Skelter-Flutter
-Dsonar.projectName=Skelter-Flutter
-Dsonar.sources=lib
-Dsonar.tests=test
-Dsonar.sourceEncoding=UTF-8
-Dsonar.exclusions=test/**/*_test.mocks.dart,lib/**/*.g.dart
-Dsonar.dart.analyzer.mode=DETECT
-Dsonar.dart.analyzer.options.override=false
-Dsonar.flutter.coverage.reportPath=coverage/lcov.info
-Dsonar.flutter.tests.reportPath=test-report.xml
# 6. ANDROID SIGNING AND BUILD ─────────────
# Decode Android keystore (fail-fast)
- name: 5.1 Decode Android Keystore
run: |
set -euo pipefail
decode() { base64 -d 2>/dev/null || base64 -D; }
echo "${{ secrets.KEYSTORE_BASE64 }}" | decode > android/app/keystore.jks
# Create key.properties for signing (fail-fast)
- name: 5.2 Create key.properties
run: |
set -euo pipefail
echo "storePassword=${{ secrets.KEYSTORE_PASSWORD }}" > android/key.properties
echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/key.properties
echo "storeFile=keystore.jks" >> android/key.properties
- name: Setup Gradle cache
uses: gradle/actions/setup-gradle@v4
# Build Android APKs for dev and stage flavors (fail-fast)
- name: 5.3 Build Android APKs (dev and stage)
run: |
set -euo pipefail
flutter build apk --flavor dev --dart-define=APP_FLAVOR=DEV --release
flutter build apk --flavor stage --dart-define=APP_FLAVOR=STAGE --release
flutter build apk --flavor prod --dart-define=APP_FLAVOR=PROD --release
# # 7. iOS SETUP ─────────────────────────────
# # Set up Xcode environment
# - name: 6.1 Setup Xcode environment
# uses: maxim-lobanov/setup-xcode@v1.6.0
# with:
# xcode-version: latest-stable
#
# # Install CocoaPods and dependencies (fail-fast)
# - name: 6.2 Fresh CocoaPods install
# run: |
# set -euo pipefail
# cd ios
# pod install --repo-update
# cd ..
#
# # 8. iOS CERTIFICATES, PROFILES, AND BUILD ─────────────────────
# # --- DEV FLAVOR ---
# # Decode dev certificates and ExportOptions (fail-fast)
# - name: 7.1.1 Decode iOS Certificates & Profiles (dev)
# run: |
# set -euo pipefail
# decode() { base64 -d 2>/dev/null || base64 -D; }
# echo "${{ secrets.P12_BASE64 }}" | decode > certificate.p12
# echo "${{ secrets.P12_PASSWORD }}" > p12_password.txt
# echo "${{ secrets.PROFILE_DEV_BASE64 }}" | decode > profile_dev.mobileprovision
# echo "${{ secrets.EXPORTOPTIONS_DEV_BASE64 }}" | decode > ExportOptionsDev.plist
#
# # Set up keychain and import certificate (fail-fast)
# - name: 7.1.2 Set up iOS keychain & import certificate
# run: |
# set -euo pipefail
# security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
# security set-keychain-settings -lut 21600 build.keychain
# security list-keychains -d user -s build.keychain login.keychain
# security default-keychain -s build.keychain
# security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
# security import certificate.p12 -k build.keychain -P "$(cat p12_password.txt)" -A
# security set-key-partition-list -S apple-tool:,apple: -k "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
#
#
# # create a new folder for profiles
# - name: create a new folder for profiles
# run: |
# set -euo pipefail
# mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
#
# # Install provisioning profile for dev flavor (fail-fast)
# - name: 7.1.3 Install provisioning profile (dev)
# run: |
# set -euo pipefail
# cp profile_dev.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
#
# # Build IPA for dev flavor (fail-fast)
# - name: 7.1.4 Build IPA (dev)
# run: set -euo pipefail; flutter build ipa --flavor dev --export-options-plist=ExportOptionsDev.plist --release
#
# # Rename IPA for dev flavor (fail-fast)
# - name: Rename IPA (dev)
# run: |
# set -euo pipefail;
# IPA=$(ls -1t build/ios/ipa/*.ipa | head -n1)
# mv "$IPA" build/ios/ipa/app-dev.ipa
#
# # --- STAGE FLAVOR ---
# # Install provisioning profile for stage flavor (fail-fast)
# - name: 7.2.1 Install provisioning profile (stage)
# run: |
# set -euo pipefail
# decode() { base64 -d 2>/dev/null || base64 -D; }
# echo "${{ secrets.PROFILE_STAGE_BASE64 }}" | decode > profile_stage.mobileprovision
# cp profile_stage.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
# echo "${{ secrets.EXPORTOPTIONS_STAGE_BASE64 }}" | decode > ExportOptionsStage.plist
#
# # Build IPA for stage flavor (fail-fast)
# - name: 7.2.2 Build IPA (stage)
# run: set -euo pipefail; flutter build ipa --flavor stage --export-options-plist=ExportOptionsStage.plist --release
#
# # Rename IPA for stage flavor (fail-fast)
# - name: Rename IPA (stage)
# run: |
# set -euo pipefail;
# IPA=$(ls -1t build/ios/ipa/*.ipa | head -n1)
# mv "$IPA" build/ios/ipa/app-stage.ipa
#
# # --- PROD FLAVOR ---
# # Install provisioning profile for prod flavor (fail-fast)
# - name: 7.3.1 Install provisioning profile (prod)
# run: |
# set -euo pipefail
# decode() { base64 -d 2>/dev/null || base64 -D; }
# echo "${{ secrets.PROFILE_PROD_BASE64 }}" | decode > profile_prod.mobileprovision
# cp profile_prod.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
# echo "${{ secrets.EXPORTOPTIONS_PROD_BASE64 }}" | decode > ExportOptionsProd.plist
#
# # Build IPA for prod flavor (fail-fast)
# - name: 7.3.2 Build IPA (prod)
# run: set -euo pipefail; flutter build ipa --flavor prod --export-options-plist=ExportOptionsProd.plist --release
#
# # Rename IPA for prod flavor (fail-fast)
# - name: Rename IPA (prod)
# run: |
# set -euo pipefail
# IPA=$(ls -1t build/ios/ipa/*.ipa | head -n1)
# mv "$IPA" build/ios/ipa/app-prod.ipa
# 9. UPLOAD ARTIFACTS ──────────────────────
# Upload Android dev APK
- name: 8.1 Upload APK (dev)
if: success()
uses: actions/upload-artifact@v4.6.2
with:
name: apk-dev-release
path: build/app/outputs/flutter-apk/app-dev-release.apk
if-no-files-found: error
# Upload Android stage APK
- name: 8.2 Upload APK (stage)
if: success()
uses: actions/upload-artifact@v4.6.2
with:
name: apk-stage-release
path: build/app/outputs/flutter-apk/app-stage-release.apk
if-no-files-found: error
- name: 8.6 Upload APK (prod)
if: success()
uses: actions/upload-artifact@v4.6.2
with:
name: apk-prod-release
path: build/app/outputs/flutter-apk/app-prod-release.apk
if-no-files-found: error
# Upload iOS dev IPA
# - name: 8.3 Upload IPA (dev)
# if: success()
# uses: actions/upload-artifact@v4.6.2
# with:
# name: ipa-dev-release
# path: build/ios/ipa/app-dev.ipa
# if-no-files-found: error
# retention-days: 14
#
# # Upload iOS stage IPA
# - name: 8.4 Upload IPA (stage)
# if: success()
# uses: actions/upload-artifact@v4.6.2
# with:
# name: ipa-stage-release
# path: build/ios/ipa/app-stage.ipa
# if-no-files-found: error
#
# # Upload iOS prod IPA
# - name: 8.5 Upload IPA (prod)
# if: success()
# uses: actions/upload-artifact@v4.6.2
# with:
# name: ipa-prod-release
# path: build/ios/ipa/app-prod.ipa
# if-no-files-found: error
# Upload build logs on failure for debug
- name: Upload build logs (on failure)
if: failure()
uses: actions/upload-artifact@v4.6.2
with:
name: build-logs
path: |
build/**/logs/*.log
build/**/logs/*.txt
build/**/failures/**/*
if-no-files-found: ignore
# 10. Cleanup secrets and keychain ─────────────────────
# Explicitly remove sensitive/temporary files including .env (fail-fast)
- name: 9. Cleanup secrets and keychain
if: always()
run: |
set -euo pipefail
rm -f android/app/keystore.jks android/key.properties
rm -f certificate.p12 p12_password.txt profile_*.mobileprovision ExportOptions*.plist .env
rm -f build/ios/ipa/app-*.ipa
security default-keychain -s login.keychain || true
security delete-keychain build.keychain || true