Repository navigation
Fix iOS App Store production bundle id - #7809
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe App Store lane now uses ChangesApp Store identity alignment
APNs production bundle alignment
Estimated code review effort: 4 (Complex) | ~50 minutes Sequence Diagram(s)sequenceDiagram
participant AppStoreLane
participant xcodebuild
participant codesign
participant security
participant IPAUpload
AppStoreLane->>xcodebuild: Archive and export IPA
AppStoreLane->>codesign: Verify embedded app signature
AppStoreLane->>security: Decode provisioning profile and signed entitlements
AppStoreLane->>AppStoreLane: Compare com.cmux.app identity values
AppStoreLane->>IPAUpload: Upload after validation succeeds
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 24 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (24 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 |
This comment has been minimized.
This comment has been minimized.
Greptile SummaryThis PR moves the iOS App Store production identity to
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (5): Last reviewed commit: "Handle stale App Store profile secrets" | Re-trigger Greptile |
|
|
||
| TEAM_ID="${IOS_APPSTORE_TEAM_ID:-7WLXT3NR37}" | ||
| BUNDLE_IDENTIFIER="${IOS_APPSTORE_BUNDLE_IDENTIFIER:-com.cmuxterm.app}" | ||
| BUNDLE_IDENTIFIER="${IOS_APPSTORE_BUNDLE_IDENTIFIER:-com.cmux.app}" |
There was a problem hiding this comment.
When IOS_APPSTORE_PROVISIONING_PROFILE_BASE64 still contains a profile for the old com.cmuxterm.app id, this new default makes EXPECTED_APP_ID become 7WLXT3NR37.com.cmux.app. The strict secret-profile validation exits immediately on that mismatch, so the later App Store Connect download/create fallback never runs and the App Store workflow fails before it can mint the new profile.
|
|
||
| TEAM_ID="${IOS_APPSTORE_TEAM_ID:-7WLXT3NR37}" | ||
| BUNDLE_IDENTIFIER="${IOS_APPSTORE_BUNDLE_IDENTIFIER:-com.cmuxterm.app}" | ||
| BUNDLE_IDENTIFIER="${IOS_APPSTORE_BUNDLE_IDENTIFIER:-com.cmux.app}" |
There was a problem hiding this comment.
This default makes EXPECTED_APP_ID resolve to 7WLXT3NR37.com.cmux.app, but the primary IOS_APPSTORE_PROVISIONING_PROFILE_BASE64 path is still validated with strict=true. When that secret still contains the old com.cmuxterm.app profile, validate_profile calls die on the app-id mismatch and the script exits before the App Store Connect download/create fallback can run. The workflow can still fail before it has a chance to mint or install the new com.cmux.app profile.
|
|
||
| TEAM_ID="${IOS_APPSTORE_TEAM_ID:-7WLXT3NR37}" | ||
| BUNDLE_IDENTIFIER="${IOS_APPSTORE_BUNDLE_IDENTIFIER:-com.cmuxterm.app}" | ||
| BUNDLE_IDENTIFIER="${IOS_APPSTORE_BUNDLE_IDENTIFIER:-com.cmux.app}" |
There was a problem hiding this comment.
When IOS_APPSTORE_PROVISIONING_PROFILE_BASE64 still contains a profile for the old com.cmuxterm.app id, this default makes EXPECTED_APP_ID resolve to 7WLXT3NR37.com.cmux.app. The primary secret path is still validated in strict mode later in the script, so that app-id mismatch exits before the non-strict fallback secrets or App Store Connect download/create path can run. The App Store workflow can still fail immediately instead of minting and installing the new com.cmux.app profile.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@tests/test_ios_appstore_lane_identity.py`:
- Around line 243-289: Ensure the App Store lane exercised by
test_upload_appstore_lane_uses_production_bundle_id reaches the export step and
creates ExportOptions.plist at the expected upload directory. Inspect
upload-app-store.sh and its relationship to upload-testflight.sh, preserving
CMUX_FAKE_EXPORT_OPTIONS_COPY in the xcodebuild subprocess environment and
aligning export paths/options so the fake xcodebuild can copy the generated
plist. Verify the wrapper invokes the corrected production bundle-ID flow and
propagates all required environment variables.
- Around line 228-240: Remove the unused tmp parameter from _run and update
every call site to stop passing it, or use tmp explicitly for temporary-file
isolation; ensure the function signature and all invocations remain consistent.
🪄 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: 96d491d3-6d5b-4b7a-abc0-dcbf89a2109f
📒 Files selected for processing (6)
.github/scripts/install-app-store-provisioning-profile.sh.github/workflows/ci.yml.github/workflows/ios-app-store.ymlios/scripts/upload-testflight.shios/scripts/validate-app-store-release.shtests/test_ios_appstore_lane_identity.py
| def _run(args: list[str], *, env: dict[str, str], tmp: Path) -> subprocess.CompletedProcess[str]: | ||
| result = subprocess.run( | ||
| args, | ||
| cwd=ROOT, | ||
| env=env, | ||
| text=True, | ||
| stdout=subprocess.PIPE, | ||
| stderr=subprocess.PIPE, | ||
| ) | ||
| if result.returncode != 0: | ||
| print(result.stdout) | ||
| print(result.stderr, file=sys.stderr) | ||
| return result |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
_run has an unused tmp parameter.
The tmp parameter is passed to _run but never referenced in the function body. Either remove it or use it for temp-file isolation.
♻️ Proposed fix
def _run(args: list[str], *, env: dict[str, str], tmp: Path) -> subprocess.CompletedProcess[str]:
result = subprocess.run(
args,
cwd=ROOT,
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)Either remove tmp:
-def _run(args: list[str], *, env: dict[str, str], tmp: Path) -> subprocess.CompletedProcess[str]:
+def _run(args: list[str], *, env: dict[str, str]) -> subprocess.CompletedProcess[str]:and update call sites, or use it for additional temp-file cleanup.
🧰 Tools
🪛 ast-grep (0.44.1)
[error] 228-235: Use of unsanitized data to create processes
Context: subprocess.run(
args,
cwd=ROOT,
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(os-system-unsanitized-data)
[error] 228-235: Command coming from incoming request
Context: subprocess.run(
args,
cwd=ROOT,
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(subprocess-from-request)
🪛 Ruff (0.15.20)
[warning] 228-228: Unused function argument: tmp
(ARG001)
[warning] 229-236: Prefer capture_output over sending stdout and stderr to PIPE
Replace with capture_output keyword argument
(UP022)
[error] 229-229: subprocess call: check for execution of untrusted input
(S603)
[warning] 229-229: subprocess.run without explicit check argument
Add explicit check=False
(PLW1510)
🤖 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 `@tests/test_ios_appstore_lane_identity.py` around lines 228 - 240, Remove the
unused tmp parameter from _run and update every call site to stop passing it, or
use tmp explicitly for temporary-file isolation; ensure the function signature
and all invocations remain consistent.
Source: Linters/SAST tools
| def test_upload_appstore_lane_uses_production_bundle_id(tmp: Path, fakebin: Path) -> None: | ||
| env = _base_env(tmp, fakebin) | ||
| env["CMUX_IOS_UPLOAD_DIR"] = str(tmp / "upload") | ||
| env["CMUX_BUILD_NUMBER_OUT_FILE"] = str(tmp / "build-number.txt") | ||
| result = _run( | ||
| [ | ||
| "bash", | ||
| str(ROOT / "ios" / "scripts" / "upload-app-store.sh"), | ||
| "--signing", | ||
| "manual", | ||
| "--export-only", | ||
| "--build-number", | ||
| "20260710041750", | ||
| ], | ||
| env=env, | ||
| tmp=tmp, | ||
| ) | ||
| _check(result.returncode == 0, "appstore export-only lane succeeds with fake Apple tools") | ||
| _check("signed IPA bundle identity verified: com.cmux.app" in result.stdout, "signed IPA identity gate passes") | ||
|
|
||
| xcodebuild_calls = [ | ||
| json.loads(line) | ||
| for line in (tmp / "xcodebuild.jsonl").read_text(encoding="utf-8").splitlines() | ||
| ] | ||
| archive_call = next(call for call in xcodebuild_calls if "archive" in call) | ||
| _check( | ||
| f"PRODUCT_BUNDLE_IDENTIFIER={APPSTORE_BUNDLE_ID}" in archive_call, | ||
| "archive command stamps com.cmux.app", | ||
| ) | ||
| _check( | ||
| all("PRODUCT_BUNDLE_IDENTIFIER=com.cmuxterm.app" not in call for call in archive_call), | ||
| "archive command does not stamp the retired com.cmuxterm.app id", | ||
| ) | ||
|
|
||
| export_options = plistlib.loads((tmp / "ExportOptions.plist").read_bytes()) | ||
| profiles = export_options.get("provisioningProfiles", {}) | ||
| _check( | ||
| profiles.get(APPSTORE_BUNDLE_ID) == "cmux App Store Distribution", | ||
| "export options map the App Store profile to com.cmux.app", | ||
| ) | ||
| _check("com.cmuxterm.app" not in profiles, "export options do not include the retired app id") | ||
|
|
||
| ipa_line = next(line for line in result.stdout.splitlines() if line.startswith("IPA_PATH=")) | ||
| ipa_path = Path(ipa_line.removeprefix("IPA_PATH=")) | ||
| with zipfile.ZipFile(ipa_path) as zf: | ||
| info = plistlib.loads(zf.read("Payload/cmux.app/Info.plist")) | ||
| _check(info.get("CFBundleIdentifier") == APPSTORE_BUNDLE_ID, "final signed IPA Info.plist is com.cmux.app") |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical
Pipeline failure: ExportOptions.plist is never created, causing FileNotFoundError at line 277.
The CI workflow-guard-tests job fails because test_upload_appstore_lane_uses_production_bundle_id crashes with FileNotFoundError: '/tmp/.../upload-test/ExportOptions.plist'. The fake xcodebuild copies the export options plist to CMUX_FAKE_EXPORT_OPTIONS_COPY only when it reaches the -exportArchive branch (line 135), but the file is never created — meaning either the upload script fails before the export step, or CMUX_FAKE_EXPORT_OPTIONS_COPY is not available in the xcodebuild subprocess environment.
The test calls upload-app-store.sh (line 250), but the changes under review are in upload-testflight.sh. If upload-app-store.sh is a wrapper, it may not propagate CMUX_FAKE_EXPORT_OPTIONS_COPY or may set up different export paths. The _check assertions are non-raising, so the test continues past failed returncode/stdout checks until it hits the unhandled FileNotFoundError.
🔍 Verification script to investigate the root cause
#!/bin/bash
# Check if upload-app-store.sh exists and its relationship to upload-testflight.sh
ls -la ios/scripts/upload-app-store.sh 2>/dev/null || echo "upload-app-store.sh not found"
file ios/scripts/upload-app-store.sh 2>/dev/null || true
head -30 ios/scripts/upload-app-store.sh 2>/dev/null || true
# Check if it's a symlink
readlink -f ios/scripts/upload-app-store.sh 2>/dev/null || true
# Check if it sources or calls upload-testflight.sh
rg -n 'upload-testflight|upload_app_store|source|exec' ios/scripts/upload-app-store.sh 2>/dev/null || true
# Check how ExportOptions.plist is created in the upload script
rg -n 'ExportOptions|exportOptions|CMUX_FAKE_EXPORT_OPTIONS_COPY' ios/scripts/upload-testflight.sh | head -20🧰 Tools
🪛 GitHub Actions: CI / 12_workflow-guard-tests.txt
[error] 277-277: FileNotFoundError while reading '/tmp/tmprmo5y00n/upload-test/ExportOptions.plist' using plistlib.loads((tmp / 'ExportOptions.plist').read_bytes()). ExportOptions.plist was not created.
🪛 GitHub Actions: CI / workflow-guard-tests
[error] 277-277: Test failed due to missing export options file: FileNotFoundError: No such file or directory: '/tmp/tmprmo5y00n/upload-test/ExportOptions.plist'
🤖 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 `@tests/test_ios_appstore_lane_identity.py` around lines 243 - 289, Ensure the
App Store lane exercised by test_upload_appstore_lane_uses_production_bundle_id
reaches the export step and creates ExportOptions.plist at the expected upload
directory. Inspect upload-app-store.sh and its relationship to
upload-testflight.sh, preserving CMUX_FAKE_EXPORT_OPTIONS_COPY in the xcodebuild
subprocess environment and aligning export paths/options so the fake xcodebuild
can copy the generated plist. Verify the wrapper invokes the corrected
production bundle-ID flow and propagates all required environment variables.
Source: Pipeline failures
Summary
com.cmuxterm.appto the App Store Connect app record bundle idcom.cmux.app.Testing
python3 -m py_compile tests/test_ios_appstore_lane_identity.py && python3 tests/test_ios_appstore_lane_identity.pypython3 tests/test_ios_testflight_external_distribution.py && python3 tests/test_ios_testflight_notes.pypython3 tests/test_ci_change_areas.py./tests/test_ci_release_sdk_lane.sh && ./tests/test_ci_self_hosted_guard.sh && ./tests/test_ci_scheme_testaction_debug.shgit ls-files 'tests/*.py' 'tests_v2/*.py' 'scripts/*.py' | xargs python3 -m py_compile(existing warning intests/test_issue_1138_sidebar_pr_polling.py)Issues
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Note
Medium Risk
Changes production iOS signing/upload identity and release gates; mistakes block uploads but misconfigured ASC secrets or profiles could still delay releases until operators update credentials for
com.cmux.app.Overview
Switches the production App Store lane from
com.cmuxterm.apptocom.cmux.appeverywhere defaults, docs, CI summaries, and provisioning-profile install logic apply, addressing App Store Connect ITMS-90055 when the ASC app record does not match the uploaded binary.Fail-closed identity checks are added on the upload path: archives must match the lane bundle id; signed IPAs are checked for
CFBundleIdentifier, embedded profileapplication-identifier, and signed entitlements; App Store uploads require a numericASC_APP_IDand verify ASCbundleIdmatches the lane beforeasc builds upload. Release validation no longer treats a bundle id as--app.APNs accepts
com.cmux.appas a production topic while keepingcom.cmuxterm.appfor existing tokens. CI runstests/test_ios_appstore_lane_identity.py(fake Apple tooling) against the real upload, profile-install, and validation entrypoints.Reviewed by Cursor Bugbot for commit aca4acd. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Switch iOS App Store production bundle id from
com.cmuxterm.apptocom.cmux.app, add strict identity gates for archives/signed IPAs/ASC uploads, and handle stale provisioning profile secrets. Resolves App Store Connect ITMS-90055 for Apple ID 6783338052, version 1.0.4.com.cmux.appacross upload/validation/profile-install scripts, CI summary, and docs; default profile naming to “cmux App Store CI …”.CFBundleIdentifier, embedded profileapplication-identifier, or signed entitlements mismatch; hard-check push and Apple Sign In entitlements; allow overridingPlistBuddyvia$PLISTBUDDY.ASC_APP_ID, read the app record viaasc apps view, and refuse upload if its bundle id differs from the lane.com.cmux.appas production while keepingcom.cmuxterm.appallowlisted; update tests and schema comment.Written for commit ca24ba7. Summary will update on new commits.
Summary by CodeRabbit
com.cmux.appacross lanes, docs, and checklists.CFBundleIdentifiermatches the expected production bundle id.com.cmux.appas production for APNs routing.com.cmux.app.