name: Mobile Checks on: pull_request: types: - opened - synchronize - reopened - ready_for_review - converted_to_draft paths: - 'mobile/**' # Route shared edits to mobile’s imports/RPC recordings/typecheck; root lockfile changes affect those modules’ root-resolved packages. - 'src/shared/**' # Mobile's Node RPC recording tools import this package. - 'src/packages/process-host/**' - 'pnpm-lock.yaml' # Mobile launch contracts exercise the real host dispatcher and durable receipt store. - 'src/main/agent-launch/**' - 'src/main/runtime/rpc/**' - 'src/main/runtime/runtime-rpc/**' - 'src/main/runtime/runtime-rpc.ts' - 'src/main/runtime/device-registry.ts' - 'src/main/runtime/orca-runtime.ts' - 'src/main/runtime/agent-session-*.ts' - 'src/main/native-chat/agent-session-wire/**' - 'src/main/runtime/structured-attention-original-delivery.test-fixture.ts' # Rerun the only Fastfile-loading checks for release-workflow/Fastfile edits. - '.github/workflows/mobile.yml' - 'config/mobile-ci.vitest.config.mjs' - 'config/scripts/run-mobile-ci-tests.mjs' # Mobile typechecks run through the root compiler selection. - 'config/scripts/run-typecheck-projects-in-parallel.mjs' - 'config/scripts/tsrs-typecheck-compiler.mjs' - 'config/scripts/pinned-bun-executable.mjs' - 'config/oxlint-code-quality-native-plugins.json' - 'config/.bun-version' - '.github/actions/install-node-dependencies/**' - '.github/actions/setup-pnpm/**' - '.github/actions/restore-pnpm-verification/**' - '.github/workflows/mobile-ios-release.yml' - 'config/scripts/mobile-release-check-scope*' - 'config/scripts/mobile-test-change-scope*' - 'config/scripts/pr-code-change-scope.mjs' - 'config/scripts/pr-package-test-paths.mjs' - 'config/scripts/pr-layout-oracle-route.mjs' - 'config/scripts/pr-shell-contract-prefixes.mjs' # Desktop suites do not feed mobile; keep fixtures and test/toolchain config inputs. - '!src/shared/**/*.test.[jt]s' - '!src/shared/**/*.test.[jt]sx' - '!src/shared/**/*.test.[cm][jt]s' - '!src/packages/process-host/**/*.test.[jt]s' - '!src/packages/process-host/**/*.test.[jt]sx' - '!src/packages/process-host/**/*.test.[cm][jt]s' - '!src/main/**/*.test.[jt]s' - '!src/main/**/*.test.[jt]sx' - '!src/main/**/*.test.[cm][jt]s' # Check matching source inputs on main too: independently passing PRs can conflict after merge, and verify is PR-only. push: branches: - main paths: - 'mobile/**' - 'src/shared/**' - 'src/packages/process-host/**' - 'pnpm-lock.yaml' - 'src/main/agent-launch/**' - 'src/main/runtime/rpc/**' - 'src/main/runtime/runtime-rpc/**' - 'src/main/runtime/runtime-rpc.ts' - 'src/main/runtime/device-registry.ts' - 'src/main/runtime/orca-runtime.ts' - 'src/main/runtime/agent-session-*.ts' - 'src/main/native-chat/agent-session-wire/**' - 'src/main/runtime/structured-attention-original-delivery.test-fixture.ts' - '.github/workflows/mobile.yml' - 'config/mobile-ci.vitest.config.mjs' - 'config/scripts/run-mobile-ci-tests.mjs' - 'config/scripts/run-typecheck-projects-in-parallel.mjs' - 'config/scripts/tsrs-typecheck-compiler.mjs' - 'config/scripts/pinned-bun-executable.mjs' - 'config/.bun-version' - '.github/actions/install-node-dependencies/**' - '.github/actions/setup-pnpm/**' # Desktop suites do not feed mobile; keep fixtures and test/toolchain config inputs. - '!src/shared/**/*.test.[jt]s' - '!src/shared/**/*.test.[jt]sx' - '!src/shared/**/*.test.[cm][jt]s' - '!src/packages/process-host/**/*.test.[jt]s' - '!src/packages/process-host/**/*.test.[jt]sx' - '!src/packages/process-host/**/*.test.[cm][jt]s' - '!src/main/**/*.test.[jt]s' - '!src/main/**/*.test.[jt]sx' - '!src/main/**/*.test.[cm][jt]s' concurrency: # Key main concurrency by commit: GitHub replaces pending runs even with cancel-in-progress:false, hiding middle-merge regressions. group: mobile-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true permissions: contents: read jobs: verify: name: Mobile verify if: github.event_name == 'pull_request' && github.event.pull_request.draft != true # Use ARM for pure Vitest; no x86 native toolchain is needed and Gemfile.lock’s ruby platform supports frozen install. runs-on: ubuntu-24.04-arm env: # Freeze bundler to reject Gemfile drift before release jobs resolve different fastlane versions. BUNDLE_FROZEN: 'true' ORCA_MOBILE_TYPECHECK_COMPILER: 'bun' defaults: run: working-directory: mobile steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 2 - uses: ./.github/actions/install-node-dependencies with: cache-pnpm-store: 'false' cache-dependency-path: | pnpm-lock.yaml mobile/pnpm-lock.yaml - name: Detect Ruby release inputs id: ruby-scope shell: bash working-directory: . run: | # Keep deletions when release files move into an application directory. if ! git diff --name-only --no-renames -z HEAD^1 HEAD > "$RUNNER_TEMP/mobile-release-changes"; then echo 'should_run=true' >> "$GITHUB_OUTPUT" elif ! node config/scripts/mobile-release-check-scope.mjs "$RUNNER_TEMP/mobile-release-changes"; then echo 'should_run=true' >> "$GITHUB_OUTPUT" fi - name: Setup Ruby id: ruby-setup background: true if: steps.ruby-scope.outputs.should_run != 'false' uses: ruby/setup-ruby@v1 with: ruby-version: '3.3' working-directory: mobile - name: Install dependencies run: pnpm install --frozen-lockfile - wait: ruby-setup # ruby/setup-ruby's bundler-cache saves on every miss; the platform warmup saves this key on main. - name: Resolve fastlane bundle cache id: ruby-bundle if: steps.ruby-scope.outputs.should_run != 'false' env: GEMFILE_HASH: ${{ hashFiles('mobile/Gemfile', 'mobile/Gemfile.lock') }} run: | . /etc/os-release printf 'key=mobile-bundle-v1-%s-%s-%s-%s\n' "$ID-$VERSION_ID" "$(ruby -e 'print RUBY_PLATFORM')" "$(ruby -e 'print RbConfig::CONFIG["ruby_version"]')" "$GEMFILE_HASH" >> "$GITHUB_OUTPUT" - name: Restore fastlane bundle if: steps.ruby-scope.outputs.should_run != 'false' uses: actions/cache/restore@v5 with: path: mobile/vendor/bundle key: ${{ steps.ruby-bundle.outputs.key }} # Installing proves the release’s pinned fastlane still resolves before a release needs it. - name: Install fastlane id: ruby-tools background: true if: steps.ruby-scope.outputs.should_run != 'false' run: | bundle config set --local path "$PWD/vendor/bundle" bundle config set --local deployment true bundle install --jobs 4 # Check mobile import cycles after Expo deps install; pre-install pr.yml cannot load its tsconfig. - name: Lint mobile imports with mobile dependencies installed working-directory: . run: node node_modules/oxlint/bin/oxlint --config config/oxlint-code-quality-native-plugins.json mobile --deny-warnings # Call installed tools so pnpm's dependency refresh cannot race between checks. - name: Typecheck id: production-types background: true run: node ../config/scripts/run-typecheck-projects-in-parallel.mjs --compiler=bun --bun-threads=2 --project=mobile/tsconfig.json # Why a ratchet and not the raw typecheck: mobile/tsconfig.json excludes test files, so until # tsconfig.test.json existed nothing checked them, and at introduction 127 of the 632 had # drifted. This fails when a test file that checks today stops checking, when a test leaves # the program, and on @ts-nocheck; the baseline may only shrink. - name: Typecheck tests (ratchet) id: test-types background: true run: node scripts/check-tests-typecheck-ratchet.mjs # Run bridged recording corpus unconditionally: reject growing/unclassified divergence and changed verdicts among 103 C1 goldens. Share worker time instead of a separate skippable env-gated step. - name: Detect mobile test inputs id: test-scope shell: bash working-directory: . run: | if ! git diff --name-only --no-renames -z HEAD^1 HEAD > "$RUNNER_TEMP/mobile-test-changes"; then echo 'should_run=true' >> "$GITHUB_OUTPUT" elif ! node config/scripts/mobile-test-change-scope.mjs "$RUNNER_TEMP/mobile-test-changes"; then echo 'should_run=true' >> "$GITHUB_OUTPUT" fi - name: Setup Bun for tests if: steps.test-scope.outputs.should_run != 'false' uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version-file: config/.bun-version no-cache: true - name: Test if: steps.test-scope.outputs.should_run != 'false' env: ORCA_BACKGROUND_LAUNCH: '1' run: node ../config/scripts/run-mobile-ci-tests.mjs # All checks read the completed install; compilers need not hold up test workers. - wait: [production-types, test-types, ruby-tools] # Decode hash-addressed golden behavior changes for review, including after failed tests; advisory only. - name: Summarize RPC recording changes if: ${{ !cancelled() }} continue-on-error: true run: pnpm run rpc:diff HEAD^1 --summary "$GITHUB_STEP_SUMMARY" - name: Test iOS release version resolution if: steps.ruby-scope.outputs.should_run != 'false' run: ruby fastlane/ios_release_version_test.rb - name: Test TestFlight lane arguments if: steps.ruby-scope.outputs.should_run != 'false' run: ruby fastlane/fastfile_testflight_arguments_test.rb # Load/list Fastfile lanes without Apple credentials/network to catch syntax/require/constants before release; ios-distribute failed six days without this guard. - name: Smoke-check the Fastfile if: steps.ruby-scope.outputs.should_run != 'false' env: FASTLANE_SKIP_UPDATE_CHECK: '1' FASTLANE_OPT_OUT_USAGE: '1' run: bundle exec fastlane lanes - name: Smoke-check CocoaPods if: steps.ruby-scope.outputs.should_run != 'false' run: bundle exec pod --version - name: Lint run: pnpm lint - name: Check formatting run: pnpm format:check # Reports, never gates: flags in the job summary when a pull request changes the Expo fingerprint, # i.e. dev clients need a native rebuild. Only mobile's install, without scripts: the fingerprint # hashes project sources and dependency paths, never installed package contents. native-fingerprint: name: Mobile native fingerprint if: github.event_name == 'pull_request' && github.event.pull_request.draft != true runs-on: ubuntu-24.04 timeout-minutes: 15 continue-on-error: true defaults: run: working-directory: mobile steps: - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 2 persist-credentials: false - uses: actions/setup-node@v6 with: node-version-file: package.json package-manager-cache: false - name: Setup pnpm uses: ./.github/actions/setup-pnpm # The base commit may predate the fingerprint tools, so run the head's copy against both. - name: Compare native fingerprint with the base shell: bash run: | set -euo pipefail tools="$RUNNER_TEMP/native-fingerprint" mkdir -p "$tools" cp scripts/native-client.mjs scripts/native-build-cache.cjs "$tools/" pnpm install --frozen-lockfile --ignore-scripts node "$tools/native-client.mjs" fingerprint --project . --out "$tools/head.json" git checkout --quiet --detach HEAD^1 pnpm install --frozen-lockfile --ignore-scripts node "$tools/native-client.mjs" fingerprint --project . --out "$tools/base.json" node "$tools/native-client.mjs" compare "$tools/base.json" "$tools/head.json" \ --project . --summary "$GITHUB_STEP_SUMMARY" main-tests: name: Mobile tests on main env: ORCA_MOBILE_TYPECHECK_COMPILER: 'bun' if: github.event_name == 'push' # Why ARM: the same pure-Node Vitest run `verify` makes on a pull request. runs-on: ubuntu-24.04-arm defaults: run: working-directory: mobile steps: - name: Checkout uses: actions/checkout@v6 with: persist-credentials: false # Restoring the 650 MB store took longer than downloading these packages. - uses: ./.github/actions/install-node-dependencies with: cache-pnpm-store: 'false' cache-dependency-path: | pnpm-lock.yaml mobile/pnpm-lock.yaml - name: Install dependencies run: pnpm install --frozen-lockfile - name: Typecheck id: production-types background: true run: node ../config/scripts/run-typecheck-projects-in-parallel.mjs --compiler=bun --bun-threads=2 --project=mobile/tsconfig.json - name: Typecheck tests (ratchet) id: test-types background: true run: node scripts/check-tests-typecheck-ratchet.mjs # Run all golden consumers, including mutant/page-bridge suites; failures name the recording and rpc:record command. - name: Setup Bun for tests uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version-file: config/.bun-version no-cache: true - name: Test env: ORCA_BACKGROUND_LAUNCH: '1' run: node ../config/scripts/run-mobile-ci-tests.mjs - wait: [production-types, test-types]