Merge pull request #90 from solguruz/feat/home_screen_liquid_glass_de… #369
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 | |
| check "${{ secrets.DEV_GOOGLE_SERVICES_JSON }}" DEV_GOOGLE_SERVICES_JSON | |
| check "${{ secrets.FIREBASE_OPTIONS_DEV }}" FIREBASE_OPTIONS_DEV | |
| check "${{ secrets.FIREBASE_OPTIONS_PROD }}" FIREBASE_OPTIONS_PROD | |
| check "${{ secrets.FIREBASE_OPTIONS_STAGE }}" FIREBASE_OPTIONS_STAGE | |
| [ "$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.41.9' | |
| # 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 "REVENEUCAT_TEST_STORE_API_KEY=${{ secrets.REVENEUCAT_TEST_STORE_API_KEY }}" >> .env | |
| - name: 3.2 Create Firebase options files (decode from base64) | |
| run: | | |
| set -euo pipefail | |
| decode() { base64 -d 2>/dev/null || base64 -D; } | |
| echo "${{ secrets.FIREBASE_OPTIONS_DEV }}" | decode > lib/firebase_options_dev.dart | |
| echo "${{ secrets.FIREBASE_OPTIONS_PROD }}" | decode > lib/firebase_options_prod.dart | |
| echo "${{ secrets.FIREBASE_OPTIONS_STAGE }}" | decode > lib/firebase_options_stage.dart | |
| # Verify files were created and are non-empty | |
| for f in lib/firebase_options_dev.dart lib/firebase_options_prod.dart lib/firebase_options_stage.dart; do | |
| [ -s "$f" ] || { echo "Failed to create or empty: $f"; exit 1; } | |
| done | |
| - name: 3.3 Setup Google Services | |
| run: | | |
| set -euo pipefail | |
| decode() { base64 -d 2>/dev/null || base64 -D; } | |
| # Create flavor directories | |
| mkdir -p android/app/src/dev | |
| # Decode and write the files | |
| echo "${{ secrets.DEV_GOOGLE_SERVICES_JSON }}" | decode > android/app/src/dev/google-services.json | |
| # Verify file exists | |
| test -f android/app/src/dev/google-services.json || { echo "Failed to create dev google-services.json"; exit 1; } | |
| # 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 APK | |
| - name: 5.3 Build Android APK | |
| run: | | |
| set -euo pipefail | |
| flutter build apk --flavor dev --dart-define=APP_FLAVOR=DEV --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 | |
| # 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 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 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 lib/firebase_options_dev.dart lib/firebase_options_prod.dart lib/firebase_options_stage.dart | |
| rm -f build/ios/ipa/app-*.ipa | |
| security default-keychain -s login.keychain || true | |
| security delete-keychain build.keychain || true |