Skip to content

Upload cmux INTERNAL for every main push - #8694

Merged
azooz2003-bit merged 4 commits into
mainfrom
feat-ios-internal-every-main-push
Jul 23, 2026
Merged

azooz2003-bit merged 4 commits into
mainfrom
feat-ios-internal-every-main-push

Conversation

@azooz2003-bit

@azooz2003-bit azooz2003-bit commented Jul 22, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

  • Trigger the cmux INTERNAL TestFlight workflow immediately for every push to main, without path or SHA skip gates.
  • Preserve every pushed SHA with per-run concurrency, then order uploads so App Store Connect build numbers stay monotonic.
  • Fix the current Release archive failure by making ToastCenter available outside DEBUG builds.
  • Keep manual dispatch as an operator escape hatch.

This deliberately replaces the batching policy from #6214 because the required contract is now one internal build per main change.

Verification

  • python3 tests/test_ios_testflight_every_main_push.py
  • go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.7 .github/workflows/ios-testflight.yml
  • Exact unsigned cmux INTERNAL Release archive: passed
  • Tagged macOS cloud build tfmain: passed
  • Tagged iOS build, install, and launch on isolated cmux-tfmain-codex simulator: passed

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


Summary by cubic

Ships a cmux INTERNAL TestFlight build for every push to main, and queues uploads so build numbers stay monotonic. Also removes the old schedule lane and fixes a Release archive failure.

  • New Features

    • Trigger on every main push; schedule and path gates removed.
    • Per-SHA concurrency with a pre-upload wait so earlier runs finish “Upload to TestFlight” first; manual runs key off run_id.
    • Manual workflow_dispatch stays, with optional build number and marketing version overrides.
    • Automatic internal identity: bundle ID dev.cmux.app.internal, display name “cmux INTERNAL”, and internal group assignment.
    • Removed the assign-only retry flow and assignment-state artifacts; assignment runs only after a successful upload.
  • Bug Fixes

    • Make ToastCenter available in Release builds to fix the Release archive failure.

Written for commit 6dc0ad1. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • iOS TestFlight builds now trigger automatically on every push to main (manual builds still available).
    • Main-push uploads are coordinated to keep build numbering sequential and notes derived from the latest successful upload.
    • Internal TestFlight assignment now runs after a successful upload.
  • Tests

    • Added an automated workflow validation test to confirm triggers, concurrency/cancellation behavior, upload ordering, and internal lane settings.
  • Documentation

    • Updated upload-script guidance to match the new “every-main-push” beta behavior.
  • Refactor

    • Tidied the mobile settings view’s toast dependency declaration order.

@cursor

cursor Bot commented Jul 22, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The iOS TestFlight workflow now uploads builds for every main push, preserves upload ordering, and assigns the internal group after successful uploads. Workflow tests validate these rules, comments are updated, and a mobile settings environment property is reordered.

Changes

Every-main-push TestFlight lane

Layer / File(s) Summary
Trigger and upload ordering
.github/workflows/ios-testflight.yml
The workflow runs on main pushes and manual dispatches, uses SHA- or run-based concurrency, waits for earlier uploads, and enables builds for these runs.
Internal assignment wiring
.github/workflows/ios-testflight.yml, ios/scripts/upload-testflight.sh
Successful uploads pass the final build number directly to internal assignment, with build-number, lane identity, marketing-version, and assignment comments updated.
Workflow contract tests
tests/test_ios_testflight_every_main_push.py
Tests validate triggers, ordering controls, removed assignment-state markers, and CMUX internal bundle settings.

Settings property ordering

Layer / File(s) Summary
Toast environment declaration
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileSettingsView.swift
The ToastCenter environment property is moved earlier without changing its uses.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant decide
  participant upload
  participant TestFlight
  GitHubActions->>decide: Start on main push or manual dispatch
  decide->>GitHubActions: Wait for earlier main uploads
  decide->>upload: Enable build with notes base SHA
  upload->>TestFlight: Upload CMUX internal build
  GitHubActions->>TestFlight: Assign uploaded build to internal group
Loading

Possibly related PRs

