Skip to content

Run iOS RNTester E2E tests on Apple Silicon runners - #59642

Open
cipolleschi wants to merge 8 commits into
mainfrom
cipolleschi/e2e-ios-8-apple-silicon
Open

cipolleschi wants to merge 8 commits into
mainfrom
cipolleschi/e2e-ios-8-apple-silicon

Conversation

@cipolleschi

@cipolleschi cipolleschi commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Stack (iOS RNTester E2E cost, review bottom-up):

  1. Retry failing iOS E2E flows once and stop rerunning real failures #59635
  2. Cancel superseded Test All runs on pull requests #59636
  3. Run iOS E2E flows in a single Maestro invocation #59637
  4. Run the FlatList maintainVisibleContentPosition E2E flows in Release only on iOS #59638
  5. Overlap simulator boot with the rest of the iOS E2E setup #59640
  6. Fail iOS RNTester E2E straight away when the app artifact is missing #59641
  7. Run iOS RNTester E2E tests on Apple Silicon runners #59642 ← this PR

Summary:

Part 7 of the iOS E2E cost stack.

The RNTester E2E jobs ran on macos-26-large, the Intel x86_64 runner, and the -large runners are billed at a premium.

Run them on the standard arm64 macos-26 runner instead. The simulator and app run natively on arm64, and standard GitHub-hosted runners aren't billed for public repositories. RNTesterApp-NewArch-{Debug,Release} are already universal (lipo shows x86_64 + arm64 for the app binary, hermesvm, React and ReactNativeDependencies), so the build job doesn't change.

Measured on CI (table in the Test Plan), the 3-core standard runner is faster than macos-26-large despite having fewer cores: the Debug job took 22.0 min instead of 44.6, and the Release job 43.2 min instead of 55.0. The two jobs run in parallel, so the E2E stage now takes about as long as the Release job (~43 min instead of ~55). Billed minutes go from ~100 -large minutes per run to none. The only step that got slower is the Maestro download and install (~2 min more). If the standard runner ever turns out to be too slow, macos-26-xlarge (arm64, 5 cores) is the fallback. The template app E2E is unchanged.

Changelog:

[INTERNAL] [CHANGED] - Run iOS RNTester E2E tests on Apple Silicon runners

