CI: deterministic required iOS build gate (prevent iOS compile breaks on main)#6202
CI: deterministic required iOS build gate (prevent iOS compile breaks on main)#6202lawrencecchen wants to merge 6 commits into
Conversation
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughA new ChangesiOS CI Compile Gate
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error)
✅ Passed checks (20 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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.
…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 SummaryAdds a new
Confidence Score: 5/5CI-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
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]
%%{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]
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.
There was a problem hiding this comment.
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
📒 Files selected for processing (2)
.github/workflows/ci.yml.github/workflows/test-ios.yml
| - name: Checkout | ||
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | ||
| with: | ||
| submodules: recursive |
There was a problem hiding this comment.
🧩 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.ymlRepository: 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.
| - 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
| - 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 |
There was a problem hiding this comment.
🧩 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.ymlRepository: 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:
- 1: https://forums.swift.org/t/xcode-ignoring-package-resolved-file/65031
- 2: https://gist.github.com/palaniraja/4e58f40cae21105d532952a5c6eb1b30
- 3: https://gist.github.com/ldakhoa/020ac54241d11e8616bea97f7a4292bc
- 4: https://developer.apple.com/forums/thread/699342
- 5: https://forums.swift.org/t/error-during-package-resolution-on-ci-machine-whenever-updating-package-dependencies/56472
- 6: https://developer.apple.com/forums/thread/718178
🏁 Script executed:
# Verify line numbers in the snippet match the comment range
sed -n '1328,1342p' .github/workflows/ci.ymlRepository: 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.
| 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
1268-1271:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAdd checkout credential hardening in
ios-build.Line 1268 uses
actions/checkoutwithout disabling credential persistence. Addpersist-credentials: falsein the checkoutwith: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 winKeep
ios-builddeterministic by disabling auto package resolution during build.Line 1330’s
build-for-testingomits-disableAutomaticPackageResolutioneven 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
📒 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).
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
1356-1362:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDisable 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 winHarden checkout credentials in
ios-build.Line 1273 checkout still persists credentials by default. Add
persist-credentials: falseto 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
📒 Files selected for processing (1)
.github/workflows/ci.yml
| 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']}") |
There was a problem hiding this comment.
🧩 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.ymlRepository: manaflow-ai/cmux
Length of output: 99
🏁 Script executed:
# Read lines around 1329-1331
sed -n '1320,1340p' .github/workflows/ci.ymlRepository: 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.

Why
The iOS app + iOS-only
CmuxMobile*packages are compiled only in the non-required, flakyios-simulatorjob (test-ios.yml). So an iOS-only Swift compile break merges green while every required check passes. That is exactly how the@MainActor/nonisolatedbreak in the composer image-decode path (TerminalComposerView.boundedSendPayload) reached main via #6102 and broke main CI (fix: #6198).What
ios-buildjob in ci.yml (runs on every PR + push to main, no path filter → always reports → safe to require). It compiles thecmux-iosscheme for a generic iOS Simulator destination withbuild-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 keptios-simulatornon-required.test-ios.ymlalso 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-buildis observed running green on a few PRs, addios-buildto themainbranch 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
Need help on this PR? Tag
/codesmithwith 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-buildjob toci.ymlso iOS-only Swift compile failures are caught on every PR and push tomain, not only in the optionalios-simulatorworkflow.The job runs on GitHub-hosted
macos-26, provisions GhosttyKit viaensure-ghosttykit.sh(with an explicit note not to cache repo-rootGhosttyKit.xcframeworkin a way that could poison macOS cache keys), caches SwiftPM under.spm-cache-ios, picks a concrete iPhone simulator UDID, thenresolvePackageDependenciesandbuild-for-testingforcmux-ioswithCODE_SIGNING_ALLOWED=NO—compile only, no simulator boot and no test execution.Intended follow-up (outside this diff): mark
ios-buildas a required branch protection check once it is green onmain.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-triggeredtest-iosrun is removed.New Features
ios-buildjob inci.ymlcompilescmux-iosfor a concrete iOS Simulator UDID (picked viasimctl) usingbuild-for-testing(compile only; no sim boot or tests).macos-26to supportsudoduringGhosttyKitprovisioning.GhosttyKitviaensure-ghosttykit.sh; caches SwiftPM in.spm-cache-ioskeyed byios/cmuxPackage/Package.resolved(noactions/cacheforGhosttyKit.xcframework).Migration
ios-buildas a required status check once stable.Written for commit ac6c927. Summary will update on new commits.
Summary by CodeRabbit