Suggested reviewers: lawrencecchen

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
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 Only Swift production change is a SwiftUI view property reorder; no new actor-isolation issue, and ToastCenter is already @MainActor.
Cmux Swift Blocking Runtime ✅ Passed Only change in the touched Swift file is moving @Environment(ToastCenter.self); no waits, sleeps, locks, syncs, or polling were introduced.
Cmux Browser Automation Off-Main ✅ Passed PASS: The PR only changes an iOS workflow, a Swift view, a shell script, and a test; it does not touch browser automation routing or WebKit wait code.
Cmux Expensive Synchronous Load ✅ Passed MobileSettingsView only reorders @Environment(ToastCenter.self); no expensive history loader or main-actor sync parsing was added or moved.
Cmux Cache Substitution Correctness ✅ Passed PASS: The only production Swift delta moves a ToastCenter environment declaration; no cache/opportunistic read replaced an authoritative persistence/history/undo/snapshot read.
Cmux No Hacky Sleeps ✅ Passed The only new wait/polling is in GitHub Actions YAML, which the rule explicitly exempts; the shell script changes are comment-only.
Cmux Algorithmic Complexity ✅ Passed No production hot-path complexity regression: Swift change is property reordering, shell changes are comments, and the new workflow JS loops are bounded CI orchestration, not user-data scans.
Cmux Swift Concurrency ✅ Passed Branch diff only reorders MobileSettingsView's ToastCenter environment property; no new DispatchQueue, Combine, completion-handler, or fire-and-forget Task patterns appear.
Cmux Swift @Concurrent ✅ Passed Only Swift diff is moving @Environment(ToastCenter.self); no async, @concurrent, or actor-isolation changes were introduced.
Cmux Swift Package Boundaries ✅ Passed PASS: The only Swift change is a UI-only @Environment reorder inside CmuxMobileShellUI; no independent feature logic was added to the app target.
Cmux Swiftpm Lockfiles ✅ Passed PR only changes a workflow, Swift file, script, and test; no .gitignore, Package.resolved, or Xcode package-reference files are touched.
Cmux Swift Logging ✅ Passed PASS: The diff only touches a GitHub Actions workflow and a Python test; no production Swift code or new logging statements are added or modified.
Cmux User-Facing Error Privacy ✅ Passed The diff only changes workflow internals and a test; no user-facing error/alert/copy was added or exposed.
Cmux Full Internationalization ✅ Passed Only a Swift environment-property reorder plus workflow/script comments and tests changed; no new user-facing strings or locale/catalog edits were introduced.
Cmux Swiftui State Layout ✅ Passed No new ObservableObject/@Published/GeometryReader/lazy-row store pattern was introduced; added @State/Task usage is event-driven, and MobileSettingsView only moved an existing @Bindable binding.
Cmux Architecture Rethink ✅ Passed The only Swift change is moving ToastCenter out of a DEBUG-only declaration; the app root already owns/injects it, and no timing/observer/side-channel repair was added.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR only reorders a ToastCenter @Environment in MobileSettingsView; it doesn’t add or change any NSWindow/WindowGroup/NSPanel code or cmuxAuxiliaryWindowIdentifiers.
Cmux Source Artifacts ✅ Passed Changed paths are workflow/source/script/test files, not generated artifacts, caches, scratch dirs, or build outputs.
Cmux No Test Or Debug Seam In Production Source ✅ Passed MobileSettingsView only moves an existing ToastCenter dependency out of #if DEBUG to fix release behavior; no test/debug seam or accessor was added.
Cmux No Ambient Global State ✅ Passed The only Swift change moves an @Environment injection within MobileSettingsView; no new top-level func, global var, namespace, or singleton was added.
Title check ✅ Passed The title clearly and concisely summarizes the main change: running the cmux INTERNAL workflow on every main push.
Description check ✅ Passed The description covers the summary and verification well, but it omits the demo video, review trigger, and checklist sections from the template.
✨ 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-ios-internal-every-main-push

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.

@greptile-apps

greptile-apps Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR shifts the iOS TestFlight lane from a scheduled (every-2h) batch model to a per-push model: every commit to main now triggers its own cmux INTERNAL build, uploads are serialized by a new ordering loop in the decide job, and the Release archive failure is fixed by moving @Environment(ToastCenter.self) private var toasts outside the #if DEBUG block in MobileSettingsView.swift.

  • Workflow trigger & concurrency: Replaces the schedule: trigger and path-gate with a bare push: branches: [main]. Per-SHA concurrency groups preserve every commit; manual (workflow_dispatch) runs key off run_id so operators can always rebuild any SHA.
  • Upload ordering: The decide job now polls GitHub Actions API (up to 300 × 60 s = 5 h) to wait for all earlier push/dispatch runs to finish their "Upload to TestFlight" job before proceeding, ensuring monotonic App Store Connect build numbers.
  • Swift fix: @Environment(ToastCenter.self) private var toasts was inside the #if DEBUG block alongside the demo/gallery state vars, but its production caller (the "Toasts" beta-feature toggle at line 206) is outside that guard. Moving it to the top-level @Environment section resolves the Release-mode compile failure.

