Skip to content

ci(ios): launch the app in a simulator, not just link it - #290

Merged
doublegate merged 2 commits into
mainfrom
feat/ios-simulator-run
Aug 1, 2026
Merged

ci(ios): launch the app in a simulator, not just link it#290
doublegate merged 2 commits into
mainfrom
feat/ios-simulator-run

Conversation

@doublegate

Copy link
Copy Markdown
Owner

Second v1.30.0 item, after #289 (Android CI).

The gap

Every iOS verification so far is compile/link-only. A binary that links and then traps on launch — a symbol reached lazily, a Metal device that will not create — looks identical to a working one in a build log. docs/mobile-readiness.md has recorded that no iOS build has ever executed.

What this adds

ios.yml now picks an available iPhone simulator, boots it, builds for that concrete destination, installs the .app, launches it, and requires the process to still be alive eight seconds later.

The liveness check asks the simulator via launchctl list rather than trusting simctl launch's exit code — which has already returned 0 by the time a launch-crash happens, and so cannot catch the exact failure this exists to catch.

What it does not prove

Emulation. The app bundles no cartridge, so no ROM has run; that is a licensing question rather than a CI one. docs/mobile-readiness.md now states the narrowed claim — "it launches" — instead of listing the whole runtime question as open. I would rather record the smaller true thing than the larger convenient one.

Written blind

This environment has no macOS/Xcode toolchain, so the runner is the first place any of this executes. The YAML parses and the shell is set -euo pipefail throughout, but I cannot run simctl locally. If a step is wrong, this PR's own CI is where it surfaces — which is the point of putting it in a PR rather than asserting it works.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@doublegate, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 19 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5ef96e57-7c2f-4945-969c-369609542ca8

📥 Commits

Reviewing files that changed from the base of the PR and between c20dd02 and a60acb9.

📒 Files selected for processing (3)
  • .github/workflows/ios.yml
  • CHANGELOG.md
  • docs/mobile-readiness.md

Comment @coderabbitai help to get the list of available commands.

doublegate and others added 2 commits July 31, 2026 19:48
Every iOS verification so far was compile/link-only, and a binary that
links and then traps on launch is indistinguishable from a working one in
a build log.

ios.yml now boots a simulator, installs the built .app, launches it, and
requires the process to still be alive eight seconds later. The liveness
check asks the simulator via launchctl list rather than trusting simctl
launch's exit code, which has already returned 0 by the time a
launch-crash happens.

What this does NOT prove is emulation: the app bundles no cartridge, so
no ROM has run -- that is a licensing question rather than a CI one.
docs/mobile-readiness.md now records the narrowed claim instead of
listing the whole runtime question as open.

Written blind: this environment has no macOS toolchain, so the runner is
the first place any of it executes. If a step is wrong, CI is where that
surfaces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The liveness check piped `launchctl list` straight into `grep -q`. That
exits at its first match and closes the pipe, `launchctl` then takes
SIGPIPE, and `set -o pipefail` makes that the pipeline's status -- so
the step reported "it started and died" for an app that was running.

The first run failed exactly that way, with the process visibly alive
one line above the error.

Capture the listing before searching it, and print it on a genuine
failure so the next one is diagnosable from the log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@doublegate
doublegate force-pushed the feat/ios-simulator-run branch from 7b46d8e to a60acb9 Compare July 31, 2026 23:48
@github-actions

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR updates the iOS CI workflow to boot an available iPhone simulator, install and launch the application bundle, and verify via launchctl list that the process remains alive eight seconds post-launch, alongside corresponding documentation updates.

Blocking issues

None found.

Suggestions

  • .github/workflows/ios.yml:142: app=$(find build/Build/Products -maxdepth 2 -name '*.app' | head -1) uses head -1 over a generic wildcard search. If auxiliary bundles (such as test runners, extensions, or framework targets) are present in build products, find ordering is non-deterministic. Match the target bundle name explicitly (e.g., RustySNES.app).
  • .github/workflows/ios.yml:171: grep -q "$bundle_id" evaluates the bundle identifier as a regular expression, causing dots in reverse-DNS identifiers (e.g., com.rustysnes.app) to act as single-character wildcards. Pass -F (grep -qF "$bundle_id") for exact fixed-string matching.

Nitpicks

  • .github/workflows/ios.yml:93,138: xcodebuild is invoked twice—first for a generic iOS Simulator destination and then again for the specific simulator id=$UDID. Consider combining or eliminating the redundant generic build step to reduce CI runtime.

Automated first-pass review by agy on a self-hosted runner -- not a human review.

@doublegate
doublegate merged commit a2a615b into main Aug 1, 2026
17 checks passed
@doublegate
doublegate deleted the feat/ios-simulator-run branch August 1, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant