Skip to content

ci: automate stable promotion releases#777

Merged
maccman merged 3 commits into
nextfrom
codex/automate-stable-promotion
Jul 13, 2026
Merged

ci: automate stable promotion releases#777
maccman merged 3 commits into
nextfrom
codex/automate-stable-promotion

Conversation

@maccman

@maccman maccman commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

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 nextmaster PR, merge it, wait for a second stable Release PR, merge that too, and then deploy. The moving next branch 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.N must become exactly 1.0.0.

Before → After

Before After
Promotion candidate Human opens a PR from moving next One bot-maintained PR points at the latest fully published beta commit
Stable approval Merge promotion PR, then merge stable Release PR Merge the promotion PR once; generated release metadata merges mechanically
Stable version Inferred again from conventional commits Pinned to the stable base of the approved beta tag
Build source Reusable workflows may inherit the caller SHA or resolve a tag independently Every delivery path checks out one verified immutable release commit
Hotfixes Manual stable Release PR Unchanged; direct master hotfix releases stay manual

Changes

  1. Maintain an exact beta promotion snapshot

    • Adds release-promotion.mjs, which creates or safely advances release-promotion/latest-beta only after both beta delivery workflows succeed.
    • Reuses one open PR, rejects divergent/rewound branch updates, ignores same-named fork PRs, and verifies the beta tag still resolves to the release commit.
  2. Make the promotion merge the single stable approval

    • Recognizes only a merge-commit promotion whose second parent, tree, ancestry, tag, and published prerelease record all match the approved beta.
    • Uses the lockfile-pinned release-please 17.6.0 CLI to create the stable Release PR with an exact release-as value, including major releases such as 1.0.0-beta.41.0.0.
    • Allow-lists and validates the generated manifest, changelog, and package-version changes before squash-merging that metadata PR and creating the stable draft/tag in the same workflow.
  3. Harden races and retries

    • Serializes branch release maintenance, rejects stale master events, 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.
    • Makes the post-stable back-merge idempotent and keeps direct stable hotfix Release PRs human-controlled.
  4. Pin every delivery job to the released commit

    • Resolves the lightweight tag once, verifies it against the GitHub release target, and passes that SHA to macOS, TestFlight, beta-feed sync, promotion, and back-merge checkouts.
    • Updates the distribution and contribution guides with the single-approval flow and major-version behavior.

Tests

  • release-promotion.test.mjs covers beta/commit validation, forward-only branch movement, exact PR metadata, explicit repository scoping, and fork/cross-repository collisions.
  • Existing macOS and iOS release-helper suites continue to cover signing/publishing and TestFlight behavior.
  • A release-please dry run with --release-as 1.0.0 produced chore(next): release 1.0.0 and 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 passed
  • actionlint .github/workflows/*.yml — passed
  • pnpm check — passed (two pre-existing max-lines warnings)
  • pnpm build — passed (expected local Sentry-token and chunk-size warnings)
  • git diff --check — passed
  • pnpm install --frozen-lockfile --ignore-scripts --filter @reflect/monorepo — passed; pinned CLI reports 17.6.0

Risk / 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 master still 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 nextmaster PR plus a second manual stable Release PR. After macOS and TestFlight both succeed for a beta, a new job runs release-promotion.mjs to maintain release-promotion/latest-beta at the published beta commit and open/update one promotion PR. Merging that PR with a merge commit is the only human approval; release-please.yml then validates the merge against the beta tag, creates the stable Release PR with a pinned release-as (via lockfile release-please 17.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 required commit input 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-master guards, idempotent promotion/back-merge PR handling, and release_available so 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.Nv1.0.0). Direct master hotfixes 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

    • Added a managed beta-to-stable promotion process with automated validation and controlled stable release preparation.
    • Release builds, macOS packages, and TestFlight uploads now use the verified release commit.
  • Bug Fixes

    • Improved safeguards against incorrect, outdated, divergent, or prematurely tagged releases.
    • Added safer handling for optional release and upload settings.
  • Documentation

    • Clarified beta, stable, hotfix, promotion, rerun, and back-merge procedures.
    • Updated contribution guidance for release markers and version promotion.

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

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

Changes

Managed release promotion

Layer / File(s) Summary
Promotion branch and PR tooling
apps/desktop/scripts/release-promotion.mjs, apps/desktop/scripts/release-promotion.test.mjs, package.json
Adds validated beta-tag and commit handling, rolling promotion branch updates, managed PR orchestration, and Vitest coverage.
Verified stable promotion workflow
.github/workflows/release-please.yml
Detects valid promotion merges, creates and verifies the stable Release PR, performs the squash merge, reconciles two release-please passes, and updates promotion/back-merge jobs.
Immutable release consumers and back-merge
.github/workflows/release.yml, .github/workflows/testflight.yml, .github/workflows/release-please.yml
Passes verified release commits into publishing and TestFlight workflows, uses immutable checkouts, and validates post-stable branches against tag commits.
Release process documentation and guidance
CONTRIBUTING.md, docs/macos-distribution.md
Documents stable promotion sequencing, immutable release boundaries, recovery behavior, and release-as rules.

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
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: automating stable release promotions from beta releases.
✨ 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 codex/automate-stable-promotion

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

@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

🤖 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

📥 Commits

Reviewing files that changed from the base of the PR and between fa0095a and dbb8357.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • .github/workflows/release-please.yml
  • .github/workflows/release.yml
  • .github/workflows/testflight.yml
  • CONTRIBUTING.md
  • apps/desktop/scripts/release-promotion.mjs
  • apps/desktop/scripts/release-promotion.test.mjs
  • docs/macos-distribution.md
  • package.json

Comment thread docs/macos-distribution.md Outdated

@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 using default effort 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 4177c2e. Configure here.

Comment thread .github/workflows/release-please.yml
@maccman maccman merged commit cae14ed into next Jul 13, 2026
9 checks passed
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