Confidence Score: 5/5

Safe to merge — the workflow logic is internally consistent, the ordering mechanism correctly serializes uploads by polling job status rather than relying on wall-clock guesses, and the Swift fix addresses a real compile failure without introducing any new debug seams or ambient global state.

The Swift change is a one-line relocation that resolves a confirmed Release-mode compile error (the property had a production caller outside its #if DEBUG guard). The workflow rewrite is well-documented, the ordering loop correctly keys on run ID comparison and job-status polling rather than wall-clock assumptions, and the tests pin the critical invariants. No correctness bugs were found.

No files require special attention.

Important Files Changed

Filename Overview
.github/workflows/ios-testflight.yml Rewrites the upload trigger from schedule+path-gate to push-per-SHA with a GitHub-API-based ordering loop in the decide job; logic is sound and well-commented.
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileSettingsView.swift Moves @Environment(ToastCenter.self) out of the #if DEBUG block to fix the Release archive failure; production caller (Toasts beta-feature toggle) now compiles in Release.
ios/scripts/upload-testflight.sh Comment-only edits updating 'every-2h' references to 'every-main-push'; no logic changes.
tests/test_ios_testflight_every_main_push.py New regression tests asserting trigger shape, per-SHA concurrency, ordering primitives, and internal bundle/display identity are present in the workflow YAML.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub (push to main)
    participant D as decide job(Linux, up to 6h)
    participant API as GitHub Actions API
    participant U as upload job(macOS)
    participant ASC as App Store Connect
    participant A as assign-internal-group job

    GH->>D: trigger (per-SHA concurrency group)
    loop every 60s until no earlier upload is blocking
        D->>API: listWorkflowRuns(branch:main, per_page:100)
        API-->>D: earlier active runs
        D->>API: listJobsForWorkflowRun(run_id)
        API-->>D: job status for Upload to TestFlight
    end
    D->>D: resolve last_uploaded_sha (notes base)
    D-->>U: "should_build=true, last_uploaded_sha"
    U->>U: xcodebuild archive + export
    U->>ASC: upload IPA (monotonic timestamp build number)
    ASC-->>U: build accepted
    U-->>A: final_build_number
    A->>ASC: assign build to internal TestFlight group
Loading

Reviews (3): Last reviewed commit: "Remove stale TestFlight schedule event" | Re-trigger Greptile

Comment thread .github/workflows/ios-testflight.yml Outdated
Comment on lines +68 to +69
run.status !== 'completed' &&
['push', 'workflow_dispatch', 'schedule'].includes(run.event)

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.

P2 The 'schedule' event in this filter is a leftover from the removed cron trigger. Since the workflow no longer has a schedule: trigger, no run will ever have run.event === 'schedule', so the entry is dead code. Removing it keeps the filter aligned with the actual trigger surface.

Suggested change
run.status !== 'completed' &&
['push', 'workflow_dispatch', 'schedule'].includes(run.event)
run.status !== 'completed' &&
['push', 'workflow_dispatch'].includes(run.event)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ios-testflight.yml (1)

113-123: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Stale downstream comment/dead artifact once assign-only retry logic is removed here.

This range confirms the decide job's assignment-retry variables/outputs are removed. Further down in this same file, the assign-internal-group job still has a comment ("the decide job's assign-only retry logic looks for this exact artifact name") and an "Upload assignment-state artifact" step producing ios-testflight-assignment-state-complete — but nothing in the redesigned decide job reads that artifact anymore, and the 492-496 change confirms BUILD_NUMBER now flows directly from needs.upload.outputs.final_build_number rather than an artifact restore path. That comment and artifact upload appear vestigial now and should be updated or removed to avoid misleading future maintainers.

🤖 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/ios-testflight.yml around lines 113 - 123, Remove the
obsolete assignment-state artifact upload from the assign-internal-group job,
along with the comment claiming the decide job consumes it. Keep BUILD_NUMBER
sourced directly from needs.upload.outputs.final_build_number and remove only
the now-unused artifact-related logic.
🤖 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/ios-testflight.yml:
- Around line 42-100: Wrap the earlier-run polling logic in the decide step
around the workflow and job API calls with transient-error handling, following
the graceful behavior used by the “resolve last uploaded sha” loop. On API
failure, log the error and continue waiting/retrying rather than allowing the
exception to fail the job; preserve the existing timeout failure and ensure
control reaches should_build output for a successful decision.

---

Outside diff comments:
In @.github/workflows/ios-testflight.yml:
- Around line 113-123: Remove the obsolete assignment-state artifact upload from
the assign-internal-group job, along with the comment claiming the decide job
consumes it. Keep BUILD_NUMBER sourced directly from
needs.upload.outputs.final_build_number and remove only the now-unused
artifact-related logic.
🪄 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: 12f6cf66-f341-4f04-9835-0533f4f651d3

📥 Commits

Reviewing files that changed from the base of the PR and between 70224d2 and 4413402.

📒 Files selected for processing (4)
  • .github/workflows/ios-testflight.yml
  • Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/MobileSettingsView.swift
  • ios/scripts/upload-testflight.sh
  • tests/test_ios_testflight_every_main_push.py

Comment thread .github/workflows/ios-testflight.yml
@cursor

cursor Bot commented Jul 23, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@cursor

cursor Bot commented Jul 23, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/ios-testflight.yml (2)

60-66: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Paginate the workflow-run query before proceeding.

listWorkflowRuns(..., per_page: 100) inspects only the first page. With more than 100 queued/newer runs, an older active upload can be omitted, allowing concurrent App Store Connect uploads and violating monotonic build-number ordering. Continue paging until all earlier runs are covered, or use an authoritative upload queue.

As per coding guidelines, identify scalable collections and avoid incomplete full scans.

🤖 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/ios-testflight.yml around lines 60 - 66, Update the
workflow-run lookup using github.rest.actions.listWorkflowRuns so it paginates
through all pages rather than inspecting only per_page: 100 results. Use the
complete run collection when determining earlier active uploads, preserving the
existing filtering and ordering behavior before proceeding.

Source: Coding guidelines


57-86: 🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Avoid the per-run API fan-out on every polling pass.

For R queued runs, each 60-second iteration performs roughly 1 + R API calls, and across R waiting workflow runs this becomes O(R²) calls. Merge bursts can exhaust GitHub API limits and cause the entire upload lane to fail. Prefer a durable predecessor/queue mechanism; otherwise bound the cohort and avoid querying each run’s jobs independently.

As per coding guidelines, avoid repeated full scans and per-item nested scans over scalable collections.

🤖 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/ios-testflight.yml around lines 57 - 86, Replace the
per-poll `listJobsForWorkflowRun` fan-out inside the `earlierActiveRuns` loop
with a durable predecessor or queue-based mechanism that identifies the
immediate blocking run without scanning every predecessor’s jobs. If that
mechanism is unavailable, bound the candidate cohort and reuse a single bounded
workflow/job query rather than issuing one jobs request per run on every polling
iteration; preserve the existing ordering and blocking behavior.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In @.github/workflows/ios-testflight.yml:
- Around line 60-66: Update the workflow-run lookup using
github.rest.actions.listWorkflowRuns so it paginates through all pages rather
than inspecting only per_page: 100 results. Use the complete run collection when
determining earlier active uploads, preserving the existing filtering and
ordering behavior before proceeding.
- Around line 57-86: Replace the per-poll `listJobsForWorkflowRun` fan-out
inside the `earlierActiveRuns` loop with a durable predecessor or queue-based
mechanism that identifies the immediate blocking run without scanning every
predecessor’s jobs. If that mechanism is unavailable, bound the candidate cohort
and reuse a single bounded workflow/job query rather than issuing one jobs
request per run on every polling iteration; preserve the existing ordering and
blocking behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1311135b-deaf-409c-b596-867b711a417e

📥 Commits

Reviewing files that changed from the base of the PR and between 7577045 and 6dc0ad1.

📒 Files selected for processing (2)
  • .github/workflows/ios-testflight.yml
  • tests/test_ios_testflight_every_main_push.py

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