chore(release): collapse release flow to single human action#377
Conversation
Per the plan in #373: trigger release.yml on push to main instead of tag push, collapse three jobs into two, and make "merge the version PR" the only human step. release.yml: - Trigger flips from `push: tags: v*` to `push: branches: [main]`. - Two jobs (`prepare` / `publish-and-tag`) preserve least-privilege: prepare holds contents+pull-requests (no OIDC); publish-and-tag holds contents+id-token (no PR-write). - prepare uses `persist-credentials: false` paired with `commitMode: github-api` so changesets/action commits the version bump via the GitHub API (verified, github-actions[bot]-authored). - publish-and-tag is gated on `hasChangesets == 'false'` so feature PRs with changesets do not trigger the publish path; their changesets accumulate in the version PR. - Build + test run inside publish-and-tag, gated by the outer job condition, so no-changeset paths skip them. - Publish uses changesets/action's publish input so its `published` output is the authoritative "did anything go to npm" signal, closing the trap where every doc-only / plan-only main push would otherwise hit `gh release create` for an existing tag. - Per-package git tags suppressed via `--no-git-tag`; the repo-wide `vX.Y.Z` is the single tag. - `gh release create` creates tag ref + release in one API call. - Concurrency block (`workflow-ref`) serializes against itself. - Prerelease detection anchored on canonical suffixes (`*-alpha`, `*-alpha.*`, etc.) — no false positives like `1.0.0-alphabet`. AGENTS.md: - § Release Process rewritten around the single-action flow. Drops force-push tag instructions. - Workflows table row for release.yml updated (trigger column: "Push to main"). Adversarial review: fresh subagent against this branch's diff flagged 11 issues. Critical (gating logic conflated "no pending changesets" with "version PR just merged") fixed by switching to `changesets/action`'s `published` output. Plan-file reference in workflow header dropped (plan lives on PR #373, not yet on main). AGENTS.md "re-runs" wording sharpened. Remaining items accepted as documented tradeoffs (concurrency latency on doc pushes, broader credential scope on publish-and-tag, fetch-depth default). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughExtracts build/test into a reusable GitHub Actions workflow, updates main-build to call it, reworks release.yml to run on pushes to main and drive publishing via Changesets (gating build/test and publish/tag steps), and updates AGENTS.md to document the new main-branch release flow. ChangesRelease & CI workflow refactor (single DAG)
Sequence DiagramsequenceDiagram
actor Maintainer
participant Git as Repository (main)
participant GH as GitHub Actions
participant Prepare as changesets prepare
participant BuildTest as build-and-test
participant Publish as publish-and-tag
participant Registry as Package Registry
participant Release as GitHub Release
Maintainer->>Git: Merge Changesets version PR
Git->>GH: Trigger release.yml (push to main)
GH->>Prepare: Run changesets/action (prepare)
Prepare-->>GH: Emit should_publish=true
GH->>BuildTest: Invoke reusable build-and-test (when should_publish)
BuildTest->>BuildTest: Install, build, run tests
BuildTest-->>GH: Upload artifact (tko)
GH->>Publish: Run publish-and-tag (when should_publish)
Publish->>Publish: Determine VERSION (tools/release-version.cjs)
Publish->>Registry: changesets publish (publish path)
Registry-->>Publish: published=true
Publish->>Release: Create tag & GitHub Release
Release-->>Git: Push vX.Y.Z tag
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78d156f9e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| id: publish | ||
| uses: changesets/action@v1 | ||
| with: | ||
| publish: npx changeset publish --no-git-tag |
There was a problem hiding this comment.
Don’t rely on
published when using --no-git-tag
This workflow now uses changesets/action output steps.publish.outputs.published to decide whether to create the repo-wide vX.Y.Z release tag, but the publish command is changeset publish --no-git-tag. In changesets/action, published is inferred from New tag: lines in publish stdout, and those lines are only emitted when git tags are created; with --no-git-tag, npm publish can succeed while published remains false. On version-PR merge commits, that causes the Create GitHub release step to be skipped, so packages can be published without creating the expected repo tag/release.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR rewrites the release pipeline around the standard Changesets “push to main” flow so that merging the version PR becomes the only manual release step, and updates contributor/maintainer docs to match.
Changes:
- Switches
release.ymlfrom a tag-push trigger topushonmain, collapsing the flow intoprepareandpublish-and-tag. - Moves publish, repo tag creation, and GitHub release creation into the post-version-PR-merge path, using
changesets/actionoutputs to avoid spurious releases on no-op publishes. - Rewrites
AGENTS.mdrelease guidance and workflow table to document the new single-action release process.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
AGENTS.md |
Updates maintainer release documentation and the workflow trigger/purpose table. |
.github/workflows/release.yml |
Replaces the tag-driven release workflow with a push-to-main Changesets flow and combines publish/tag/release logic into one job. |
| # Build is gated on the publish path so doc-only / plan-only main | ||
| # pushes do not pay the cost. | ||
| - name: Build all packages | ||
| run: bun run build | ||
|
|
||
| # Tests run before publish so a regression caught only in the | ||
| # browser matrix cannot ship to npm. main-build.yml runs in | ||
| # parallel and is not a gate; this is the gate. |
| # Build is gated on the publish path so doc-only / plan-only main | ||
| # pushes do not pay the cost. |
| # - publish-and-tag: when no pending changesets remain (i.e. the | ||
| # version PR was just merged), build, test, npm-publish via OIDC, | ||
| # then create the repo-wide vX.Y.Z tag + GitHub release in one | ||
| # gh release create call. |
| id: changesets | ||
| uses: changesets/action@v1 | ||
| with: | ||
| version: npx changeset version |
| id: publish | ||
| uses: changesets/action@v1 | ||
| with: | ||
| publish: npx changeset publish --no-git-tag |
| # Tests run before publish so a regression caught only in the | ||
| # browser matrix cannot ship to npm. main-build.yml runs in | ||
| # parallel and is not a gate; this is the gate. | ||
| - name: Run tests |
| # persist-credentials defaults to true — required for the | ||
| # post-publish tag/release step to authenticate. |
Replace the in-job `bun run test` (which only ran headless because publish-and-tag's runner has no Playwright browsers) with a real browser-matrix gate. - New `.github/workflows/build-and-test.yml` reusable workflow (`on: workflow_call`) holds the Playwright-container build + browser matrix + cli-happy-dom + ESM-extension verification + artifact upload. - `main-build.yml` becomes a thin shim that calls the reusable on push to main / workflow_dispatch. - `release.yml` adds a `build-and-test` job that calls the same reusable, gated on `should_publish == 'true'`. `publish-and-tag` now `needs: [prepare, build-and-test]`, so a red browser matrix blocks publish through standard GitHub Actions semantics — no polling. Both callers pass `secrets: inherit` so future secret-using steps in the reusable don't silently get nothing. main-build.yml gains a concurrency block matching release.yml's so two rapid main pushes cannot run two parallel Playwright runners against the same SHA. publish-and-tag still runs `bun run build` locally to produce the publishable dists in its worktree. The reusable is the gate, not an artifact source. Adversarial review: fresh subagent against this diff flagged 10 items. Two actionable (secrets: inherit, main-build concurrency) — fixed. Remaining items accepted as documented tradeoffs (double build per push, cosmetic name on shim). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
AGENTS.md (1)
133-159:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate the
main-build.ymlrow while you're here.This PR makes
main-build.ymla wrapper over the reusable browser-matrix workflow, but the CI/CD table still describes it asBuild + audit + headless test. Tightening that row now will keep the docs aligned with the workflow change described below.Based on learnings: If PR replaces a test runner, environment, or matrix target, say so explicitly in the PR and justify the coverage delta; a test failing in a new environment is usually signal worth investigating.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@AGENTS.md` around lines 133 - 159, Update the CI/CD table entry for main-build.yml to reflect that it is now a wrapper around the reusable browser-matrix workflow rather than the old "Build + audit + headless test" description: find the table row for `main-build.yml` and replace the short description with a concise, accurate summary such as "Wrapper over reusable browser-matrix workflow — build + audit; run browser-matrix tests" (or equivalent wording that calls out the reusable browser-matrix workflow and the actual steps run).
🧹 Nitpick comments (2)
.github/workflows/release.yml (1)
63-75: ⚡ Quick winUse Bun for the Changesets commands in the release path.
npxadds a second package-manager execution path to the workflow that cuts releases. Switching both invocations tobunx changeset ...keeps this pinned to the repo-managed toolchain and avoids an npm fallback in the critical path.Suggested diff
- version: npx changeset version + version: bunx changeset version ... - publish: npx changeset publish --no-git-tag + publish: bunx changeset publish --no-git-tagBased on learnings: Use bun install instead of npm install, and bunx instead of npx; Bun is the package manager and script runner.
Also applies to: 151-156
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/release.yml around lines 63 - 75, Replace any npx invocations used to run Changesets in the release workflow with bunx to pin execution to the repo-managed Bun toolchain: specifically update the changesets/action step where the input "version: npx changeset version" is set (and the analogous occurrences later in the file) to use "bunx changeset ..." instead of "npx changeset ..."; ensure any other release-related commands that currently call npm/npx (e.g., install or run commands referenced near the changesets usage) are switched to bun install / bunx equivalents so the workflow uses Bun end-to-end..github/workflows/build-and-test.yml (1)
15-20: ⚡ Quick winLock this reusable workflow to read-only permissions.
This job only needs repository read access, but the shared workflow leaves
GITHUB_TOKENpermissions implicit. Adding an explicit read-only boundary here keeps future callers from accidentally giving a pure build/test job write scopes.Suggested diff
on: workflow_call: +permissions: + contents: read + jobs: test:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/build-and-test.yml around lines 15 - 20, The test job currently runs with implicit GITHUB_TOKEN scopes; add an explicit read-only permission block to lock it down by adding a permissions mapping under the test job (jobs -> test) such as permissions: contents: read (or other minimal read-only scopes you need) so the job/container (container image mcr.microsoft.com/playwright:v1.59.1-noble) only has repository read access; ensure you place the permissions key at the same level as runs-on/container in the test job.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@AGENTS.md`:
- Around line 133-159: Update the CI/CD table entry for main-build.yml to
reflect that it is now a wrapper around the reusable browser-matrix workflow
rather than the old "Build + audit + headless test" description: find the table
row for `main-build.yml` and replace the short description with a concise,
accurate summary such as "Wrapper over reusable browser-matrix workflow — build
+ audit; run browser-matrix tests" (or equivalent wording that calls out the
reusable browser-matrix workflow and the actual steps run).
---
Nitpick comments:
In @.github/workflows/build-and-test.yml:
- Around line 15-20: The test job currently runs with implicit GITHUB_TOKEN
scopes; add an explicit read-only permission block to lock it down by adding a
permissions mapping under the test job (jobs -> test) such as permissions:
contents: read (or other minimal read-only scopes you need) so the job/container
(container image mcr.microsoft.com/playwright:v1.59.1-noble) only has repository
read access; ensure you place the permissions key at the same level as
runs-on/container in the test job.
In @.github/workflows/release.yml:
- Around line 63-75: Replace any npx invocations used to run Changesets in the
release workflow with bunx to pin execution to the repo-managed Bun toolchain:
specifically update the changesets/action step where the input "version: npx
changeset version" is set (and the analogous occurrences later in the file) to
use "bunx changeset ..." instead of "npx changeset ..."; ensure any other
release-related commands that currently call npm/npx (e.g., install or run
commands referenced near the changesets usage) are switched to bun install /
bunx equivalents so the workflow uses Bun end-to-end.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7949279f-5a11-447b-8ddb-80496f39a45b
📒 Files selected for processing (4)
.github/workflows/build-and-test.yml.github/workflows/main-build.yml.github/workflows/release.ymlAGENTS.md
Codex P1: changesets/action infers `outputs.published` by parsing `New tag:` lines from `changeset publish` stdout. Those lines are only emitted when changeset publish creates per-package git tags. With `--no-git-tag`, `published` always reports false → the post-publish tag step never fires → release tag never created. Drop the flag and let changeset publish create its 27 per-package tags. Noisy but harmless; the canonical release marker remains the repo-wide vX.Y.Z tag created by `gh release create` after publish. Also addresses Copilot review: - Standardize on `bunx` for CLI invocations (was `npx`); matches the rest of the repo's tooling per AGENTS.md. - Reword the workflow header to acknowledge that publish-and-tag runs on any no-pending-changeset main push (docs/plans included), not only after a version-PR merge — the published-output gate is what makes the path safe. - Sharpen the persist-credentials note: the post-publish gh release create authenticates via GH_TOKEN; persist-credentials: true is needed for `changeset publish`'s per-package tag pushes via git remote. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addressed in 29d8decCodex P1 (blocker): dropped Copilot:
Not addressed (by design)
|
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
Summary
Implements the plan in #373 — switch
release.ymlfrom tag-push trigger to push-to-main trigger, collapse three jobs into two, and make merging the version PR the only human action in a release.Changes
.github/workflows/release.ymlpush: tags: v*topush: branches: [main].prepare/publish-and-tag) preserve least-privilege isolation:prepareholdscontents:write+pull-requests:write(no OIDC).publish-and-tagholdscontents:write+id-token:write(no PR write).prepareusespersist-credentials: falsepaired withcommitMode: github-api, sochangesets/actioncommits the version bump via the API (verified,github-actions[bot]-authored).publish-and-tagis gated onhasChangesets == 'false', so feature PRs with changesets do not trigger the publish path — their changesets accumulate in the version PR.publish-and-tag, gated by the outer job condition, so no-changeset paths skip them.changesets/action'spublishinput. Itspublishedoutput is the authoritative "did anything go to npm" signal — closing the trap where a plan-only / doc-only main push would otherwise hitgh release createfor an already-existing tag.--no-git-tag; the repo-widevX.Y.Zis the single tag.gh release createcreates tag ref + release in one API call. No moregit tag && git pushorphan-tag failure mode.workflow-ref) serializes against itself so two near-simultaneous main pushes can't race.*-alpha,*-alpha.*, etc.) — no false positives like1.0.0-alphabet.AGENTS.mdrelease.ymlupdated (trigger column: "Push to main").Test plan
plans/single-action-release.mdis onmainwhen this PR merges and the workflow comment chain stays consistent. (Plan PR was deliberately not bundled here so the design discussion stayed on the plan.)vX.Y.Ztag + GH release on the version-PR-merge commit.main(e.g. README edit) runs onlyprepare, exits withshould_publish=true, runspublish-and-tag, seesoutputs.published=false, skips the tag step. (This is the "wasteful but safe" path described in the plan's Doc-only main pushes tradeoff.)prepare, exits withshould_publish=false, skipspublish-and-tagentirely, and updates the open version PR.Notes
This PR depends on #373 conceptually but does not require it to merge first to function — the workflow stands on its own. Recommend landing #373 first for review-trail clarity.
Adversarial review
Performed in commit message of
78d156f9. Fresh subagent flagged the gating-logic bug (conflating "no pending changesets" with "version PR just merged"); fixed by switching tochangesets/action'spublishedoutput. Remaining items accepted as documented tradeoffs.Summary by CodeRabbit
Chores
Documentation