ci: automate stable promotion releases#777
Conversation
WalkthroughThe PR adds managed beta-to-stable promotion with strict merge, version, tag, and commit verification. Release workflows consume immutable release commits, publish downstream artifacts, manage post-stable back-merges, and document the revised process. ChangesManaged release promotion
Estimated code review effort: 5 (Critical) | ~100 minutes Sequence Diagram(s)sequenceDiagram
participant BetaRelease
participant PromotionWorkflow
participant StableRelease
participant PublishWorkflow
participant TestFlight
BetaRelease->>PromotionWorkflow: provide beta tag and release commit
PromotionWorkflow->>StableRelease: create and validate stable Release PR
StableRelease->>PromotionWorkflow: return verified stable commit and tag
PromotionWorkflow->>PublishWorkflow: pass immutable release commit
PromotionWorkflow->>TestFlight: pass immutable release commit
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@docs/macos-distribution.md`:
- Around line 214-220: Reconcile the manifest-only guidance in the release
documentation with the manual fallback instructions: either narrow the rule near
the “Release-As” warning to apply only to the normal release-please flow, or
change the fallback to use the appropriate manifest files instead of directly
editing apps/desktop/package.json. Ensure the documented procedures clearly
distinguish normal releases from the manual fallback.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a8c9fcda-9cee-438c-b5d7-67a45d03b934
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
.github/workflows/release-please.yml.github/workflows/release.yml.github/workflows/testflight.ymlCONTRIBUTING.mdapps/desktop/scripts/release-promotion.mjsapps/desktop/scripts/release-promotion.test.mjsdocs/macos-distribution.mdpackage.json
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort 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 4177c2e. Configure here.

Problem
Beta releases already have a maintained release-please PR and automatic macOS/GitHub + TestFlight delivery, but graduating a published beta to stable is still a multi-step manual sequence: open a
next→masterPR, merge it, wait for a second stable Release PR, merge that too, and then deploy. The movingnextbranch also makes it easy for a promotion review to drift beyond the beta that was actually tested and shipped.Major graduation is especially fragile: conventional commits cannot intentionally express the product decision that
1.0.0-beta.Nmust become exactly1.0.0.Before → After
nextmasterhotfix releases stay manualChanges
Maintain an exact beta promotion snapshot
release-promotion.mjs, which creates or safely advancesrelease-promotion/latest-betaonly after both beta delivery workflows succeed.Make the promotion merge the single stable approval
release-asvalue, including major releases such as1.0.0-beta.4→1.0.0.Harden races and retries
masterevents, quarantines a generated Release PR if its base changes inside GitHub’s merge operation, and resumes safely before/after the metadata merge or draft creation.Pin every delivery job to the released commit
Tests
release-promotion.test.mjscovers beta/commit validation, forward-only branch movement, exact PR metadata, explicit repository scoping, and fork/cross-repository collisions.--release-as 1.0.0producedchore(next): release 1.0.0and the expected manifest/changelog/version updates.Verification
pnpm --filter @reflect/desktop exec vitest run scripts/release-promotion.test.mjs scripts/release-macos.test.mjs scripts/release-ios.test.mjs— 66 tests passedactionlint .github/workflows/*.yml— passedpnpm check— passed (two pre-existing max-lines warnings)pnpm build— passed (expected local Sentry-token and chunk-size warnings)git diff --check— passedpnpm install --frozen-lockfile --ignore-scripts --filter @reflect/monorepo— passed; pinned CLI reports 17.6.0Risk / Rollout
This changes release control flow, so the workflow fails closed whenever GitHub state does not match the reviewed beta snapshot. Merging this PR does not ship stable by itself: it activates on
next, and the promotion PR appears only after a subsequent beta finishes both delivery channels. Stable deployment starts only when that promotion PR is merge-committed. Leaving or closing the promotion PR remains a safe stop mechanism.A direct hotfix on
masterstill requires its stable Release PR to be merged manually. Failed stable metadata/tag creation can be rerun from the original promotion workflow; failed macOS or TestFlight delivery can use the existing manual retry workflows against the stable commit.Note
High Risk
Large changes to release control flow, branch history, and automated merges on
master; mistakes could ship wrong versions or block releases, though the workflow is designed to fail closed on state mismatches.Overview
Stable promotion no longer relies on a hand-opened
next→masterPR plus a second manual stable Release PR. After macOS and TestFlight both succeed for a beta, a new job runsrelease-promotion.mjsto maintainrelease-promotion/latest-betaat the published beta commit and open/update one promotion PR. Merging that PR with a merge commit is the only human approval;release-please.ymlthen validates the merge against the beta tag, creates the stable Release PR with a pinnedrelease-as(via lockfilerelease-please17.6.0), squash-merges it mechanically, and runs release-please again to tag and draft the stable release.Delivery reusable workflows (
release.yml,testflight.yml) gain a requiredcommitinput so macOS, TestFlight, beta-feed sync, promotion, and post-stable back-merge all checkout the same immutable SHA resolved once from the tag, not the caller event SHA.Resilience includes promotion detection, stale-
masterguards, idempotent promotion/back-merge PR handling, andrelease_availableso post-stable jobs can recover when a release is already published.Docs (
macos-distribution.md,CONTRIBUTING.md) describe the single-approval flow and major-version promotion (v1.0.0-beta.N→v1.0.0). Directmasterhotfixes still merge stable Release PRs manually.Reviewed by Cursor Bugbot for commit 338f2d5. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation