Skip to content

CI: deterministic required iOS build gate (prevent iOS compile breaks on main)#6202

Open
lawrencecchen wants to merge 6 commits into
mainfrom
feat-ci-ios-build-gate
Open

CI: deterministic required iOS build gate (prevent iOS compile breaks on main)#6202
lawrencecchen wants to merge 6 commits into
mainfrom
feat-ci-ios-build-gate

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Why

The iOS app + iOS-only CmuxMobile* packages are compiled only in the non-required, flaky ios-simulator job (test-ios.yml). So an iOS-only Swift compile break merges green while every required check passes. That is exactly how the @MainActor/nonisolated break in the composer image-decode path (TerminalComposerView.boundedSendPayload) reached main via #6102 and broke main CI (fix: #6198).

What

  • New ios-build job in ci.yml (runs on every PR + push to main, no path filter → always reports → safe to require). It compiles the cmux-ios scheme for a generic iOS Simulator destination with build-for-testing: no simulator boot, no virtual display, no test execution. Pure compilation is deterministic, so it can be a required check without the flakiness that kept ios-simulator non-required.
  • test-ios.yml also re-runs on push to main (iOS paths) for post-merge detection.

Follow-up (manual, intentionally not in this PR)

After this merges and ios-build is observed running green on a few PRs, add ios-build to the main branch ruleset's required status checks. (Adding a required check that isn't yet on main would block all PRs, so it must land first.)

This would have hard-blocked #6102 and today's break with zero added flakiness.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Note

Low Risk
CI-only workflow addition with no application or runtime behavior changes; main risk is job duration or hosted macOS/Xcode availability, not product code.

Overview
Adds a new ios-build job to ci.yml so iOS-only Swift compile failures are caught on every PR and push to main, not only in the optional ios-simulator workflow.

The job runs on GitHub-hosted macos-26, provisions GhosttyKit via ensure-ghosttykit.sh (with an explicit note not to cache repo-root GhosttyKit.xcframework in a way that could poison macOS cache keys), caches SwiftPM under .spm-cache-ios, picks a concrete iPhone simulator UDID, then resolvePackageDependencies and build-for-testing for cmux-ios with CODE_SIGNING_ALLOWED=NO—compile only, no simulator boot and no test execution.

Intended follow-up (outside this diff): mark ios-build as a required branch protection check once it is green on main.

Reviewed by Cursor Bugbot for commit ac6c927. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds a deterministic iOS compile gate in CI to block iOS-only Swift compile breaks from merging. It runs on every PR and on pushes to main; the flaky push-triggered test-ios run is removed.

  • New Features

    • New ios-build job in ci.yml compiles cmux-ios for a concrete iOS Simulator UDID (picked via simctl) using build-for-testing (compile only; no sim boot or tests).
    • Runs on GitHub‑hosted macos-26 to support sudo during GhosttyKit provisioning.
    • Always provisions GhosttyKit via ensure-ghosttykit.sh; caches SwiftPM in .spm-cache-ios keyed by ios/cmuxPackage/Package.resolved (no actions/cache for GhosttyKit.xcframework).
  • Migration

    • After merge, mark ios-build as a required status check once stable.

Written for commit ac6c927. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Chores
    • Added a new CI job that performs deterministic iOS Simulator compile-only validation on macOS, without running tests or launching a simulator.
    • Resolves iOS Swift package dependencies during the workflow and caches build artifacts to improve repeat run times.
    • Automatically selects a suitable Xcode environment and provisions the required build tooling before compiling the iOS app.

The iOS app and iOS-only CmuxMobile* packages were compiled ONLY in the
non-required, flaky ios-simulator job (test-ios.yml), so an iOS-only compile
break could merge green with every required check passing. That is how the
@MainActor/nonisolated break in the composer image-decode path
(TerminalComposerView) reached main (via #6102) and broke main CI.

Add an 'ios-build' job to ci.yml (runs on every PR + push to main, no path
filter, so it always reports and is safe to require) that compiles the
cmux-ios scheme for a GENERIC iOS Simulator destination with build-for-testing:
no simulator boot, no virtual display, no test execution. Compilation is
deterministic, unlike the sim/UI test jobs, so this can be a required status
check without trading compile-break escapes for flake-blocked merges.

Also re-run test-ios.yml on push to main (iOS paths) for post-merge detection.

Follow-up (manual, after this merges and ios-build is observed running):
add 'ios-build' to the main branch ruleset's required status checks.
@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 16, 2026 2:51am
cmux-staging Building Building Preview, Comment Jun 16, 2026 2:51am

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

A new ios-build GitHub Actions job is added to ci.yml on the macos-26 runner. The job checks out the repository with submodules, selects Xcode via DEVELOPER_DIR, provisions GhosttyKit using Zig and ensure-ghosttykit.sh, caches iOS SwiftPM dependencies in .spm-cache-ios keyed by Package.resolved, deterministically selects a concrete iOS Simulator destination, resolves dependencies for cmux-ios, and runs a compile-only xcodebuild build-for-testing against that simulator with CODE_SIGNING_ALLOWED=NO and no simulator boot.

Changes

iOS CI Compile Gate

Layer / File(s) Summary
ios-build job scaffolding and Xcode selection
.github/workflows/ci.yml
Defines the new ios-build job with macos-26 runner, 35-minute timeout, recursive submodule checkout, and DEVELOPER_DIR Xcode selection logic with fallback to the latest available Xcode, verified by xcodebuild -version.
GhosttyKit, SwiftPM cache, simulator selection, and compile-only build
.github/workflows/ci.yml
Provisions GhosttyKit via Zig and ensure-ghosttykit.sh, caches .spm-cache-ios keyed on ios/cmuxPackage/Package.resolved, deterministically selects a concrete iOS Simulator UDID (preferring iPhone 17/16), resolves SwiftPM dependencies for cmux-ios targeting that simulator, then runs xcodebuild build-for-testing with CODE_SIGNING_ALLOWED=NO and no tests or simulator boot.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐇 Hop, hop, the CI grows,
A new iOS job in the workflow flows.
Zig installs fast, GhosttyKit appears,
SwiftPM caches away all our fears.
Simulators selected, compile gate stands strong —
The bunny applauds this workflow song! 🍎


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Cmux Source Artifacts ❌ Error The diff adds .claude/scheduled_tasks.lock (local tool state file with sessionId/pid/timestamps) and .agents/skills (symlink), both violating source-control-artifacts.md rules on local artifacts. Delete .claude/scheduled_tasks.lock and .agents/skills symlink from the commit, or add them to .gitignore for local-only use.
✅ Passed checks (20 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a deterministic iOS build gate to CI to prevent iOS compile breaks from merging to main.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed PR only modifies GitHub Actions workflow configuration (.github/workflows/ci.yml). No production Swift code changes present, so the Swift actor isolation check is not applicable.
Cmux Swift Blocking Runtime ✅ Passed PR adds only a CI workflow job (.github/workflows/ci.yml) with no production Swift code changes; no blocking/timing synchronization patterns introduced.
Cmux Expensive Synchronous Load ✅ Passed PR contains only GitHub Actions workflow changes (.github/workflows/ci.yml), not production Swift code. Check applies to production Swift changes; not applicable.
Cmux Cache Substitution Correctness ✅ Passed This PR modifies only CI workflow configuration (.github/workflows/ci.yml), not production Swift/TypeScript/JavaScript code. The custom check applies only to production code changes.
Cmux No Hacky Sleeps ✅ Passed GitHub Actions workflow YAML is intentionally out of scope per runtime-no-hacky-sleeps.md. PR only modifies ci.yml with no runtime script changes. The new ios-build job contains no sleep/timing calls.
Cmux Algorithmic Complexity ✅ Passed PR adds CI configuration to .github/workflows/ci.yml, not production code. Code operates on fixed-size collections (iOS simulators on runner) with acceptable O(n) complexity; algorithmic-complexity...
Cmux Swift Concurrency ✅ Passed PR modifies only .github/workflows/ci.yml (GitHub Actions YAML). Zero Swift code changes; check for Swift concurrency patterns is not applicable to CI/infrastructure-only changes.
Cmux Swift @Concurrent ✅ Passed PR adds 2730 Swift files (new codebase) and workflow changes. Checked for @concurrent annotation violations: found ~256 nonisolated sync functions (allowed), 1 @concurrent on async closure (correct...
Cmux Swift File And Package Boundaries ✅ Passed PR contains only CI workflow configuration changes (ios-build job in .github/workflows/ci.yml), not production Swift code changes, so the check for swift-file-package-boundaries violations is not a...
Cmux Swift Logging ✅ Passed PR modifies only .github/workflows/ci.yml (workflow config) with no Swift code changes. The custom check applies to production Swift changes; this PR contains zero Swift files, so the logging rule...
Cmux User-Facing Error Privacy ✅ Passed PR adds only CI/CD infrastructure (ios-build job to .github/workflows/ci.yml), not production code. CI workflow outputs are developer-facing operational logs, not user-facing error messages; they f...
Cmux Full Internationalization ✅ Passed PR adds only workflow infrastructure to ci.yml with no production code, user-facing text, or localization catalog changes. All content is developer-only comments and CI configuration tokens, which...
Cmux Swiftui State Layout ✅ Passed PR modifies only .github/workflows/ci.yml (a CI/CD workflow file), containing no SwiftUI source code changes. The custom check for SwiftUI state layout violations is not applicable.
Cmux Architecture Rethink ✅ Passed Custom check for Swift architecture rethink is not applicable; PR contains only CI workflow configuration changes with no Swift source code modifications.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no Swift code changes; it only adds a GitHub Actions workflow job to ci.yml. The check applies only to Swift window-related code changes, making it not applicable here.
Description check ✅ Passed The pull request description provides a clear summary of changes, rationale, and technical approach, with good detail about the problem being solved and the solution implemented.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-ci-ios-build-gate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0886258. Configure here.

Comment thread .github/workflows/ci.yml
…nsure script

Autoreview P1: the actions/cache step keyed on repo-root GhosttyKit.xcframework
would poison the shared ghosttykit-<sha> key. ensure-ghosttykit.sh writes the
framework to ghostty/macos and manages its own $HOME cache, so it does not
populate the repo-root path the macOS jobs cache via download-prebuilt. Mirror
the ios-simulator job: provision unconditionally, no actions/cache here.
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a new ios-build job to ci.yml that compiles the cmux-ios scheme against a concrete iOS Simulator UDID using xcodebuild build-for-testing — no simulator boot, no test execution, no virtual display. Because the job is in ci.yml (no path filter, runs on every PR and push to main), it is safe to promote to a required status check once it is observed green.

  • ios-build job (macos-26, 35-min timeout): provisions GhosttyKit via ensure-ghosttykit.sh, caches SwiftPM under .spm-cache-ios, resolves packages, then compiles with CODE_SIGNING_ALLOWED=NO.
  • Simulator picker: Python script resolves a concrete UDID from simctl (preferred: iPhone 17/16 → any iPhone), which is required because a pure generic/platform=iOS Simulator destination was found unreliable for compilation.
  • Post-merge detection gap (pre-existing, noted in earlier review thread): test-ios.yml was not updated to trigger on push to main, so the stated "post-merge detection" goal from the PR description is not yet delivered by this change.

Confidence Score: 5/5

CI-only workflow addition with no production code changes; safe to merge as-is.

The change is purely additive — a new compile-only CI job with no effect on shipped code. The job logic is straightforward: provision toolchain, resolve packages, compile. The only fragile spot is the Python simulator picker's bare next() fallback, which would emit a traceback instead of a clear error message if no iPhone simulator exists on the runner, but this won't silently pass or corrupt anything.

No files require special attention; the single changed file is a CI workflow with no production impact.

Important Files Changed

Filename Overview
.github/workflows/ci.yml Adds a new ios-build job that compiles cmux-ios for a concrete iOS Simulator UDID using build-for-testing; deterministic compile gate with no sim boot or test execution. Minor Python StopIteration risk in the simulator picker fallback.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[PR or push to main] --> B[ci.yml triggered - no path filter]
    B --> C[ios-build job - macos-26]
    C --> D[Checkout with submodules]
    D --> E[Select Xcode]
    E --> F[Provision GhosttyKit\nensure-ghosttykit.sh]
    F --> G[Cache SwiftPM\n.spm-cache-ios]
    G --> H[Pick simulator UDID\nvia simctl]
    H --> I[Resolve packages\nxcodebuild -resolvePackageDependencies]
    I --> J[Build for testing\nxcodebuild build-for-testing\nCODE_SIGNING_ALLOWED=NO]
    J --> K{Compile OK?}
    K -->|Yes| L[Job green - safe to require]
    K -->|No| M[Job red - blocks PR]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[PR or push to main] --> B[ci.yml triggered - no path filter]
    B --> C[ios-build job - macos-26]
    C --> D[Checkout with submodules]
    D --> E[Select Xcode]
    E --> F[Provision GhosttyKit\nensure-ghosttykit.sh]
    F --> G[Cache SwiftPM\n.spm-cache-ios]
    G --> H[Pick simulator UDID\nvia simctl]
    H --> I[Resolve packages\nxcodebuild -resolvePackageDependencies]
    I --> J[Build for testing\nxcodebuild build-for-testing\nCODE_SIGNING_ALLOWED=NO]
    J --> K{Compile OK?}
    K -->|Yes| L[Job green - safe to require]
    K -->|No| M[Job red - blocks PR]
Loading

Reviews (5): Last reviewed commit: "ios-build gate: use concrete simulator +..." | Re-trigger Greptile

Autoreview P3s:
- Cache key hashed a non-existent lockfile path; the real iOS lockfile is
  ios/cmuxPackage/Package.resolved. Key on it so the cache actually rotates on
  dependency changes instead of pinning the first checkout.
- Drop the test-ios.yml push:main trigger. detect-ios-changes treats every
  non-PR event as should_run=true, so any package push to main (incl. macOS-only
  packages) would boot the flaky iphone+ipad simulator jobs. The deterministic
  ios-build job in ci.yml already runs on push to main and is the real
  compile-break detection; the flaky sim push trigger added noise without
  added prevention.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 1268-1271: Add `persist-credentials: false` to the `with:` section
of the Checkout step in the ios-build job that uses
`actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd`. This option
prevents git credentials from being persisted in the local git config, reducing
token exposure in subsequent job steps and aligning with the security hardening
pattern used in test-ios.yml.
- Around line 1328-1342: The iOS simulator build step using xcodebuild with the
build-for-testing target is missing the -disableAutomaticPackageResolution flag,
creating inconsistency with the package resolution approach used in other build
steps. Add -disableAutomaticPackageResolution to the xcodebuild command
arguments in the "Build iOS app for simulator" step to ensure deterministic
behavior and align with the explicit package resolution pattern established
elsewhere in the workflow.

In @.github/workflows/test-ios.yml:
- Around line 24-39: The `Packages/**` path in the push paths filter on line 36
is too broad and bypasses the iOS-specific package filtering, causing
unnecessary job triggers for non-iOS packages. Remove the `Packages/**` catchall
from the push paths section and rely only on the specific iOS-owned package
paths (CMUXAuthCore, CMUXMobileCore, CmuxAuthRuntime, CmuxMobile*, CMUXMobile*).
Alternatively, if broader package path matching is intentional, apply the same
file-change filtering logic from the `should_run` regex (line 93) to push events
instead of unconditionally forcing `should_run=true` on line 86 for non-PR
events.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f35b3c1-fb7b-406c-970c-f34fe317cdf0

📥 Commits

Reviewing files that changed from the base of the PR and between 723329f and 0886258.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • .github/workflows/test-ios.yml

Comment thread .github/workflows/ci.yml
Comment on lines +1268 to +1271
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify checkout hardening mismatch between ci.yml ios-build and test-ios.yml jobs.
rg -n -C3 'ios-build:|name: Checkout|persist-credentials|submodules:' .github/workflows/ci.yml .github/workflows/test-ios.yml

Repository: manaflow-ai/cmux

Length of output: 9720


Add persist-credentials: false to the ios-build checkout step for credential hardening.

The checkout in this job leaves credentials persisted in the local git config by default, increasing token exposure in subsequent steps. This differs from the hardened pattern used in test-ios.yml. Add persist-credentials: false to align with security best practices.

Suggested patch
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
+         persist-credentials: false
          submodules: recursive
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
submodules: recursive
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 1268-1271: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1268 - 1271, Add `persist-credentials:
false` to the `with:` section of the Checkout step in the ios-build job that
uses `actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd`. This option
prevents git credentials from being persisted in the local git config, reducing
token exposure in subsequent job steps and aligning with the security hardening
pattern used in test-ios.yml.

Source: Linters/SAST tools

Comment thread .github/workflows/ci.yml
Comment on lines +1328 to +1342
- name: Build iOS app for simulator (compile only, no tests, no boot)
run: |
set -euo pipefail
# build-for-testing compiles the app and the test targets for the
# simulator using a GENERIC destination, so nothing is booted and the
# result is deterministic. This catches the Swift compile breaks
# (actor isolation, missing await, etc.) that only manifest in the iOS
# build and are invisible to the macOS unit-test job.
xcodebuild -workspace ios/cmux.xcworkspace \
-scheme cmux-ios \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath /tmp/cmux-ios-build \
-clonedSourcePackagesDirPath .spm-cache-ios \
CODE_SIGNING_ALLOWED=NO \
build-for-testing

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify ios-build resolves packages explicitly, then builds without -disableAutomaticPackageResolution.
rg -n -C4 'name: Resolve packages|resolvePackageDependencies|name: Build iOS app for simulator|build-for-testing|disableAutomaticPackageResolution' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 7893


🌐 Web query:

xcodebuild -disableAutomaticPackageResolution flag documentation

💡 Result:

The xcodebuild flag -disableAutomaticPackageResolution is used to prevent the build system from automatically resolving or updating Swift package dependencies to versions other than those explicitly recorded in the Package.resolved file [1][2][3]. By using this flag, you ensure that your CI pipeline or build environment strictly adheres to the state defined in the Package.resolved file [4]. This is a common best practice for CI/CD workflows to prevent unexpected dependency changes and to ensure build reproducibility [4][5]. Key points regarding this flag: - Functionality: It forces xcodebuild to rely on the existing Package.resolved file. If the project's dependencies are out of sync with this file, the build will typically fail rather than attempting to automatically resolve the differences [6][5]. - Usage: It is frequently paired with -onlyUsePackageVersionsFromResolvedFile, which serves a similar purpose [1][2][3]. - CI/CD Context: It is standard to include this flag in CI environments to guarantee that the code being tested uses the exact versions of dependencies that were vetted and committed to source control [4]. If you encounter an error stating that an out-of-date resolved file was detected, this indicates that your Package.resolved file does not match your current project dependency configuration [6][5]. In such cases, you must update the Package.resolved file in your local development environment and commit the changes to your repository before the CI can proceed successfully [4][6].

Citations:


🏁 Script executed:

# Verify line numbers in the snippet match the comment range
sed -n '1328,1342p' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 528


Add -disableAutomaticPackageResolution to prevent implicit package resolution in iOS build step.

Line 1320 explicitly resolves packages, but the subsequent build-for-testing at line 1336 omits -disableAutomaticPackageResolution, creating inconsistency with all other build steps in the workflow and risking non-deterministic behavior.

Suggested patch
           xcodebuild -workspace ios/cmux.xcworkspace \
             -scheme cmux-ios \
             -destination 'generic/platform=iOS Simulator' \
             -derivedDataPath /tmp/cmux-ios-build \
             -clonedSourcePackagesDirPath .spm-cache-ios \
+            -disableAutomaticPackageResolution \
             CODE_SIGNING_ALLOWED=NO \
             build-for-testing
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1328 - 1342, The iOS simulator build
step using xcodebuild with the build-for-testing target is missing the
-disableAutomaticPackageResolution flag, creating inconsistency with the package
resolution approach used in other build steps. Add
-disableAutomaticPackageResolution to the xcodebuild command arguments in the
"Build iOS app for simulator" step to ensure deterministic behavior and align
with the explicit package resolution pattern established elsewhere in the
workflow.

Comment thread .github/workflows/test-ios.yml Outdated
Comment thread .github/workflows/ci.yml
Comment on lines 1251 to +1336
kill "$VDISPLAY_PERSISTENT_PID" >/dev/null 2>&1 || true
fi
rm -f /tmp/cmux-vdisplay-persistent.ready /tmp/cmux-vdisplay-persistent.id /tmp/cmux-vdisplay-persistent.log

ios-build:
# Deterministic iOS compile gate. The iOS app and the iOS-only CmuxMobile*
# packages are otherwise compiled ONLY in the non-required, flaky
# ios-simulator job (test-ios.yml), so an iOS-only compile break can merge
# green even though every required check passed. That is exactly how the
# @MainActor/nonisolated break in the composer image-decode path
# (TerminalComposerView) reached main. This job compiles the iOS app for the
# simulator WITHOUT running tests or booting a simulator, so it is
# deterministic (no virtual display, no sim boot) and safe to require.
# It runs on every PR and on push to main; make it a required status check.
runs-on: ${{ vars.MACOS_RUNNER_26 || 'warp-macos-26-arm64-6x' }}
timeout-minutes: 35
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive

- name: Select Xcode
run: |
set -euo pipefail
if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then
XCODE_DIR="/Applications/Xcode.app/Contents/Developer"
else
XCODE_APP="$(find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null | sort | tail -n 1 || true)"
if [ -n "$XCODE_APP" ]; then
XCODE_DIR="$XCODE_APP/Contents/Developer"
else
echo "No Xcode.app found under /Applications" >&2
exit 1
fi
fi
echo "DEVELOPER_DIR=$XCODE_DIR" >> "$GITHUB_ENV"
export DEVELOPER_DIR="$XCODE_DIR"
xcodebuild -version

- name: Provision GhosttyKit
run: |
# The iOS app links GhosttyKit (incl. its iOS + iOS-simulator slices)
# via a local-path binaryTarget, so it must exist before resolve.
# Mirror the ios-simulator job exactly: ensure-ghosttykit.sh manages
# its own $HOME/.cache/cmux/ghosttykit cache and writes the framework
# where the iOS workspace expects it. Do NOT add an actions/cache step
# keyed on repo-root GhosttyKit.xcframework here: ensure-ghosttykit.sh
# does not populate that path, and the shared ghosttykit-<sha> key is
# owned by the macOS jobs that use download-prebuilt (a real dir), so
# caching a symlink/absent path from this job would poison that key.
./scripts/install-zig-ci.sh
./scripts/ensure-ghosttykit.sh

- name: Cache Swift packages
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: .spm-cache-ios
key: spm-ios-${{ hashFiles('ios/cmuxPackage/Package.resolved') }}
restore-keys: spm-ios-

- name: Resolve packages
run: |
set -euo pipefail
xcodebuild -workspace ios/cmux.xcworkspace \
-scheme cmux-ios \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath /tmp/cmux-ios-build \
-clonedSourcePackagesDirPath .spm-cache-ios \
-resolvePackageDependencies

- name: Build iOS app for simulator (compile only, no tests, no boot)
run: |
set -euo pipefail
# build-for-testing compiles the app and the test targets for the
# simulator using a GENERIC destination, so nothing is booted and the
# result is deterministic. This catches the Swift compile breaks
# (actor isolation, missing await, etc.) that only manifest in the iOS
# build and are invisible to the macOS unit-test job.
xcodebuild -workspace ios/cmux.xcworkspace \
-scheme cmux-ios \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath /tmp/cmux-ios-build \
-clonedSourcePackagesDirPath .spm-cache-ios \
CODE_SIGNING_ALLOWED=NO \
build-for-testing

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.

P1 test-ios.yml push trigger described but not added

The PR description lists "test-ios.yml also re-runs on push to main (iOS paths) for post-merge detection" as one of the explicit goals of this change. However, test-ios.yml was not modified — its on: block still only contains pull_request (paths-filtered) and workflow_dispatch. A push to main that touches iOS paths therefore still will not trigger the simulator test matrix, leaving the described post-merge detection capability absent despite the PR stating it was included.

The job died at Provision GhosttyKit because ensure-ghosttykit.sh calls sudo,
which has no passwordless/tty path on the self-hosted fleet runner that
MACOS_RUNNER_26/warp resolves to. The proven iOS jobs (ios-simulator,
mobile-core-package) run on GitHub-hosted macos-26 for this reason; match them.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)

1268-1271: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add checkout credential hardening in ios-build.

Line 1268 uses actions/checkout without disabling credential persistence. Add persist-credentials: false in the checkout with: block to reduce token exposure across subsequent steps.

Suggested patch
       - name: Checkout
         uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
         with:
+          persist-credentials: false
           submodules: recursive
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1268 - 1271, In the Checkout step of
the ios-build workflow that uses actions/checkout, add persist-credentials:
false to the with block. This will disable credential persistence and reduce
token exposure in subsequent workflow steps, improving the security posture of
the workflow.

1330-1336: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep ios-build deterministic by disabling auto package resolution during build.

Line 1330’s build-for-testing omits -disableAutomaticPackageResolution even though packages are explicitly resolved earlier. Add the flag so the build cannot drift from the resolved dependency state.

Suggested patch
           xcodebuild -workspace ios/cmux.xcworkspace \
             -scheme cmux-ios \
             -destination 'generic/platform=iOS Simulator' \
             -derivedDataPath /tmp/cmux-ios-build \
             -clonedSourcePackagesDirPath .spm-cache-ios \
+            -disableAutomaticPackageResolution \
             CODE_SIGNING_ALLOWED=NO \
             build-for-testing
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1330 - 1336, The xcodebuild command
with build-for-testing is missing the -disableAutomaticPackageResolution flag,
which allows the build to potentially resolve packages differently than intended
and drift from the explicitly resolved dependency state. Add the
-disableAutomaticPackageResolution flag to the xcodebuild invocation before the
build-for-testing argument to ensure the build remains deterministic and uses
only the pre-resolved package versions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 1268-1271: In the Checkout step of the ios-build workflow that
uses actions/checkout, add persist-credentials: false to the with block. This
will disable credential persistence and reduce token exposure in subsequent
workflow steps, improving the security posture of the workflow.
- Around line 1330-1336: The xcodebuild command with build-for-testing is
missing the -disableAutomaticPackageResolution flag, which allows the build to
potentially resolve packages differently than intended and drift from the
explicitly resolved dependency state. Add the -disableAutomaticPackageResolution
flag to the xcodebuild invocation before the build-for-testing argument to
ensure the build remains deterministic and uses only the pre-resolved package
versions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 59d6e972-3a44-459b-8b82-cc1f3cb5bf9d

📥 Commits

Reviewing files that changed from the base of the PR and between 0886258 and 1abd680.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

A pure app compile is the break class we gate; plain build with a generic
simulator destination is more robust than build-for-testing (which also builds
the iOS test targets and can fail for unrelated reasons). The ios-simulator job
still builds+runs the test targets on iOS PRs.
…s-simulator job)

A generic 'platform=iOS Simulator' destination with plain build failed the
build step on CI. The proven ios-simulator job compiles the app for a CONCRETE
simulator UDID. Match it: add a Pick simulator step (simctl) and use
-destination platform=iOS Simulator,id=$SIMULATOR_ID with build-for-testing
(compiles app + test targets, no boot, no tests run).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)

1356-1362: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Disable automatic package resolution during compile gate build.

The compile step resolves packages implicitly unless you pass -disableAutomaticPackageResolution, which weakens reproducibility versus your explicit resolve step.

Suggested patch
           xcodebuild -workspace ios/cmux.xcworkspace \
             -scheme cmux-ios \
             -destination "platform=iOS Simulator,id=$SIMULATOR_ID" \
             -derivedDataPath /tmp/cmux-ios-build \
             -clonedSourcePackagesDirPath .spm-cache-ios \
+            -disableAutomaticPackageResolution \
             CODE_SIGNING_ALLOWED=NO \
             build-for-testing
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1356 - 1362, The xcodebuild command in
the compile gate build step does not disable automatic package resolution, which
can weaken reproducibility. Add the `-disableAutomaticPackageResolution` flag to
the xcodebuild invocation that includes the options like -workspace
ios/cmux.xcworkspace, -scheme cmux-ios, and build-for-testing. This flag should
be added alongside the existing flags to ensure packages are only resolved
through the explicit resolution step and not implicitly during compilation.

1273-1276: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Harden checkout credentials in ios-build.

Line 1273 checkout still persists credentials by default. Add persist-credentials: false to reduce token exposure across later steps.

Suggested patch
       - name: Checkout
         uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
         with:
+          persist-credentials: false
           submodules: recursive
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1273 - 1276, The checkout step using
actions/checkout is persisting credentials by default, which exposes the
authentication token to subsequent workflow steps. Add persist-credentials:
false as a configuration option in the with section of the checkout action
(alongside the existing submodules: recursive parameter) to prevent credential
persistence and reduce token exposure.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 1329-1331: The second next() call on line 1331 that searches for
an iPhone simulator lacks a default value, which will raise StopIteration and
cause a hard job failure if no iPhone simulator is found. Add a default value
(None) to this next() call and then add an explicit guard check after line 1331
to verify that selected is not None before attempting to access
selected['udid']. If selected is None, exit the script with an appropriate error
message indicating that no available iPhone simulator was found.

---

Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 1356-1362: The xcodebuild command in the compile gate build step
does not disable automatic package resolution, which can weaken reproducibility.
Add the `-disableAutomaticPackageResolution` flag to the xcodebuild invocation
that includes the options like -workspace ios/cmux.xcworkspace, -scheme
cmux-ios, and build-for-testing. This flag should be added alongside the
existing flags to ensure packages are only resolved through the explicit
resolution step and not implicitly during compilation.
- Around line 1273-1276: The checkout step using actions/checkout is persisting
credentials by default, which exposes the authentication token to subsequent
workflow steps. Add persist-credentials: false as a configuration option in the
with section of the checkout action (alongside the existing submodules:
recursive parameter) to prevent credential persistence and reduce token
exposure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 439abc11-13e9-499d-8e59-66915a91f69b

