[Docs] Set correct document url #13
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |