name: Open Shorebird CI on: push: branches: ["**"] pull_request: workflow_dispatch: inputs: full_sdk_build: description: Build custom SDK and engine artifacts during manual dispatch. required: false default: true type: boolean run_gclient_sync: description: Run gclient sync before heavy SDK/engine builds. required: false default: true type: boolean base_flutter_engine_revision: description: Optional upstream Flutter engine revision for artifact proxy fallback metadata. required: false default: "" type: string linux_heavy_runner: description: Runner label for Linux SDK/Android/web heavy jobs. required: false default: ubuntu-latest type: string macos_heavy_runner: description: Runner label for macOS SDK and Apple engine heavy jobs. required: false default: macos-latest type: string sdk_min_free_disk_gb: description: Minimum free disk GiB required before SDK-only heavy jobs. required: false default: 8 type: number engine_min_free_disk_gb: description: Minimum free disk GiB required before engine heavy jobs. required: false default: 8 type: number run_runtime_smokes: description: Run Android/Linux runtime patch smokes on provisioned runners. required: false default: false type: boolean linux_runtime_runner: description: Runner label for the Linux runtime smoke job. required: false default: self-hosted type: string android_runtime_runner: description: Runner label for the Android runtime smoke job. required: false default: self-hosted type: string android_serial: description: Optional adb serial for the Android runtime smoke target. required: false default: "" type: string permissions: contents: read defaults: run: shell: bash env: DART_SDK_CHANNEL: stable GO_VERSION: "1.23.x" JAVA_VERSION: "17" DEPOT_TOOLS_UPDATE: "0" PUB_CACHE: ${{ github.workspace }}/.pub-cache BASE_FLUTTER_ENGINE_REVISION: ${{ inputs.base_flutter_engine_revision || '' }} SDK_MIN_FREE_DISK_GB: ${{ inputs.sdk_min_free_disk_gb || 8 }} ENGINE_MIN_FREE_DISK_GB: ${{ inputs.engine_min_free_disk_gb || 8 }} jobs: source-checks: name: Source checks runs-on: ubuntu-latest timeout-minutes: 60 steps: - name: Checkout workspace uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Set up Dart uses: dart-lang/setup-dart@v1 with: sdk: ${{ env.DART_SDK_CHANNEL }} - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: shorebird-server/go.sum - name: Set up Rust uses: dtolnay/rust-toolchain@stable - name: Cache pub packages uses: actions/cache@v4 with: path: | .pub-cache key: pub-${{ runner.os }}-${{ hashFiles('shorebird/pubspec.lock', 'shorebird/**/pubspec.yaml') }} restore-keys: | pub-${{ runner.os }}- - name: Run source-level workspace checks env: SKIP_GCLIENT_SYNC: "1" run: ./scripts/bootstrap_linux.sh - name: Verify CI scripts run: | bash -n scripts/*.sh shorebird/third_party/flutter/bin/internal/shared.sh flutter/bin/internal/update_dart_sdk.sh python3 -m py_compile scripts/validate_artifact_mirror.py python3 -m py_compile scripts/validate_release_manifest.py python3 -m py_compile scripts/safe_extract_tar.py python3 -m py_compile scripts/write_artifact_manifest.py python3 -m py_compile scripts/write_release_manifest.py python3 -m py_compile scripts/write_sha256.py python3 - <<'PY' from pathlib import Path compile(Path('flutter/DEPS').read_text(encoding='utf-8'), 'flutter/DEPS', 'exec') PY ./scripts/verify_ci_capacity.sh ./scripts/verify_open_infrastructure_defaults.sh ./scripts/verify_sync_open_sources.sh ./scripts/verify_powershell_open_defaults.sh ./scripts/verify_write_sha256.sh ./scripts/verify_release_manifest.sh ./scripts/verify_artifact_mirror_validator.sh ./scripts/verify_assemble_artifact_mirror.sh ./scripts/verify_artifact_mirror_workflow_assembly.sh ./scripts/verify_hosted_full_sdk_build.sh --help >/dev/null 2>&1 ./scripts/verify_ios_interpreter_route_validator.sh ./scripts/verify_ci_workflow.sh cli-artifacts: name: Build CLI artifacts needs: source-checks runs-on: ${{ matrix.os }} timeout-minutes: 45 strategy: fail-fast: false matrix: include: - os: ubuntu-latest artifact_os: linux artifact_arch: x64 extension: "" patch_zip: patch-linux-x64.zip patch_entry: patch - os: macos-15-intel artifact_os: macos artifact_arch: x64 extension: "" patch_zip: patch-darwin-x64.zip patch_entry: patch - os: macos-14 artifact_os: macos artifact_arch: arm64 extension: "" patch_zip: patch-darwin-arm64.zip patch_entry: patch - os: windows-latest artifact_os: windows artifact_arch: x64 extension: ".exe" patch_zip: patch-windows-x64.zip patch_entry: patch.exe steps: - name: Checkout workspace uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Set up Dart uses: dart-lang/setup-dart@v1 with: sdk: ${{ env.DART_SDK_CHANNEL }} - name: Set up Rust uses: dtolnay/rust-toolchain@stable - name: Verify Rust run: | rustc --version cargo --version - name: Cache pub packages uses: actions/cache@v4 with: path: | .pub-cache key: pub-${{ runner.os }}-${{ hashFiles('shorebird/pubspec.lock', 'shorebird/**/pubspec.yaml') }} restore-keys: | pub-${{ runner.os }}- - name: Compile Shorebird CLI and open patch tools shell: bash run: | set -euo pipefail rm -rf artifacts/cli mkdir -p artifacts/cli/bin/internal cp shorebird/bin/internal/flutter.version artifacts/cli/bin/internal/flutter.version cp flutter/bin/internal/engine.version artifacts/cli/bin/internal/engine.version cd shorebird dart pub get dart compile exe packages/shorebird_cli/bin/shorebird.dart \ -o ../artifacts/cli/bin/shorebird${{ matrix.extension }} dart compile exe packages/open_aot_patch_tools/bin/open_aot_patch_tools.dart \ -o ../artifacts/cli/bin/open_aot_patch_tools${{ matrix.extension }} dart compile exe packages/artifact_proxy/bin/server.dart \ -o ../artifacts/cli/bin/artifact_proxy${{ matrix.extension }} cd .. flutter_revision="$(cat artifacts/cli/bin/internal/flutter.version)" engine_revision="$(cat artifacts/cli/bin/internal/engine.version)" cat > artifacts/cli/manifest.json </dev/null version_output="$(HOME="$PWD/artifacts/cli-home" artifacts/cli/bin/shorebird${{ matrix.extension }} --version)" printf '%s\n' "$version_output" grep -q 'https://git.tonycloud.org/flutter/shorebird.git' <<<"$version_output" if grep -q 'git@github.com:shorebirdtech/shorebird.git' <<<"$version_output"; then echo "compiled CLI still reports the official Shorebird SSH remote" >&2 exit 1 fi HOME="$PWD/artifacts/cli-home" artifacts/cli/bin/artifact_proxy${{ matrix.extension }} --health-check tar -C artifacts/cli -czf open-shorebird-cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}.tar.gz . cli_extract_dir="$(mktemp -d)" mkdir -p artifacts/cli-extracted-home tar -C "$cli_extract_dir" -xzf open-shorebird-cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}.tar.gz test -f "$cli_extract_dir/manifest.json" test -f "$cli_extract_dir/bin/internal/flutter.version" test -f "$cli_extract_dir/bin/internal/engine.version" HOME="$PWD/artifacts/cli-extracted-home" "$cli_extract_dir/bin/open_aot_patch_tools${{ matrix.extension }}" --help >/dev/null extracted_version_output="$(HOME="$PWD/artifacts/cli-extracted-home" "$cli_extract_dir/bin/shorebird${{ matrix.extension }}" --version)" printf '%s\n' "$extracted_version_output" grep -q 'https://git.tonycloud.org/flutter/shorebird.git' <<<"$extracted_version_output" if grep -q 'git@github.com:shorebirdtech/shorebird.git' <<<"$extracted_version_output"; then echo "extracted CLI still reports the official Shorebird SSH remote" >&2 exit 1 fi HOME="$PWD/artifacts/cli-extracted-home" "$cli_extract_dir/bin/artifact_proxy${{ matrix.extension }}" --health-check ./scripts/write_sha256.sh \ open-shorebird-cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}.tar.gz \ open-shorebird-cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}.tar.gz.sha256 - name: Build mirror patch artifact shell: bash run: | set -euo pipefail cargo_target_dir="$PWD/artifacts/cargo-target/${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}" CARGO_TARGET_DIR="$cargo_target_dir" \ cargo build --release --manifest-path updater/patch/Cargo.toml --bin patch mkdir -p artifacts/mirror patch_binary="$cargo_target_dir/release/patch${{ matrix.extension }}" cp "$patch_binary" "artifacts/mirror/${{ matrix.patch_entry }}" python_bin=python3 if ! command -v "$python_bin" >/dev/null 2>&1; then python_bin=python fi "$python_bin" - \ "artifacts/mirror/${{ matrix.patch_zip }}" \ "artifacts/mirror/${{ matrix.patch_entry }}" \ "${{ matrix.patch_entry }}" <<'PY' import sys import zipfile zip_path, input_path, archive_name = sys.argv[1:] with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as archive: archive.write(input_path, archive_name) PY "$python_bin" - \ "artifacts/mirror/${{ matrix.patch_zip }}" \ "${{ matrix.patch_entry }}" <<'PY' import sys import zipfile zip_path, expected_entry = sys.argv[1:] with zipfile.ZipFile(zip_path) as archive: names = archive.namelist() if names != [expected_entry]: raise SystemExit( f"expected only {expected_entry!r} in {zip_path}, got {names!r}" ) info = archive.getinfo(expected_entry) if info.file_size <= 0: raise SystemExit(f"{expected_entry} in {zip_path} is empty") PY ./scripts/write_sha256.sh \ "artifacts/mirror/${{ matrix.patch_zip }}" \ "artifacts/mirror/${{ matrix.patch_zip }}.sha256" engine_revision="$(cat flutter/bin/internal/engine.version)" mirror_patch_dir="artifacts/mirror/shorebird/$engine_revision" mkdir -p "$mirror_patch_dir" cp "artifacts/mirror/${{ matrix.patch_zip }}" "$mirror_patch_dir/${{ matrix.patch_zip }}" cp "artifacts/mirror/${{ matrix.patch_zip }}.sha256" "$mirror_patch_dir/${{ matrix.patch_zip }}.sha256" test -f "$mirror_patch_dir/${{ matrix.patch_zip }}" test -f "$mirror_patch_dir/${{ matrix.patch_zip }}.sha256" - name: Build mirror metadata if: ${{ matrix.artifact_os == 'linux' && matrix.artifact_arch == 'x64' }} shell: bash run: | set -euo pipefail engine_revision="$(cat flutter/bin/internal/engine.version)" base_engine_revision="${BASE_FLUTTER_ENGINE_REVISION:-$engine_revision}" manifest_path="artifacts/mirror/shorebird/$engine_revision/artifacts_manifest.yaml" python3 scripts/write_artifact_manifest.py \ --flutter-engine-revision "$base_engine_revision" \ --output "$manifest_path" ./scripts/write_sha256.sh "$manifest_path" "$manifest_path.sha256" test -f "artifacts/mirror/shorebird/$engine_revision/artifacts_manifest.yaml.sha256" - name: Upload CLI artifacts uses: actions/upload-artifact@v4 with: name: cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }} path: | open-shorebird-cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}.tar.gz open-shorebird-cli-${{ matrix.artifact_os }}-${{ matrix.artifact_arch }}.tar.gz.sha256 if-no-files-found: error - name: Upload mirror patch artifact uses: actions/upload-artifact@v4 with: name: mirror-${{ matrix.patch_zip }} path: | artifacts/mirror/${{ matrix.patch_zip }} artifacts/mirror/${{ matrix.patch_zip }}.sha256 artifacts/mirror/shorebird/**/${{ matrix.patch_zip }} artifacts/mirror/shorebird/**/${{ matrix.patch_zip }}.sha256 if-no-files-found: error - name: Upload mirror metadata if: ${{ matrix.artifact_os == 'linux' && matrix.artifact_arch == 'x64' }} uses: actions/upload-artifact@v4 with: name: mirror-metadata path: | artifacts/mirror/shorebird/**/artifacts_manifest.yaml artifacts/mirror/shorebird/**/artifacts_manifest.yaml.sha256 if-no-files-found: error server-artifacts: name: Build server artifacts needs: source-checks runs-on: ubuntu-latest timeout-minutes: 45 strategy: fail-fast: false matrix: include: - goos: linux goarch: amd64 extension: "" - goos: linux goarch: arm64 extension: "" - goos: darwin goarch: amd64 extension: "" - goos: darwin goarch: arm64 extension: "" - goos: windows goarch: amd64 extension: ".exe" steps: - name: Checkout workspace uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: shorebird-server/go.sum - name: Test server working-directory: shorebird-server run: go test ./... - name: Build server binary working-directory: shorebird-server env: CGO_ENABLED: "0" GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} run: | set -euo pipefail mkdir -p ../artifacts/server-${GOOS}-${GOARCH} go build -trimpath -ldflags="-s -w" \ -o ../artifacts/server-${GOOS}-${GOARCH}/shorebird-server${{ matrix.extension }} \ ./cmd/server cp -R web ../artifacts/server-${GOOS}-${GOARCH}/ cp .env.example ../artifacts/server-${GOOS}-${GOARCH}/ cp README.md ../artifacts/server-${GOOS}-${GOARCH}/ cp internal/api/handlers/openapi.yaml ../artifacts/server-${GOOS}-${GOARCH}/openapi.yaml server_revision="$(git rev-parse HEAD)" cat > ../artifacts/server-${GOOS}-${GOARCH}/manifest.json </dev/null; then kill "$smoke_pid" 2>/dev/null || true wait "$smoke_pid" 2>/dev/null || true fi rm -rf "$smoke_dir" } trap cleanup_server_smoke EXIT tar -C "$smoke_dir" -xzf ../shorebird-server-${GOOS}-${GOARCH}.tar.gz cd "$smoke_dir/server-${GOOS}-${GOARCH}" SERVER_HOST=127.0.0.1 \ SERVER_PORT=18080 \ SERVER_BASE_URL=http://127.0.0.1:18080 \ DB_DRIVER=sqlite \ DB_PATH="$smoke_dir/shorebird.db" \ STORAGE_DRIVER=local \ STORAGE_LOCAL_DIR="$smoke_dir/storage" \ DEFAULT_ADMIN_PASSWORD=admin123456 \ ./shorebird-server >"$smoke_dir/server.log" 2>&1 & smoke_pid="$!" for _ in {1..40}; do if curl -fsS http://127.0.0.1:18080/health >"$smoke_dir/health.json"; then break fi sleep 0.25 done cat "$smoke_dir/health.json" grep -q '"status":"ok"' "$smoke_dir/health.json" curl -fsS http://127.0.0.1:18080/ >"$smoke_dir/index.html" grep -q '' "$smoke_dir/index.html" curl -fsS http://127.0.0.1:18080/openapi.yaml >"$smoke_dir/openapi.yaml" grep -q 'openapi: 3.1.0' "$smoke_dir/openapi.yaml" fi - name: Upload server artifact uses: actions/upload-artifact@v4 with: name: shorebird-server-${{ matrix.goos }}-${{ matrix.goarch }} path: | shorebird-server-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz shorebird-server-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz.sha256 if-no-files-found: error custom-dart-sdk: name: Build custom Dart SDK needs: source-checks runs-on: ${{ inputs.linux_heavy_runner || 'ubuntu-latest' }} timeout-minutes: 180 if: ${{ github.event_name != 'workflow_dispatch' || inputs.full_sdk_build }} steps: - name: Checkout workspace uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Install depot_tools run: | if [ ! -d depot_tools ]; then git clone --depth 1 \ https://chromium.googlesource.com/chromium/tools/depot_tools.git \ depot_tools fi echo "$GITHUB_WORKSPACE/depot_tools" >> "$GITHUB_PATH" - name: Verify depot_tools build tools run: | python3 --version gclient help >/dev/null ninja --version - name: Free Ubuntu runner disk run: ./scripts/free_ci_disk_linux.sh - name: Check runner capacity run: CI_MIN_FREE_DISK_GB="$SDK_MIN_FREE_DISK_GB" ./scripts/check_ci_capacity.sh - name: Write gclient config run: ./scripts/write_gclient.sh linux - name: Sync open-source links run: ./scripts/sync_open_sources.sh - name: gclient sync if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} run: gclient sync --no-history - name: Generate patched Dart SDK build files working-directory: dart-sdk run: | python3 tools/gn.py \ --mode release \ --arch x64 \ --gn-args='dart_dynamic_modules=false dart_enable_aot_patching=true dart_enable_shorebird_interpreter=true' - name: Verify patched Dart SDK args run: ./scripts/verify_dart_sdk_args.sh dart-sdk/out/ReleaseX64/args.gn - name: Build patched Dart SDK working-directory: dart-sdk run: | ninja -C out/ReleaseX64 create_sdk runtime gen_snapshot dartaotruntime - name: Run focused patch API tests working-directory: dart-sdk/out/ReleaseX64 run: | ./run_vm_tests DartAPI_AotPatchingConfiguration ./run_vm_tests DartAPI_BytecodePatchReloadConfiguration - name: Package Dart SDK artifact run: | mkdir -p artifacts/custom-dart-sdk-linux-x64 cp -R dart-sdk/out/ReleaseX64/dart-sdk artifacts/custom-dart-sdk-linux-x64/ cp dart-sdk/out/ReleaseX64/gen_snapshot artifacts/custom-dart-sdk-linux-x64/ cp dart-sdk/out/ReleaseX64/dartaotruntime artifacts/custom-dart-sdk-linux-x64/ cp dart-sdk/out/ReleaseX64/args.gn artifacts/custom-dart-sdk-linux-x64/ dart_sdk_revision="$(git -C dart-sdk rev-parse HEAD)" cat > artifacts/custom-dart-sdk-linux-x64/manifest.json <> "$GITHUB_PATH" - name: Verify depot_tools build tools run: | python3 --version gclient help >/dev/null ninja --version - name: Check runner capacity run: CI_MIN_FREE_DISK_GB="$SDK_MIN_FREE_DISK_GB" ./scripts/check_ci_capacity.sh - name: Write gclient config run: ./scripts/write_gclient.sh macos - name: Sync open-source links run: ./scripts/sync_open_sources.sh - name: gclient sync if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} run: gclient sync --no-history - name: Generate patched Dart SDK build files working-directory: dart-sdk run: | python3 tools/gn.py \ --mode release \ --arch arm64 \ --gn-args='dart_dynamic_modules=false dart_enable_aot_patching=true dart_enable_shorebird_interpreter=true' - name: Verify patched Dart SDK args run: ./scripts/verify_dart_sdk_args.sh dart-sdk/xcodebuild/ReleaseARM64/args.gn - name: Build patched Dart SDK working-directory: dart-sdk run: | ninja -C xcodebuild/ReleaseARM64 create_sdk runtime gen_snapshot dartaotruntime - name: Run focused patch API tests working-directory: dart-sdk/xcodebuild/ReleaseARM64 run: | ./run_vm_tests DartAPI_AotPatchingConfiguration ./run_vm_tests DartAPI_BytecodePatchReloadConfiguration - name: Package Dart SDK artifact run: | mkdir -p artifacts/custom-dart-sdk-macos-arm64 cp -R dart-sdk/xcodebuild/ReleaseARM64/dart-sdk artifacts/custom-dart-sdk-macos-arm64/ cp dart-sdk/xcodebuild/ReleaseARM64/gen_snapshot artifacts/custom-dart-sdk-macos-arm64/ cp dart-sdk/xcodebuild/ReleaseARM64/dartaotruntime artifacts/custom-dart-sdk-macos-arm64/ cp dart-sdk/xcodebuild/ReleaseARM64/args.gn artifacts/custom-dart-sdk-macos-arm64/ dart_sdk_revision="$(git -C dart-sdk rev-parse HEAD)" cat > artifacts/custom-dart-sdk-macos-arm64/manifest.json <> "$GITHUB_PATH" - name: Verify depot_tools build tools run: | python3 --version gclient help >/dev/null ninja --version - name: Free Ubuntu runner disk run: ./scripts/free_ci_disk_linux.sh - name: Check runner capacity run: CI_MIN_FREE_DISK_GB="$ENGINE_MIN_FREE_DISK_GB" ./scripts/check_ci_capacity.sh - name: Install Linux engine dependencies run: | sudo apt-get update sudo apt-get install -y \ libgtk-3-dev \ liblzma-dev \ pkg-config - name: Write gclient config env: INCLUDE_ENGINE_DEPS: "1" run: ./scripts/write_gclient.sh linux - name: Sync open-source links run: ./scripts/sync_open_sources.sh - name: gclient sync if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} run: gclient sync --no-history - name: gclient sync Flutter engine deps if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} working-directory: flutter run: gclient sync --no-history - name: Verify Dart tool SDK run: ./scripts/verify_dart_tool_sdk.sh - name: Configure Linux engine working-directory: flutter/engine/src run: | ./flutter/tools/gn \ --runtime-mode=release \ --target-dir=linux_release_x64 \ --no-prebuilt-dart-sdk \ --gn-args='dart_dynamic_modules=false dart_enable_aot_patching=true dart_enable_shorebird_interpreter=false shorebird_enable_aot_patching=true shorebird_use_interpreter=false flutter_prebuilt_dart_sdk=false' - name: Build Linux engine working-directory: flutter/engine/src run: | ninja -C out/linux_release_x64 \ flutter/shell/platform/linux:flutter_gtk \ flutter/build/archives:flutter_patched_sdk - name: Verify Linux engine args run: | ./scripts/verify_engine_args.sh \ flutter/engine/src/out/linux_release_x64/args.gn \ dart_dynamic_modules=false \ dart_enable_aot_patching=true \ dart_enable_shorebird_interpreter=false \ shorebird_enable_aot_patching=true \ shorebird_use_interpreter=false \ flutter_prebuilt_dart_sdk=false - name: Package Linux engine artifacts run: | set -euo pipefail out=flutter/engine/src/out/linux_release_x64 mkdir -p artifacts/linux-engine gtk_zip="$(find "$out/zip_archives" -name 'linux-x64-flutter-gtk.zip' -type f -print -quit)" patched_sdk_zip="$(find "$out/zip_archives" -name 'flutter_patched_sdk_product.zip' -type f -print -quit)" artifacts_zip="$(find "$out/zip_archives" -path '*/linux-x64-release/artifacts.zip' -type f -print -quit)" if [ -z "$artifacts_zip" ]; then test -x "$out/gen_snapshot" artifacts_zip="$out/zip_archives/linux-x64-release/artifacts.zip" mkdir -p "$(dirname "$artifacts_zip")" python3 - "$out/gen_snapshot" "$artifacts_zip" <<'PY' from pathlib import Path import sys import zipfile source = Path(sys.argv[1]) output = Path(sys.argv[2]) info = zipfile.ZipInfo('gen_snapshot') info.external_attr = (source.stat().st_mode & 0xFFFF) << 16 with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as archive: archive.writestr(info, source.read_bytes(), zipfile.ZIP_DEFLATED) PY fi test -n "$gtk_zip" test -n "$patched_sdk_zip" test -n "$artifacts_zip" cp "$gtk_zip" artifacts/linux-engine/ cp "$patched_sdk_zip" artifacts/linux-engine/ cp "$artifacts_zip" artifacts/linux-engine/artifacts.zip cp "$out/args.gn" artifacts/linux-engine/linux_release_x64.args.gn engine_revision="$(cat flutter/bin/internal/engine.version)" mirror_root="artifacts/linux-engine/mirror/shorebird/flutter_infra_release/flutter/$engine_revision" mkdir -p "$mirror_root/linux-x64-release" cp "$gtk_zip" "$mirror_root/linux-x64-release/linux-x64-flutter-gtk.zip" cp "$artifacts_zip" "$mirror_root/linux-x64-release/artifacts.zip" cp "$patched_sdk_zip" "$mirror_root/flutter_patched_sdk_product.zip" test -f "$mirror_root/linux-x64-release/linux-x64-flutter-gtk.zip" test -f "$mirror_root/linux-x64-release/artifacts.zip" test -f "$mirror_root/flutter_patched_sdk_product.zip" cat > artifacts/linux-engine/manifest.json <> "$GITHUB_PATH" - name: Verify depot_tools build tools run: | python3 --version gclient help >/dev/null ninja --version - name: Set up Java uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Install Android Rust target run: rustup target add aarch64-linux-android - name: Free Ubuntu runner disk run: ./scripts/free_ci_disk_linux.sh - name: Check runner capacity run: CI_MIN_FREE_DISK_GB="$ENGINE_MIN_FREE_DISK_GB" ./scripts/check_ci_capacity.sh - name: Write gclient config env: INCLUDE_ENGINE_DEPS: "1" run: ./scripts/write_gclient.sh linux - name: Sync open-source links run: ./scripts/sync_open_sources.sh - name: gclient sync if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} run: gclient sync --no-history - name: gclient sync Flutter engine deps if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} working-directory: flutter run: gclient sync --no-history - name: Verify Dart tool SDK run: ./scripts/verify_dart_tool_sdk.sh - name: Configure Android engine working-directory: flutter/engine/src run: | ./flutter/tools/gn \ --android \ --runtime-mode=release \ --android-cpu=arm64 \ --target-dir=android_release_arm64 \ --no-prebuilt-dart-sdk \ --gn-args='dart_dynamic_modules=false dart_enable_aot_patching=true dart_enable_shorebird_interpreter=false shorebird_enable_aot_patching=true shorebird_use_interpreter=false flutter_prebuilt_dart_sdk=false' - name: Build Android engine working-directory: flutter/engine/src run: | ninja -C out/android_release_arm64 \ flutter/shell/platform/android:flutter_jar_zip \ flutter/shell/platform/android:android_symbols \ flutter/shell/platform/android:gen_snapshot \ flutter/shell/platform/android:analyze_snapshot - name: Verify Android engine args run: | ./scripts/verify_engine_args.sh \ flutter/engine/src/out/android_release_arm64/args.gn \ dart_dynamic_modules=false \ dart_enable_aot_patching=true \ dart_enable_shorebird_interpreter=false \ shorebird_enable_aot_patching=true \ shorebird_use_interpreter=false \ flutter_prebuilt_dart_sdk=false - name: Package Android engine artifacts run: | set -euo pipefail out=flutter/engine/src/out/android_release_arm64 mkdir -p artifacts/android-engine cp "$out/args.gn" artifacts/android-engine/android_release_arm64.args.gn cp "$out/zip_archives/android-arm64-release/artifacts.zip" artifacts/android-engine/ cp "$out/zip_archives/android-arm64-release/symbols.zip" artifacts/android-engine/ cp "$out/flutter.jar" artifacts/android-engine/ cp "$out/libflutter.so" artifacts/android-engine/ if [ -f "$out/arm64_v8a_release.jar" ]; then cp "$out/arm64_v8a_release.jar" artifacts/android-engine/ fi gen_snapshot="" for candidate in "$out/gen_snapshot_arm64" "$out/clang_x64/gen_snapshot" "$out/universal/gen_snapshot"; do if [ -f "$candidate" ]; then gen_snapshot="$candidate" break fi done if [ -z "$gen_snapshot" ]; then echo "missing Android gen_snapshot tool under $out" >&2 find "$out" -maxdepth 3 -type f -name 'gen_snapshot*' -print >&2 exit 66 fi analyze_snapshot="" for candidate in "$out/analyze_snapshot_arm64" "$out/clang_x64/analyze_snapshot" "$out/universal/analyze_snapshot"; do if [ -f "$candidate" ]; then analyze_snapshot="$candidate" break fi done if [ -z "$analyze_snapshot" ]; then echo "missing Android analyze_snapshot tool under $out" >&2 find "$out" -maxdepth 3 -type f -name 'analyze_snapshot*' -print >&2 exit 66 fi cp "$gen_snapshot" artifacts/android-engine/gen_snapshot_arm64 cp "$analyze_snapshot" artifacts/android-engine/analyze_snapshot_arm64 engine_revision="$(cat flutter/bin/internal/engine.version)" mirror_root="artifacts/android-engine/mirror/shorebird/flutter_infra_release/flutter/$engine_revision/android-arm64-release" mkdir -p "$mirror_root" cp "$out/zip_archives/android-arm64-release/artifacts.zip" "$mirror_root/artifacts.zip" cp "$out/zip_archives/android-arm64-release/symbols.zip" "$mirror_root/symbols.zip" test -f "$mirror_root/artifacts.zip" test -f "$mirror_root/symbols.zip" cat > artifacts/android-engine/manifest.json <> "$GITHUB_PATH" - name: Verify depot_tools build tools run: | python3 --version gclient help >/dev/null ninja --version - name: Free Ubuntu runner disk run: ./scripts/free_ci_disk_linux.sh - name: Check runner capacity run: CI_MIN_FREE_DISK_GB="$ENGINE_MIN_FREE_DISK_GB" ./scripts/check_ci_capacity.sh - name: Write gclient config env: INCLUDE_ENGINE_DEPS: "1" run: ./scripts/write_gclient.sh linux - name: Sync open-source links run: ./scripts/sync_open_sources.sh - name: gclient sync if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} run: gclient sync --no-history - name: gclient sync Flutter engine deps if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} working-directory: flutter run: gclient sync --no-history - name: Verify Dart tool SDK run: ./scripts/verify_dart_tool_sdk.sh - name: Sync Flutter prebuilt Dart SDK run: ./scripts/sync_flutter_prebuilt_dart_sdk.sh linux-x64 - name: Configure web SDK working-directory: flutter/engine/src run: | ./flutter/tools/gn \ --web \ --runtime-mode=release \ --target-dir=wasm_release \ --gn-args='dart_dynamic_modules=false' - name: Build web SDK working-directory: flutter/engine/src run: ninja -C out/wasm_release flutter/web_sdk:flutter_web_sdk_archive - name: Verify web SDK args run: | ./scripts/verify_engine_args.sh \ flutter/engine/src/out/wasm_release/args.gn \ dart_dynamic_modules=false \ flutter_prebuilt_dart_sdk=true - name: Package web SDK artifact run: | set -euo pipefail out=flutter/engine/src/out/wasm_release mkdir -p artifacts/web-sdk cp "$out/args.gn" artifacts/web-sdk/wasm_release.args.gn cp "$out/zip_archives/flutter-web-sdk.zip" artifacts/web-sdk/ engine_revision="$(cat flutter/bin/internal/engine.version)" mirror_root="artifacts/web-sdk/mirror/shorebird/flutter_infra_release/flutter/$engine_revision" mkdir -p "$mirror_root" cp "$out/zip_archives/flutter-web-sdk.zip" "$mirror_root/flutter-web-sdk.zip" test -f "$mirror_root/flutter-web-sdk.zip" cat > artifacts/web-sdk/manifest.json <> "$GITHUB_PATH" - name: Verify depot_tools build tools run: | python3 --version gclient help >/dev/null ninja --version - name: Install Apple Rust targets run: rustup target add aarch64-apple-ios aarch64-apple-darwin - name: Check runner capacity run: CI_MIN_FREE_DISK_GB="$ENGINE_MIN_FREE_DISK_GB" ./scripts/check_ci_capacity.sh - name: Write gclient config env: INCLUDE_ENGINE_DEPS: "1" run: ./scripts/write_gclient.sh macos - name: Sync open-source links run: ./scripts/sync_open_sources.sh - name: gclient sync if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} run: gclient sync --no-history - name: gclient sync Flutter engine deps if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_gclient_sync }} working-directory: flutter run: gclient sync --no-history - name: Verify Dart tool SDK run: ./scripts/verify_dart_tool_sdk.sh - name: Configure host snapshotter working-directory: flutter/engine/src run: | ./flutter/tools/gn \ --runtime-mode=release \ --mac-cpu=arm64 \ --target-dir=host_release_arm64 \ --shorebird-interpreter \ --no-prebuilt-dart-sdk \ --gn-args='dart_dynamic_modules=false dart_enable_aot_patching=true dart_enable_shorebird_interpreter=true shorebird_use_interpreter=true flutter_prebuilt_dart_sdk=false' - name: Build host snapshotter working-directory: flutter/engine/src run: ninja -C out/host_release_arm64 gen_snapshot - name: Configure iOS engine working-directory: flutter/engine/src run: | ./flutter/tools/gn \ --ios \ --runtime-mode=release \ --target-dir=ios_release \ --shorebird-interpreter \ --no-prebuilt-dart-sdk \ --gn-args='flutter_prebuilt_dart_sdk=false' - name: Build iOS engine working-directory: flutter/engine/src run: | ninja -C out/ios_release \ flutter/shell/platform/darwin/ios:flutter_framework \ create_macos_gen_snapshots \ create_macos_analyze_snapshots - name: Verify iOS interpreter route run: ./scripts/verify_ios_interpreter_route.sh - name: Configure macOS engine working-directory: flutter/engine/src run: | ./flutter/tools/gn \ --mac \ --runtime-mode=release \ --mac-cpu=arm64 \ --target-dir=macos_release_arm64 \ --no-prebuilt-dart-sdk \ --gn-args='dart_dynamic_modules=false dart_enable_aot_patching=true dart_enable_shorebird_interpreter=false shorebird_enable_aot_patching=true shorebird_use_interpreter=false flutter_prebuilt_dart_sdk=false' - name: Build macOS engine working-directory: flutter/engine/src run: | ninja -C out/macos_release_arm64 \ flutter/shell/platform/darwin/macos:zip_macos_flutter_framework \ flutter/build/archives:flutter_patched_sdk \ create_macos_gen_snapshots - name: Verify macOS engine args run: | ./scripts/verify_engine_args.sh \ flutter/engine/src/out/macos_release_arm64/args.gn \ dart_dynamic_modules=false \ dart_enable_aot_patching=true \ dart_enable_shorebird_interpreter=false \ shorebird_enable_aot_patching=true \ shorebird_use_interpreter=false \ flutter_prebuilt_dart_sdk=false - name: Package iOS engine artifacts run: | set -euo pipefail mkdir -p artifacts/ios-engine/host_release_arm64 artifacts/ios-engine/ios-release require_path() { local path="$1" if [ ! -e "$path" ]; then echo "missing required path: $path" >&2 find flutter/engine/src/out/ios_release -maxdepth 3 \( -name 'Flutter*' -o -name '*snapshot*' \) -print >&2 || true exit 1 fi } require_directory() { require_path "$1" if [ ! -d "$1" ]; then echo "required path is not a directory: $1" >&2 exit 1 fi } require_executable() { require_path "$1" if [ ! -x "$1" ]; then echo "required path is not executable: $1" >&2 exit 1 fi } require_executable flutter/engine/src/out/host_release_arm64/gen_snapshot require_directory flutter/engine/src/out/ios_release/Flutter.xcframework require_executable flutter/engine/src/out/ios_release/universal/gen_snapshot_arm64 require_executable flutter/engine/src/out/ios_release/analyze_snapshot_arm64 cp -R flutter/engine/src/out/ios_release/Flutter.framework artifacts/ios-engine/ cp -R flutter/engine/src/out/ios_release/Flutter.xcframework artifacts/ios-engine/ cp flutter/engine/src/out/host_release_arm64/gen_snapshot artifacts/ios-engine/host_release_arm64/ cp flutter/engine/src/out/ios_release/args.gn artifacts/ios-engine/ios_release.args.gn cp flutter/engine/src/out/host_release_arm64/args.gn artifacts/ios-engine/host_release_arm64.args.gn ios_zip_staging="$(mktemp -d)" cp -R flutter/engine/src/out/ios_release/Flutter.xcframework "$ios_zip_staging/" cp flutter/engine/src/out/ios_release/universal/gen_snapshot_arm64 "$ios_zip_staging/" cp flutter/engine/src/out/ios_release/analyze_snapshot_arm64 "$ios_zip_staging/" cat > "$ios_zip_staging/entitlements.txt" <<'EOF' analyze_snapshot_arm64 gen_snapshot_arm64 EOF cat > "$ios_zip_staging/without_entitlements.txt" <<'EOF' Flutter.xcframework/ios-arm64/Flutter.framework/Flutter EOF : > "$ios_zip_staging/unsigned_binaries.txt" python3 - "$ios_zip_staging" artifacts/ios-engine/ios-release/artifacts.zip <<'PY' import os from pathlib import Path import stat import sys import zipfile root = Path(sys.argv[1]) output = Path(sys.argv[2]) with zipfile.ZipFile(output, "w", zipfile.ZIP_DEFLATED) as archive: for path in sorted(p for p in root.rglob("*") if p.is_file()): archive_name = path.relative_to(root).as_posix() info = zipfile.ZipInfo(archive_name) info.external_attr = (path.stat().st_mode & 0xFFFF) << 16 with path.open("rb") as file: archive.writestr(info, file.read(), zipfile.ZIP_DEFLATED) PY engine_revision="$(cat flutter/bin/internal/engine.version)" mirror_root="artifacts/ios-engine/mirror/shorebird/flutter_infra_release/flutter/$engine_revision/ios-release" mkdir -p "$mirror_root" cp artifacts/ios-engine/ios-release/artifacts.zip "$mirror_root/artifacts.zip" test -f "$mirror_root/artifacts.zip" cat > artifacts/ios-engine/manifest.json < artifacts/macos-engine/manifest.json <