📥 Commits

Reviewing files that changed from the base of the PR and between 23089f6 and ac6c927.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml

Comment thread .github/workflows/ci.yml
Comment on lines +1329 to +1331
selected = next((d for name in preferred for d in devices if d.get("name") == name), None)
selected = selected or next(d for d in devices if d.get("name", "").startswith("iPhone"))
print(f"SIMULATOR_ID={selected['udid']}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the file exists and read the relevant lines
head -c 200 .github/workflows/ci.yml | head -1
wc -l .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 99


🏁 Script executed:

# Read lines around 1329-1331
sed -n '1320,1340p' .github/workflows/ci.yml

Repository: manaflow-ai/cmux

Length of output: 1289


Add explicit error handling for missing iPhone simulator.

Line 1331's next() call lacks a default value and will raise StopIteration if no available iPhone simulator is found, causing a hard job failure with no diagnostic output. This can occur if runner images change or become unavailable.

Add a default and explicit guard:

Suggested patch
           preferred = ["iPhone 17", "iPhone 16"]
           selected = next((d for name in preferred for d in devices if d.get("name") == name), None)
-          selected = selected or next(d for d in devices if d.get("name", "").startswith("iPhone"))
+          selected = selected or next((d for d in devices if d.get("name", "").startswith("iPhone")), None)
+          if selected is None:
+              raise SystemExit("No available iPhone simulator found via simctl")
           print(f"SIMULATOR_ID={selected['udid']}")
           print(f"SIMULATOR_NAME={selected['name']}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1329 - 1331, The second next() call on
line 1331 that searches for an iPhone simulator lacks a default value, which
will raise StopIteration and cause a hard job failure if no iPhone simulator is
found. Add a default value (None) to this next() call and then add an explicit
guard check after line 1331 to verify that selected is not None before
attempting to access selected['udid']. If selected is None, exit the script with
an appropriate error message indicating that no available iPhone simulator was
found.

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