Files
orca/.github/workflows/mobile.yml
T
NeilandOrca Integration Recovery 77ad467ebb fix(ci): prevent concurrent pnpm refresh during mobile typechecks (#24776)
* fix(ci): run mobile typechecks without concurrent dependency refresh

* test(ci): check effective Linux E2E package list

* test(ci): preserve the mobile production compiler barrier

---------

Co-authored-by: Orca Integration Recovery <orca-validation@invalid.example>
2026-10-02 21:52:36 -07:00

221 lines
8.7 KiB
YAML

name: Mobile Checks
on:
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- 'mobile/**'
# Why all of src/shared: mobile imports hundreds of its modules, directly and through the RPC
# recordings and the host params check, so any shared edit can move a golden or break mobile's
# typecheck. Why the root lockfile: those modules resolve their packages from the root install.
- 'src/shared/**'
- '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/**'
# Why: this job holds the only checks that load the Fastfile, so edits to
# it or to the release workflow it guards must re-run them.
- '.github/workflows/mobile.yml'
- '.github/actions/install-node-dependencies/**'
- '.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'
# Why main too: two pull requests can each pass against their own base and disagree once both
# land, and `verify` never runs on main. The same source paths as above, less the CI inputs only
# `verify` reads.
push:
branches:
- main
paths:
- 'mobile/**'
- 'src/shared/**'
- '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/**'
- '.github/workflows/mobile.yml'
concurrency:
# Per commit on main, not per branch. GitHub cancels any PENDING run in a group when a new one
# queues, whatever `cancel-in-progress` says, so one shared main group drops the middle merge of
# three -- and a merge that breaks main there is exactly what `main-tests` checks for.
group: mobile-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
verify:
if: github.event_name == 'pull_request'
# Why ARM: 209s of this job is Vitest and nothing here needs x86: no Android SDK, emulator, gradle,
# Hermes or Watchman, no docker, and no artifacts. The Gemfile.lock lists the generic `ruby`
# platform, so frozen bundler installs without an aarch64-linux entry.
runs-on: ubuntu-24.04-arm
env:
# Why: an unfrozen bundler silently re-resolves when Gemfile.lock drifts
# from the Gemfile, which is how the release jobs could land on different
# fastlane versions in the first place. Fail here instead.
BUNDLE_FROZEN: 'true'
defaults:
run:
working-directory: mobile
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 2
- uses: ./.github/actions/install-node-dependencies
with:
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
# bundler-cache installs mobile/Gemfile.lock, so this job is also what
# proves the pinned fastlane the release workflow depends on still
# resolves — before a release run finds out.
- name: Setup Ruby and fastlane
if: steps.ruby-scope.outputs.should_run != 'false'
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true
working-directory: mobile
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Call installed tools so pnpm's dependency refresh cannot race between checks.
- name: Typecheck
id: production-types
background: true
run: node node_modules/typescript/bin/tsc --noEmit
- wait: production-types
# 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)
run: node scripts/check-tests-typecheck-ratchet.mjs
# This includes the bridged replay of the whole recording corpus, which used to be a second
# step of its own behind RPC_FOUNDATION_BRIDGE=1. A gate nobody can forget to set is the point:
# it fails when a divergence class grows, when a divergence lands in no class at all, or when
# one of the 103 goldens inside the C1 page closure changes the verdict it is pinned to. It is
# ~3 min of test time on its own, and Vitest runs it on a worker beside the rest of the suite,
# so folding it in costs a fraction of that in wall time and one step less to skip.
- 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: Test
if: steps.test-scope.outputs.should_run != 'false'
env:
ORCA_BACKGROUND_LAUNCH: '1'
run: pnpm test
# Reports, never gates: the goldens are content-addressed JSON, so their raw diff is hashes.
# This decodes which recorded behaviour the pull request moves into the job summary, and runs
# after a red Test too, when a reviewer most wants it.
- 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
# Why: nothing else in CI loads the Fastfile, so a syntax error, a broken
# require, or an undefined constant only surfaces mid-release — the
# ios-distribute job failed every run for six days that way. `lanes` just
# loads and lists, so it needs no App Store Connect credentials and makes
# no network calls to Apple.
- 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: Lint
run: pnpm lint
- name: Check formatting
run: pnpm format:check
main-tests:
name: Mobile tests on main
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
- uses: ./.github/actions/install-node-dependencies
with:
cache-dependency-path: |
pnpm-lock.yaml
mobile/pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
# The whole suite, not only the recordings: the mutant and page-bridge suites read the goldens
# too. A red run here names each golden and the `rpc:record` command; the author of the merge
# that turned it red re-records in a follow-up.
- name: Test
env:
ORCA_BACKGROUND_LAUNCH: '1'
run: pnpm test