ci: configure pub OIDC authentication #45
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
| # Build and release multi-platform Flutter example app | |
| # This workflow is reusable - just update the env variables at the top | |
| name: Build All Platforms | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| ##################################### | |
| # PROJECT-SPECIFIC CONFIGURATION | |
| # Update these values for your project | |
| ##################################### | |
| env: | |
| # Project Info | |
| PROJECT_NAME: FJS Example | |
| PROJECT_SLUG: fjs-example | |
| PROJECT_PACKAGE: fjs_example | |
| EXAMPLE_DIR: example | |
| # Android signing configuration | |
| ANDROID_KEYSTORE_FILE: keystore.jks | |
| ANDROID_KEYSTORE_SECRET: fjs123456 | |
| ANDROID_KEY_ALIAS: release | |
| ANDROID_KEY_PASSWORD: fjs123456 | |
| ANDROID_KEY_DNAME: 'CN=FJS Example Release,O=FlutterCandies,C=CN' | |
| # Flutter configuration | |
| FLUTTER_CHANNEL: stable | |
| ANDROID_COMPAT_FLUTTER_VERSION: 3.44.0 | |
| jobs: | |
| publish-precompiled: | |
| needs: quality | |
| if: github.event_name != 'pull_request' | |
| uses: ./.github/workflows/precompile-binaries.yml | |
| permissions: | |
| contents: write | |
| secrets: inherit | |
| ##################################### | |
| # Production quality gates | |
| ##################################### | |
| quality: | |
| name: Production quality gates | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.ANDROID_COMPAT_FLUTTER_VERSION }} | |
| cache: true | |
| - name: Setup stable Rust with quality components | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Install Linux build dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clang cmake ninja-build pkg-config libssl-dev | |
| - name: Install cargo-audit | |
| run: cargo install cargo-audit --version 0.22.2 --locked | |
| - name: Resolve root dependencies | |
| run: flutter pub get --no-example | |
| - name: Resolve CargoKit dependencies | |
| run: dart pub get | |
| working-directory: cargokit/build_tool | |
| - name: Resolve example dependencies | |
| run: flutter pub get | |
| working-directory: ${{ env.EXAMPLE_DIR }} | |
| - name: Verify dependency resolution leaves tracked files unchanged | |
| run: git diff HEAD --exit-code -- | |
| - name: Check Rust formatting | |
| run: cargo fmt --manifest-path libfjs/Cargo.toml --check | |
| - name: Lint all Rust targets and features | |
| run: cargo clippy --manifest-path libfjs/Cargo.toml --all-targets --all-features -- -D warnings | |
| - name: Test all Rust targets and features | |
| run: cargo test --manifest-path libfjs/Cargo.toml --all-targets --all-features | |
| - name: Check Rust documentation | |
| run: cargo doc --manifest-path libfjs/Cargo.toml --all-features --no-deps | |
| env: | |
| RUSTDOCFLAGS: -D warnings | |
| - name: Audit Rust lockfile with repository policy | |
| run: cargo audit --file libfjs/Cargo.lock | |
| - name: Test CargoKit | |
| run: dart test | |
| working-directory: cargokit/build_tool | |
| - name: Test FRB content-hash validation | |
| run: flutter test test/tool/check_frb_content_hash_test.dart | |
| - name: Analyze production Dart and Flutter sources | |
| run: flutter analyze --fatal-infos --fatal-warnings | |
| ##################################### | |
| # Prepare validated Darwin package | |
| ##################################### | |
| prepare-darwin: | |
| name: Prepare Darwin release package | |
| runs-on: macos-latest | |
| needs: quality | |
| timeout-minutes: 90 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter 3.44.0 | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.ANDROID_COMPAT_FLUTTER_VERSION }} | |
| cache: true | |
| - name: Setup stable Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install nightly Rust with rustfmt | |
| run: rustup toolchain install nightly --profile minimal --component rustfmt | |
| - name: Install FRB codegen 2.12.0 | |
| run: cargo install flutter_rust_bridge_codegen --version 2.12.0 --locked | |
| - name: Resolve root dependencies | |
| run: flutter pub get --no-example | |
| - name: Verify tracked release provenance | |
| run: git diff HEAD --exit-code -- | |
| - name: Prepare and validate Darwin release package | |
| run: tool/prepare_darwin_release.sh --configuration Release --artifact-dir build/darwin-release | |
| - name: Upload validated Darwin release package | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: darwin-release | |
| path: | | |
| build/darwin-release/fjs.xcframework.zip | |
| build/darwin-release/fjs.xcframework.zip.checksum | |
| retention-days: 30 | |
| if-no-files-found: error | |
| ##################################### | |
| # Prepare release info | |
| ##################################### | |
| prepare: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_tag: ${{ steps.tag.outputs.tag }} | |
| release_name: ${{ steps.tag.outputs.name }} | |
| project_version: ${{ steps.version.outputs.project_version }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Generate release tag | |
| id: tag | |
| run: | | |
| DATE=$(date +'%Y%m%d') | |
| SHORT_REF=$(git rev-parse --short HEAD) | |
| echo "tag=$DATE-$SHORT_REF" >> $GITHUB_OUTPUT | |
| echo "name=Release $DATE-$SHORT_REF" >> $GITHUB_OUTPUT | |
| - name: Read project version | |
| id: version | |
| run: | | |
| VERSION=$(grep '^version:' ${{ env.EXAMPLE_DIR }}/pubspec.yaml | head -n1 | awk '{print $2}' | cut -d'+' -f1) | |
| echo "project_version=$VERSION" >> $GITHUB_OUTPUT | |
| ##################################### | |
| # Android build (multi-architecture) | |
| ##################################### | |
| build-android: | |
| runs-on: ubuntu-latest | |
| needs: [prepare, quality] | |
| strategy: | |
| matrix: | |
| arch: [arm64, x64, universal] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: ${{ env.FLUTTER_CHANNEL }} | |
| cache: true | |
| - name: Setup Java | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: 'zulu' | |
| java-version: '17' | |
| - name: Get dependencies | |
| run: flutter pub get | |
| working-directory: ${{ env.EXAMPLE_DIR }} | |
| - name: Generate Android keystore | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }}/android/app | |
| if [ -n "$KEYSTORE_BASE64" ]; then | |
| echo "$KEYSTORE_BASE64" | base64 -d > ${{ env.ANDROID_KEYSTORE_FILE }} | |
| else | |
| keytool -genkey -v -keystore ${{ env.ANDROID_KEYSTORE_FILE }} \ | |
| -alias ${{ env.ANDROID_KEY_ALIAS }} \ | |
| -keyalg RSA -keysize 2048 -validity 36135 \ | |
| -storepass '${{ env.ANDROID_KEYSTORE_SECRET }}' \ | |
| -keypass '${{ env.ANDROID_KEY_PASSWORD }}' \ | |
| -dname '${{ env.ANDROID_KEY_DNAME }}' | |
| fi | |
| if ! grep -q "${{ env.ANDROID_KEYSTORE_FILE }}" .gitignore 2>/dev/null; then | |
| echo "${{ env.ANDROID_KEYSTORE_FILE }}" >> .gitignore | |
| fi | |
| env: | |
| KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} | |
| - name: Configure Android signing | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }}/android/app | |
| printf '\n# Signing config\n' >> gradle.properties | |
| printf 'RELEASE_STORE_FILE=${{ env.ANDROID_KEYSTORE_FILE }}\n' >> gradle.properties | |
| printf 'RELEASE_STORE_PASSWORD=${{ env.ANDROID_KEYSTORE_SECRET }}\n' >> gradle.properties | |
| printf 'RELEASE_KEY_ALIAS=${{ env.ANDROID_KEY_ALIAS }}\n' >> gradle.properties | |
| printf 'RELEASE_KEY_PASSWORD=${{ env.ANDROID_KEY_PASSWORD }}\n' >> gradle.properties | |
| cp build.gradle build.gradle.orig | |
| awk ' | |
| /^android \{/ { | |
| print "" | |
| print " signingConfigs {" | |
| print " release {" | |
| print " storeFile file(\"${{ env.ANDROID_KEYSTORE_FILE }}\")" | |
| print " storePassword \"${{ env.ANDROID_KEYSTORE_SECRET }}\"" | |
| print " keyAlias \"${{ env.ANDROID_KEY_ALIAS }}\"" | |
| print " keyPassword \"${{ env.ANDROID_KEY_PASSWORD }}\"" | |
| print " }" | |
| print " }" | |
| next | |
| } | |
| /signingConfig = signingConfigs\.debug/ { | |
| print " signingConfig signingConfigs.release" | |
| next | |
| } | |
| { print } | |
| ' build.gradle.orig > build.gradle | |
| - name: Build APK for ${{ matrix.arch }} | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| if [ "${{ matrix.arch }}" = "universal" ]; then | |
| flutter build apk --release --target-platform android-arm64,android-x64 | |
| else | |
| flutter build apk --release --target-platform android-${{ matrix.arch }} | |
| fi | |
| - name: Rename APK | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }}/build/app/outputs/flutter-apk | |
| if [ "${{ matrix.arch }}" = "universal" ]; then | |
| mv app-release.apk ${{ env.PROJECT_SLUG }}-android-universal.apk | |
| else | |
| mv app-release.apk ${{ env.PROJECT_SLUG }}-android-${{ matrix.arch }}.apk | |
| fi | |
| - name: Enable Android emulator acceleration | |
| if: matrix.arch == 'universal' | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Run Android cockpit verification | |
| if: matrix.arch == 'universal' | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 35 | |
| target: google_apis | |
| arch: x86_64 | |
| profile: pixel_7_pro | |
| script: >- | |
| cd ${{ env.EXAMPLE_DIR }} && | |
| dart run tool/ci_cockpit_verify.dart | |
| --platform android | |
| --device-id emulator-5554 | |
| --output-dir .dart_tool/flutter_cockpit/ci-android | |
| --launch-timeout-seconds 1200 | |
| - name: Upload Android cockpit evidence | |
| if: always() && matrix.arch == 'universal' | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cockpit-android | |
| path: ${{ env.EXAMPLE_DIR }}/.dart_tool/flutter_cockpit/ci-android | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Upload APK artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: android-${{ matrix.arch }} | |
| path: | | |
| ${{ env.EXAMPLE_DIR }}/build/app/outputs/flutter-apk/${{ env.PROJECT_SLUG }}-android-${{ matrix.arch }}.apk | |
| ${{ env.EXAMPLE_DIR }}/build/app/outputs/flutter-apk/${{ env.PROJECT_SLUG }}-android-universal.apk | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| ##################################### | |
| # Android Gradle compatibility (issue 19) | |
| ##################################### | |
| android-compatibility: | |
| name: Android compatibility (${{ matrix.name }}) | |
| runs-on: ubuntu-latest | |
| needs: quality | |
| timeout-minutes: 60 | |
| permissions: | |
| contents: read | |
| strategy: | |
| matrix: | |
| include: | |
| - name: Gradle 9.1 / AGP 9.0.1 / Kotlin 2.3.20 | |
| compatibility: committed | |
| - name: Gradle 8.9 / AGP 8.7.3 / Kotlin 2.1.0 | |
| compatibility: legacy | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter 3.44.0 | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: ${{ env.ANDROID_COMPAT_FLUTTER_VERSION }} | |
| cache: true | |
| - name: Setup Java | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: zulu | |
| java-version: '17' | |
| - name: Setup stable Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Select compatibility source | |
| id: source | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| flutter --version | grep -F "Flutter ${{ env.ANDROID_COMPAT_FLUTTER_VERSION }} " | |
| if [ "${{ matrix.compatibility }}" = "committed" ]; then | |
| grep -Fx 'distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip' example/android/gradle/wrapper/gradle-wrapper.properties | |
| grep -Fx ' id "com.android.application" version "9.0.1" apply false' example/android/settings.gradle | |
| grep -Fx ' id "org.jetbrains.kotlin.android" version "2.3.20" apply false' example/android/settings.gradle | |
| source_dir="$GITHUB_WORKSPACE" | |
| else | |
| source_dir="$RUNNER_TEMP/fjs-gradle-8" | |
| mkdir -p "$source_dir" | |
| git archive HEAD | tar -x -C "$source_dir" | |
| sed -i 's/gradle-9\.1\.0-all\.zip/gradle-8.9-all.zip/' "$source_dir/example/android/gradle/wrapper/gradle-wrapper.properties" | |
| sed -i 's/version "9\.0\.1"/version "8.7.3"/' "$source_dir/example/android/settings.gradle" | |
| sed -i 's/version "2\.3\.20"/version "2.1.0"/' "$source_dir/example/android/settings.gradle" | |
| sed -i '/compileSdk = 36/a\ ndkVersion = flutter.ndkVersion' "$source_dir/example/android/app/build.gradle" | |
| grep -Fx 'distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip' "$source_dir/example/android/gradle/wrapper/gradle-wrapper.properties" | |
| grep -Fx ' id "com.android.application" version "8.7.3" apply false' "$source_dir/example/android/settings.gradle" | |
| grep -Fx ' id "org.jetbrains.kotlin.android" version "2.1.0" apply false' "$source_dir/example/android/settings.gradle" | |
| grep -Fx ' ndkVersion = flutter.ndkVersion' "$source_dir/example/android/app/build.gradle" | |
| fi | |
| echo "directory=$source_dir" >> "$GITHUB_OUTPUT" | |
| - name: Resolve compatibility dependencies | |
| run: flutter pub get | |
| working-directory: ${{ steps.source.outputs.directory }}/example | |
| - name: Build arm64 split release APK | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| build_log="$RUNNER_TEMP/android-${{ matrix.compatibility }}.log" | |
| flutter build apk --release --split-per-abi --target-platform android-arm64 2>&1 | tee "$build_log" | |
| grep -F 'Environment variable CARGOKIT_CONFIGURATION: release' "$build_log" | |
| grep -F 'INFO: Building fjs for aarch64-linux-android' "$build_log" | |
| if awk ' | |
| BEGIN { RS = "" } | |
| { | |
| record = tolower($0) | |
| if (record ~ /(warning|warn|deprecated|deprecation)/ && | |
| record ~ /(cargokit|plugin\.gradle|build_tool)/) { | |
| print $0 | |
| found = 1 | |
| } | |
| } | |
| END { exit found ? 0 : 1 } | |
| ' "$build_log"; then | |
| echo 'CargoKit emitted a compatibility warning or deprecation.' >&2 | |
| exit 1 | |
| fi | |
| working-directory: ${{ steps.source.outputs.directory }}/example | |
| env: | |
| CI: 'true' | |
| GRADLE_OPTS: -Dorg.gradle.warning.mode=all | |
| - name: Verify arm64 split release APK | |
| run: test -f build/app/outputs/flutter-apk/app-arm64-v8a-release.apk | |
| working-directory: ${{ steps.source.outputs.directory }}/example | |
| ##################################### | |
| # iOS build | |
| ##################################### | |
| build-ios: | |
| runs-on: macos-latest | |
| needs: [prepare, quality, prepare-darwin] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: ${{ env.FLUTTER_CHANNEL }} | |
| cache: true | |
| - name: Download validated Darwin release package | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: darwin-release | |
| path: darwin/fjs/Binaries | |
| - name: Validate downloaded Darwin release package | |
| run: tool/check_darwin_package_support.sh --require-artifact | |
| - name: Get dependencies | |
| run: flutter pub get | |
| working-directory: ${{ env.EXAMPLE_DIR }} | |
| - name: Build iOS (no codesign) | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| flutter build ios --release --no-codesign | |
| - name: Run iOS cockpit verification | |
| run: | | |
| xcrun simctl list devices available -j > /tmp/ios-devices.json | |
| DEVICE_ID=$(python3 -c 'import json; data=json.load(open("/tmp/ios-devices.json", encoding="utf-8")); print(next(d["udid"] for runtime, devices in data["devices"].items() if "iOS" in runtime for d in devices if d.get("isAvailable") and d.get("name", "").startswith("iPhone")))') | |
| xcrun simctl boot "$DEVICE_ID" || true | |
| xcrun simctl bootstatus "$DEVICE_ID" -b | |
| cd ${{ env.EXAMPLE_DIR }} | |
| dart run tool/ci_cockpit_verify.dart \ | |
| --platform ios \ | |
| --device-id "$DEVICE_ID" \ | |
| --output-dir .dart_tool/flutter_cockpit/ci-ios \ | |
| --launch-timeout-seconds 1200 | |
| - name: Upload iOS cockpit evidence | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cockpit-ios | |
| path: ${{ env.EXAMPLE_DIR }}/.dart_tool/flutter_cockpit/ci-ios | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Create iOS bundle (zip) | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| mkdir -p build/ios-bundle/Runner.app | |
| cp -r build/ios/iphoneos/Runner.app/* build/ios-bundle/Runner.app/ | |
| cd build/ios-bundle | |
| zip -r ../../${{ env.PROJECT_SLUG }}-ios.zip . | |
| cd ../.. | |
| - name: Create IPA | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| mkdir -p Payload | |
| cp -r build/ios/iphoneos/Runner.app Payload/ | |
| zip -r ${{ env.PROJECT_SLUG }}-ios.ipa Payload | |
| rm -rf Payload | |
| - name: Upload iOS artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: ios | |
| path: | | |
| ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-ios.zip | |
| ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-ios.ipa | |
| retention-days: 30 | |
| ##################################### | |
| # Windows build | |
| ##################################### | |
| build-windows: | |
| runs-on: windows-latest | |
| needs: [prepare, quality] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: ${{ env.FLUTTER_CHANNEL }} | |
| cache: true | |
| - name: Get dependencies | |
| run: flutter pub get | |
| working-directory: ${{ env.EXAMPLE_DIR }} | |
| - name: Install Inno Setup | |
| run: | | |
| choco install innosetup -y | |
| - name: Build Windows | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| flutter build windows --release | |
| - name: Run Windows cockpit verification | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| dart run tool/ci_cockpit_verify.dart ` | |
| --platform windows ` | |
| --output-dir .dart_tool/flutter_cockpit/ci-windows ` | |
| --launch-timeout-seconds 1200 | |
| - name: Upload Windows cockpit evidence | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cockpit-windows | |
| path: ${{ env.EXAMPLE_DIR }}/.dart_tool/flutter_cockpit/ci-windows | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Create Inno Setup script | |
| shell: pwsh | |
| run: | | |
| $script = @" | |
| [Setup] | |
| AppName=${{ env.PROJECT_NAME }} | |
| AppVersion=${{ needs.prepare.outputs.project_version }} | |
| DefaultDirName={pf}\${{ env.PROJECT_NAME }} | |
| DefaultGroupName=${{ env.PROJECT_NAME }} | |
| OutputBaseFilename=${{ env.PROJECT_SLUG }}-windows-setup | |
| Compression=lzma2 | |
| SolidCompression=yes | |
| OutputDir=. | |
| [Files] | |
| Source: "build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs | |
| [Icons] | |
| Name: "{group}\${{ env.PROJECT_NAME }}"; Filename: "{app}\${{ env.PROJECT_PACKAGE }}.exe" | |
| Name: "{commondesktop}\${{ env.PROJECT_NAME }}"; Filename: "{app}\${{ env.PROJECT_PACKAGE }}.exe" | |
| "@ | |
| $script | Out-File -FilePath ${{ env.EXAMPLE_DIR }}\setup.iss -Encoding UTF8 | |
| - name: Create installer with Inno Setup | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| & "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" setup.iss | |
| - name: Create Windows zip | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| Compress-Archive -Path build\windows\x64\runner\Release\* -DestinationPath ${{ env.PROJECT_SLUG }}-windows.zip | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: windows | |
| path: | | |
| ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-windows.zip | |
| ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-windows-setup.exe | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| ##################################### | |
| # Linux build | |
| ##################################### | |
| build-linux: | |
| runs-on: ubuntu-latest | |
| needs: [prepare, quality] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: ${{ env.FLUTTER_CHANNEL }} | |
| cache: true | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev xvfb | |
| - name: Get dependencies | |
| run: flutter pub get | |
| working-directory: ${{ env.EXAMPLE_DIR }} | |
| - name: Build Linux | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| flutter build linux --release | |
| - name: Run Linux smoke test | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| xvfb-run -a flutter test integration_test/smoke_test.dart -d linux | |
| - name: Run Linux cockpit verification | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| xvfb-run -a dart run tool/ci_cockpit_verify.dart \ | |
| --platform linux \ | |
| --output-dir .dart_tool/flutter_cockpit/ci-linux \ | |
| --launch-timeout-seconds 1200 | |
| - name: Upload Linux cockpit evidence | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cockpit-linux | |
| path: ${{ env.EXAMPLE_DIR }}/.dart_tool/flutter_cockpit/ci-linux | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Create Linux tarball | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| tar -czf ${{ env.PROJECT_SLUG }}-linux.tar.gz -C build/linux/x64/release/bundle . | |
| - name: Upload Linux artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: linux | |
| path: ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-linux.tar.gz | |
| retention-days: 30 | |
| ##################################### | |
| # macOS build | |
| ##################################### | |
| build-macos: | |
| runs-on: macos-latest | |
| needs: [prepare, quality, prepare-darwin] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Setup Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| channel: ${{ env.FLUTTER_CHANNEL }} | |
| cache: true | |
| - name: Download validated Darwin release package | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: darwin-release | |
| path: darwin/fjs/Binaries | |
| - name: Validate downloaded Darwin release package | |
| run: tool/check_darwin_package_support.sh --require-artifact | |
| - name: Get dependencies | |
| run: flutter pub get | |
| working-directory: ${{ env.EXAMPLE_DIR }} | |
| - name: Build macOS | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| flutter build macos --release | |
| - name: Run macOS cockpit verification | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| dart run tool/ci_cockpit_verify.dart \ | |
| --platform macos \ | |
| --output-dir .dart_tool/flutter_cockpit/ci-macos \ | |
| --launch-timeout-seconds 1200 | |
| - name: Upload macOS cockpit evidence | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: cockpit-macos | |
| path: ${{ env.EXAMPLE_DIR }}/.dart_tool/flutter_cockpit/ci-macos | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Create macOS zip | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| cd build/macos/Build/Products/Release | |
| zip -r ../../../../../${{ env.PROJECT_SLUG }}-macos.zip . | |
| - name: Create DMG | |
| run: | | |
| cd ${{ env.EXAMPLE_DIR }} | |
| mkdir -p dmg/disk | |
| cp -r build/macos/Build/Products/Release/*.app dmg/disk/ | |
| hdiutil create -volname "${{ env.PROJECT_NAME }}" -srcfolder dmg/disk -ov -format UDZO ${{ env.PROJECT_SLUG }}-macos.dmg | |
| rm -rf dmg | |
| - name: Upload macOS artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: macos | |
| path: | | |
| ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-macos.zip | |
| ${{ env.EXAMPLE_DIR }}/${{ env.PROJECT_SLUG }}-macos.dmg | |
| retention-days: 30 | |
| ##################################### | |
| # Release all builds | |
| ##################################### | |
| release: | |
| runs-on: ubuntu-latest | |
| needs: [publish-precompiled, prepare, prepare-darwin, android-compatibility, build-android, build-ios, build-windows, build-linux, build-macos] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| permissions: | |
| contents: write | |
| id-token: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: artifacts | |
| - name: List downloaded artifacts | |
| run: | | |
| echo "=== Artifact tree ===" | |
| tree artifacts/ || ls -laR artifacts/ | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v3 | |
| with: | |
| tag_name: ${{ needs.prepare.outputs.release_tag }} | |
| name: ${{ needs.prepare.outputs.release_name }} | |
| draft: false | |
| prerelease: false | |
| generate_release_notes: true | |
| files: | | |
| artifacts/android-arm64/*.apk | |
| artifacts/android-x64/*.apk | |
| artifacts/android-universal/*.apk | |
| artifacts/ios/*.ipa | |
| artifacts/ios/*.zip | |
| artifacts/windows/*.exe | |
| artifacts/windows/*.zip | |
| artifacts/linux/*.tar.gz | |
| artifacts/macos/*.dmg | |
| artifacts/macos/*.zip | |
| artifacts/darwin-release/fjs.xcframework.zip | |
| artifacts/darwin-release/fjs.xcframework.zip.checksum | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create release summary | |
| run: | | |
| cat >> $GITHUB_STEP_SUMMARY << 'SUMMARY' | |
| # ✅ Release Created Successfully | |
| ## Tag: ${{ needs.prepare.outputs.release_tag }} | |
| ## Artifacts | |
| | Platform | Format | File | | |
| |----------|--------|------| | |
| | Android (arm64) | .apk | ${{ env.PROJECT_SLUG }}-android-arm64.apk | | |
| | Android (x64) | .apk | ${{ env.PROJECT_SLUG }}-android-x64.apk | | |
| | Android (Universal) | .apk | ${{ env.PROJECT_SLUG }}-android-universal.apk | | |
| | iOS | .ipa | ${{ env.PROJECT_SLUG }}-ios.ipa | | |
| | iOS | .zip | ${{ env.PROJECT_SLUG }}-ios.zip | | |
| | Windows | .exe | ${{ env.PROJECT_SLUG }}-windows-setup.exe | | |
| | Windows | .zip | ${{ env.PROJECT_SLUG }}-windows.zip | | |
| | Linux | .tar.gz | ${{ env.PROJECT_SLUG }}-linux.tar.gz | | |
| | macOS | .dmg | ${{ env.PROJECT_SLUG }}-macos.dmg | | |
| | macOS | .zip | ${{ env.PROJECT_SLUG }}-macos.zip | | |
| SUMMARY |