DELIVERY-125462: Add Swift Package Manager (SPM) support for Core #44
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
| # ============================================================================= | |
| # Lint, Test & Build - PR/push gate and reusable validation workflow | |
| # ============================================================================= | |
| # | |
| # Purpose: Validates code quality, runs unit tests, and builds the example app | |
| # in release mode for both Android and iOS on every PR and push to | |
| # development/master. Also reusable from rc-release.yml and | |
| # production-release.yml. | |
| # | |
| # What it does: | |
| # 1. Lints + format-checks + runs Dart/Flutter unit tests with coverage. | |
| # 2. Builds Android example app (release App Bundle). | |
| # 3. Builds iOS example app (no-codesign release IPA). | |
| # 4. Caches dependencies for faster subsequent runs. | |
| # | |
| # Note on debug builds: debug-mode Android APK and iOS simulator builds used | |
| # to live here, but they duplicate exactly what ios-e2e.yml / android-e2e.yml | |
| # do before running scenarios. Release-mode builds remain here because E2E | |
| # never exercises them and they catch R8/proguard + archive-bundling issues | |
| # that debug builds don't. | |
| # | |
| # Triggers: | |
| # - Pull requests to development or master branches | |
| # - Direct pushes to development or master branches | |
| # - Manual workflow dispatch for testing | |
| # - workflow_call (rc-release.yml, production-release.yml) | |
| # | |
| # ============================================================================= | |
| name: Lint, Test & Build | |
| on: | |
| # Trigger on pull requests targeting main branches | |
| pull_request: | |
| branches: | |
| - development | |
| - master | |
| paths-ignore: | |
| - '**.md' | |
| - 'doc/**' | |
| - 'assets/**' | |
| - '.github/workflows/close_inactive_issues.yml' | |
| - '.github/workflows/responseToSupportIssue*.yml' | |
| # Trigger on direct pushes to main branches | |
| push: | |
| branches: | |
| - development | |
| - master | |
| paths-ignore: | |
| - '**.md' | |
| - 'doc/**' | |
| - 'assets/**' | |
| # Allow manual triggering for testing | |
| workflow_dispatch: | |
| # Reusable from other workflows. Callers (rc-release.yml) can pass | |
| # skip_unit=true to skip the unit/lint/format job, or skip_builds=true to | |
| # skip the release-mode Android/iOS builds. Skipping the unit job or the | |
| # build jobs is still treated as a pass at the ci-summary level; the | |
| # caller's pre-publish gate decides whether 'skipped' is acceptable for | |
| # publish. | |
| workflow_call: | |
| inputs: | |
| skip_unit: | |
| description: 'Skip the unit-tests/lint/format job (build jobs still run)' | |
| required: false | |
| type: boolean | |
| default: false | |
| skip_builds: | |
| description: 'Skip the Android + iOS release-mode build jobs (unit job still runs)' | |
| required: false | |
| type: boolean | |
| default: false | |
| # Ensure only one CI run per PR/branch at a time | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # =========================================================================== | |
| # Job 1: Run Unit Tests | |
| # =========================================================================== | |
| # Runs all Dart/Flutter unit tests for the plugin | |
| # Uses: Ubuntu runner (fastest and free for public repos) | |
| # =========================================================================== | |
| test: | |
| name: π§ͺ Run Unit Tests | |
| runs-on: ubuntu-latest | |
| # When called from rc-release.yml with skip_unit=true, skip this job. | |
| # On PR/push triggers, inputs.skip_unit is unset and resolves to false. | |
| if: ${{ inputs.skip_unit != true }} | |
| steps: | |
| # Step 1: Checkout the repository code | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| # Step 2: Set up Flutter SDK | |
| # Uses subosito/flutter-action which caches Flutter SDK automatically | |
| - name: Setup Flutter SDK | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: 'stable' # Use latest stable Flutter | |
| cache: true # Cache Flutter SDK for faster runs | |
| # Step 3: Verify Flutter installation | |
| - name: Display Flutter version | |
| run: | | |
| flutter --version | |
| dart --version | |
| # Step 4: Get plugin dependencies | |
| # This installs all packages defined in pubspec.yaml | |
| - name: Install plugin dependencies | |
| run: flutter pub get | |
| # Step 5: Analyze code for issues | |
| # Checks for code quality issues, unused imports, etc. | |
| # Only fails on actual errors, not info-level warnings | |
| - name: Analyze code | |
| run: flutter analyze --no-fatal-infos --no-fatal-warnings | |
| # Step 6: Format check | |
| # Ensures code follows Dart formatting standards. On failure, prints | |
| # the offending files and the exact one-liner to fix locally, plus a | |
| # GitHub Actions `::error::` annotation at the top of the run. | |
| - name: Check code formatting | |
| run: | | |
| if output=$(dart format --output=none --set-exit-if-changed . 2>&1); then | |
| echo "$output" | |
| exit 0 | |
| fi | |
| echo "$output" | |
| changed=$(echo "$output" | awk '/^Changed / {sub(/^Changed /, ""); print}') | |
| inline=$(echo "$changed" | tr '\n' ' ') | |
| echo "::group::π§ Files that need formatting" | |
| echo "$changed" | |
| echo "::endgroup::" | |
| echo "::error title=Code formatting check failed::Run 'dart format .' locally, commit the result, and push again. Files: $inline" | |
| echo | |
| echo "Fix locally with:" | |
| echo " dart format $inline" | |
| echo " git add $inline" | |
| echo " git commit --amend --no-edit # or make a new commit" | |
| echo | |
| echo "Maintainer tip: ./scripts/install-hooks.sh installs a" | |
| echo "pre-commit hook that runs the same check locally." | |
| exit 1 | |
| # Step 7: Run unit tests | |
| # Executes all tests in the test/ directory | |
| - name: Run unit tests | |
| run: flutter test --coverage | |
| # Step 8: Upload coverage report (optional) | |
| # Useful for tracking code coverage over time | |
| - name: Upload coverage to Codecov (optional) | |
| if: success() | |
| uses: codecov/codecov-action@v6 | |
| with: | |
| files: ./coverage/lcov.info | |
| fail_ci_if_error: false # Don't fail CI if coverage upload fails | |
| # =========================================================================== | |
| # Job 2: Build Android Example App | |
| # =========================================================================== | |
| # Builds the example app for Android to ensure plugin integration works | |
| # Uses: Ubuntu runner with Java 17 | |
| # =========================================================================== | |
| build-android: | |
| name: π€ Build Android Example | |
| runs-on: ubuntu-latest | |
| needs: test | |
| # Run when tests pass OR when skipped (skip_unit=true), AND skip_builds is | |
| # not set. Keeps today's "test fails -> skip the build" behavior on | |
| # PR/push, plus lets a caller workflow opt out of the release builds for | |
| # fast iteration. | |
| if: ${{ (needs.test.result == 'success' || needs.test.result == 'skipped') && inputs.skip_builds != true }} | |
| steps: | |
| # Step 1: Checkout code | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| # Step 2: Set up Java (required for Android builds) | |
| # Android builds require JDK 17 for modern Gradle versions | |
| - name: Setup Java | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: 'temurin' # Eclipse Temurin (formerly AdoptOpenJDK) | |
| java-version: '17' | |
| cache: 'gradle' # Cache Gradle dependencies | |
| # Step 3: Set up Flutter SDK | |
| - name: Setup Flutter SDK | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: 'stable' | |
| cache: true | |
| # Step 4: Display versions for debugging | |
| - name: Display versions | |
| run: | | |
| flutter --version | |
| java -version | |
| echo "JAVA_HOME: $JAVA_HOME" | |
| # Step 5: Get plugin dependencies | |
| - name: Install plugin dependencies | |
| run: flutter pub get | |
| # Step 6: Get example app dependencies | |
| - name: Install example app dependencies | |
| working-directory: example | |
| run: flutter pub get | |
| # Step 7: Create dummy .env file for CI (not committed to repo) | |
| - name: Create dummy .env for CI build | |
| working-directory: example | |
| run: | | |
| echo "DEV_KEY=dummy_dev_key" > .env | |
| echo "APP_ID=dummy_app_id" >> .env | |
| # Step 8: Build Android App Bundle (release mode, no signing) | |
| # App Bundle is the preferred format for Play Store. Catches R8/proguard | |
| # regressions a debug APK won't. Debug APK builds were removed because | |
| # android-e2e.yml already builds and runs `flutter build apk --debug` | |
| # against the same source tree. | |
| - name: Build Android App Bundle (release) | |
| working-directory: example | |
| run: flutter build appbundle --release | |
| # Step 9: Upload App Bundle artifact (optional) | |
| # Useful for manual install testing and archiving release-mode output. | |
| - name: Upload App Bundle artifact | |
| if: success() | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: android-appbundle-release | |
| path: example/build/app/outputs/bundle/release/app-release.aab | |
| retention-days: 7 | |
| # =========================================================================== | |
| # Job 3: Build iOS Example App | |
| # =========================================================================== | |
| # Builds the example app for iOS to ensure plugin integration works | |
| # Uses: macOS runner (required for Xcode and iOS builds) | |
| # Note: macOS runners consume 10x minutes on private repos | |
| # =========================================================================== | |
| build-ios: | |
| name: π Build iOS Example | |
| runs-on: macos-15 # macOS 15 (Sequoia) with Xcode 16.4 default; matches ios-e2e + rc-smoke | |
| needs: test | |
| # Run when tests pass OR when skipped (skip_unit=true), AND skip_builds is | |
| # not set. Keeps today's "test fails -> skip the build" behavior on | |
| # PR/push, plus lets a caller workflow opt out of the release builds for | |
| # fast iteration. | |
| if: ${{ (needs.test.result == 'success' || needs.test.result == 'skipped') && inputs.skip_builds != true }} | |
| steps: | |
| # Step 1: Checkout code | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| # Step 2: Set up Flutter SDK | |
| - name: Setup Flutter SDK | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: 'stable' | |
| cache: true | |
| # Step 3: Display versions | |
| - name: Display versions | |
| run: | | |
| flutter --version | |
| xcodebuild -version | |
| pod --version | |
| # Step 4: Get plugin dependencies | |
| - name: Install plugin dependencies | |
| run: flutter pub get | |
| # Step 5: Get example app dependencies | |
| - name: Install example app dependencies | |
| working-directory: example | |
| run: flutter pub get | |
| # Step 6: Update CocoaPods repo (ensures latest pod specs) | |
| # This can be slow, so we only update if needed | |
| - name: Update CocoaPods repo | |
| working-directory: example/ios | |
| run: pod repo update | |
| # Step 7: Install CocoaPods dependencies | |
| # This installs native iOS dependencies including AppsFlyer SDK | |
| - name: Install CocoaPods dependencies | |
| working-directory: example/ios | |
| run: pod install | |
| # Step 8: Create dummy .env file for CI (not committed to repo) | |
| - name: Create dummy .env for CI build | |
| working-directory: example | |
| run: | | |
| echo "DEV_KEY=dummy_dev_key" > .env | |
| echo "APP_ID=dummy_app_id" >> .env | |
| # Step 9: Build iOS IPA without code signing (release mode) | |
| # Validates a full release build without requiring certificates. Catches | |
| # archive bundling and signing-config issues a simulator-debug build | |
| # won't. The simulator-debug build that used to live here was dropped | |
| # because ios-e2e.yml already runs `flutter build ios --simulator --debug` | |
| # against the same source tree. | |
| - name: Build iOS IPA (no codesign) | |
| working-directory: example | |
| run: flutter build ipa --release --no-codesign | |
| # Step 10: Upload build artifacts (optional) | |
| - name: Upload iOS build artifact | |
| if: success() | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: ios-app-unsigned | |
| path: example/build/ios/archive/Runner.xcarchive | |
| retention-days: 7 | |
| # =========================================================================== | |
| # Job 4: Summary Report | |
| # =========================================================================== | |
| # Provides a summary of all CI jobs | |
| # =========================================================================== | |
| ci-summary: | |
| name: π CI Summary | |
| runs-on: ubuntu-latest | |
| needs: [test, build-android, build-ios] | |
| if: always() # Run even if previous jobs fail | |
| steps: | |
| - name: Check CI Results | |
| run: | | |
| echo "===================================" | |
| echo "Lint, Test & Build summary" | |
| echo "===================================" | |
| echo "Test Job: ${{ needs.test.result }}" | |
| echo "Android Build: ${{ needs.build-android.result }}" | |
| echo "iOS Build: ${{ needs.build-ios.result }}" | |
| echo "===================================" | |
| # 'skipped' is acceptable for any of the three jobs (skip_unit=true | |
| # or skip_builds=true from a caller workflow); only 'failure' or | |
| # 'cancelled' should fail the summary. The caller's pre-publish | |
| # gate decides whether a skip is acceptable for publish. | |
| test_result="${{ needs.test.result }}" | |
| android_result="${{ needs.build-android.result }}" | |
| ios_result="${{ needs.build-ios.result }}" | |
| if [[ "$test_result" != "success" && "$test_result" != "skipped" ]]; then | |
| echo "β Unit/lint job failed" | |
| exit 1 | |
| fi | |
| if [[ "$android_result" != "success" && "$android_result" != "skipped" ]]; then | |
| echo "β Android release build failed" | |
| exit 1 | |
| fi | |
| if [[ "$ios_result" != "success" && "$ios_result" != "skipped" ]]; then | |
| echo "β iOS release build failed" | |
| exit 1 | |
| fi | |
| echo "β Lint, Test & Build passed" |