Test Plan:

  • Checked the architectures of the CI artifacts from run 40888922130 with lipo -info.

  • Ran the RNTester Release artifact locally on an arm64 iPhone 17 Pro simulator with Maestro 2.6.1 (see parts 1 and 3).

  • CI, comparing the previous run of this PR (macos-26, arm64) with Fail iOS RNTester E2E straight away when the app artifact is missing #59641 (the same stack, still on macos-26-large). Both runs still included the since-dropped deep-link change, so the comparison is fair. Single samples, so expect noise.

    Job macos-26-large (Fail iOS RNTester E2E straight away when the app artifact is missing #59641) macos-26 (Run iOS RNTester E2E tests on Apple Silicon runners #59642)
    Debug (20 flows): total 44.6 min 22.0 min
    Debug: test step 33.9 min 16.1 min
    Release (44 flows): total 55.0 min 43.2 min
    Release: test step 45.3 min 37.2 min
    Maestro install 1.2–1.4 min 3.1–3.3 min
    yarn install 6.5–6.7 min 1.5–1.7 min

    The flows run faster on arm64 even with 3 cores. The only consistently slower step is downloading and installing Maestro (~2 min more); yarn install timing varies between runs on both runners.

  • "GL pipe is running in software mode" still appears once per job on arm64 (vs 15–18 times per job on Intel), so I'm not claiming GPU rendering.

Each RNTester iOS flow had 5 attempts inside the job, and the whole job
(both flavors, every flow, all setup) was retried up to 3 times on top, so
a flow could run 15 times. The runner also stopped at the first flow that
exhausted its attempts, hiding later failures.

- Give each flow 2 attempts, rebooting the simulator before the retry.
- Keep running the remaining flows after a failure and report every
  failed flow at the end, with a step summary.
- Persist per-flow results between CI attempts (same format as the
  Android runner) so retries skip flows that already passed.
- Only retry the workflow when a job stopped before running every flow.
  A flow that failed both attempts fails the run immediately.
- Upload the recording, JUnit report and Maestro debug output for failed
  attempts only.
Test All had no concurrency group, so every push to a pull request left
the previous run going to completion. Over Oct 27-29 about 40% of the
macOS minutes spent on iOS E2E went to runs whose pull request had
already received a newer push.

Cancel in-progress runs of the same pull request. Push and
workflow_dispatch runs get a unique group and are never cancelled.
The runner started a new `maestro test` process for every flow. Each one
paid for JVM startup and an XCTest driver reinstall (~24s) plus stopping
the screen recording (~3s): about 21 minutes of every ~75 minute RNTester
job.

- Run every pending flow in one `maestro test` invocation and read the
  per-flow results from its JUnit report. Flows missing from the report
  (Maestro crashed or timed out) count as failed.
- Retry each failed flow on its own, on a rebooted simulator, with a
  screen recording. Passing runs don't produce recordings any more;
  failures keep Maestro's screenshots and debug output.
- Add an `exclude-tags` input, and skip the `android-only` flows in
  RNTester. They only contain an Android-gated `runFlow`, but still paid
  the per-flow startup cost on iOS.
…only on iOS

The 24 FlatList maintainVisibleContentPosition flows are half of the
RNTester Maestro suite (~37 minutes per iOS job). Android already runs
them against the release build only. On iOS about 88% of their failed
attempts over Oct 27-29 were in Debug (133 vs 17).

Rename their `android-release-only` tag to `release-only` and exclude it
from the Debug job on both platforms.
The iOS E2E action did all of its setup one step after another before
touching the simulator. `xcrun simctl install` then waited ~2 minutes for
the simulator to finish booting.

- Start booting the simulator as the first step (new `boot` subcommand
  of maestro-ios.js), so it boots while Maestro, Java, the node modules
  and Metro are being set up.
- Drop the `idb-companion` install. Maestro removed its IDB dependency on
  iOS in 1.x (see "Removed IDB on iOS" in its changelog) and 2.6.1 doesn't
  reference it.
- Drop the second Node.js setup: both callers (RNTester and the template
  app) already set up Node.js before using the action.
test_e2e_ios_rntester only checks that test_ios_rntester succeeded, but
test_ios_rntester uses continue-on-error, so it reports success even when
the leg that uploads RNTesterApp-NewArch-<flavor> failed. In run
40709683649 the Release app was never uploaded and the E2E job still
started (and retried) three times.

Download the app as the first step after checkout, and treat a missing
app as a non-retryable failure with an error that points to the build
job, instead of an incomplete run that triggers the workflow retries.
The E2E jobs ran on macos-26-large, the Intel x86_64 runner: the iOS
simulator ran under x86_64 and rendered in software ("GL pipe is running
in software mode" in every log), and the -large runners are billed at a
premium.

Run them on the standard arm64 macos-26 runner instead, where the
simulator runs natively with GPU rendering and which isn't billed for
public repositories. RNTesterApp-NewArch-{Debug,Release} are already
universal (x86_64 + arm64 for the app and all its frameworks), so the
build job doesn't need to change.

If the 3-core standard runner turns out to be too slow, macos-26-xlarge
(arm64, 5 cores) is the fallback.
The arm64 runner still logs "GL pipe is running in software mode", so
don't claim GPU rendering.
@cipolleschi
cipolleschi force-pushed the cipolleschi/e2e-ios-8-apple-silicon branch from 61952bf to 7d20429 Compare October 30, 2026 10:57
@cipolleschi
cipolleschi force-pushed the cipolleschi/e2e-ios-7-artifact-gate branch from 0136439 to 23b9b77 Compare October 30, 2026 10:57
@cipolleschi
cipolleschi changed the base branch from cipolleschi/e2e-ios-7-artifact-gate to main October 30, 2026 11:46
@meta-codesync

meta-codesync Bot commented Oct 30, 2026

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D128581888.

@cortinico cortinico left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review automatically exported from Phabricator review in Meta.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants