From 57041da0aa1913014b103e29a65100ebbfd17bc3 Mon Sep 17 00:00:00 2001 From: Benoit TRAVERS Date: Wed, 26 Aug 2026 01:00:12 +0200 Subject: [PATCH] fix(release): bump changesets/action to v2, and pin the checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5. **v1 breaks any repository that keeps agent guidance in `.changeset/`.** It bundles `@changesets/read@^0.6.7`, which parses EVERY `.changeset/*.md` as a changeset, so a `.changeset/CLAUDE.md` fails the whole release with could not parse changeset - missing or invalid frontmatter That is not hypothetical: it is btravstack/btravstack's release pipeline right now, red on every merge to main since it added one. `@changesets/read@1.0.0` — what v2 bundles — ignores `README.md`, `AGENTS.md`, `CLAUDE.md` and `GEMINI.md`, which is the ecosystem acknowledging exactly this. The consumer's own `pnpm run version` was never affected, since a repository installs a current `@changesets/read`; only the action's bundled copy is old. v2 renamed every input, so this is not a version bump alone: `version`→`version-script`, `publish`→`publish-script`, `commit`→ `commit-message`, `title`→`pr-title`. **`github-token` is an input now, not just the env var**, and getting this wrong would be silent: v2 defaults it to `${{ github.token }}`, so passing the PAT through the environment alone falls back to the bot token — and a release PR opened by that starts no workflow runs, which is the one thing the PAT exists to prevent. Closes #5 in the same file: an optional `ref` input, defaulting to "" so omitting it keeps today's behaviour. A caller on `workflow_run` passes `github.event.workflow_run.head_sha`, the exact commit the green run measured — otherwise the checkout takes the default branch's CURRENT tip, which a push landing after CI went green can have moved, and a permanent tarball is cut from a commit no CI run validated. This repository's own `release.yml` pins it directly. Also moves both workflows off `pnpm/action-setup@v4` and `actions/setup-node@v4`, which are forced onto Node 24 with a deprecation warning on every run; v6 and v7 are what the composite action here already uses. --- .github/workflows/release-reusable.yml | 47 +++++++++++++++++++++----- .github/workflows/release.yml | 24 ++++++++++--- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release-reusable.yml b/.github/workflows/release-reusable.yml index 9dfada8..7dc9f00 100644 --- a/.github/workflows/release-reusable.yml +++ b/.github/workflows/release-reusable.yml @@ -11,6 +11,8 @@ # release: # if: ${{ github.event.workflow_run.conclusion == 'success' }} # uses: btravstack/tools/.github/workflows/release-reusable.yml@main +# with: +# ref: ${{ github.event.workflow_run.head_sha }} # secrets: # RELEASE_PAT: ${{ secrets.RELEASE_PAT }} # @@ -30,6 +32,17 @@ on: description: "Command changesets/action runs to publish." type: string default: "pnpm run release" + ref: + description: > + Commit to check out. A caller on `workflow_run` should pass + `github.event.workflow_run.head_sha` — the exact commit the green run + measured. Without it a `workflow_run` checkout takes the default + branch's CURRENT tip, which a push landing after CI went green can + have moved, and the release then carries a commit no CI run + validated. Empty is checkout's own default, so omitting it keeps the + previous behaviour. + type: string + default: "" secrets: RELEASE_PAT: description: "PAT with Contents + Pull requests write (fires CI on the release PR)." @@ -48,16 +61,17 @@ jobs: - name: Checkout uses: actions/checkout@v7 with: + ref: ${{ inputs.ref }} # PAT so the release PR is created with credentials that DO fire # `pull_request` workflows (the bare GITHUB_TOKEN push is treated as a # bot event and would skip CI on the "Version Packages" branch). token: ${{ secrets.RELEASE_PAT }} - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v7 with: node-version-file: .node-version cache: pnpm @@ -65,14 +79,29 @@ jobs: - name: Install run: pnpm install --frozen-lockfile + # v2, not v1, and the reason is a real failure rather than hygiene: v1 + # bundles `@changesets/read@^0.6.7`, which parses EVERY `.changeset/*.md` + # as a changeset. A repository keeping agent guidance beside its + # changesets — `.changeset/CLAUDE.md` — fails the whole release with + # `could not parse changeset - missing or invalid frontmatter`, which is + # what happened to btravstack/btravstack. `@changesets/read@1.0.0`, which + # v2 bundles, ignores `README.md`, `AGENTS.md`, `CLAUDE.md` and + # `GEMINI.md`. + # + # v2 renamed every input; the pairs are version->version-script, + # publish->publish-script, commit->commit-message, title->pr-title. - name: Create Release Pull Request or Publish to npm - uses: changesets/action@v1 + uses: changesets/action@v2 with: - version: ${{ inputs.version-command }} - publish: ${{ inputs.publish-command }} - commit: "chore: release packages" - title: "chore: release packages" + # An INPUT now, not just the env below. v2 gives `github-token` a + # default of `${{ github.token }}`, so passing the PAT only through + # the environment would silently fall back to the bot token — and a + # release PR opened by it starts no workflow runs, which is the exact + # thing the PAT exists to prevent. + github-token: ${{ secrets.RELEASE_PAT }} + version-script: ${{ inputs.version-command }} + publish-script: ${{ inputs.publish-command }} + commit-message: "chore: release packages" + pr-title: "chore: release packages" env: - # PAT (not GITHUB_TOKEN): GITHUB_TOKEN-triggered events don't start new - # workflow runs, so the "Version Packages" PR would otherwise skip CI. GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49180d1..8becb6c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,10 @@ jobs: - name: Checkout uses: actions/checkout@v7 with: + # The exact commit the green CI run measured, never the branch tip: a + # `workflow_run` checkout otherwise takes the default branch's CURRENT + # tip, which a push landing after CI went green can have moved. + ref: ${{ github.event.workflow_run.head_sha || github.sha }} # PAT so the git remote is configured with PAT credentials; the bare # GITHUB_TOKEN push checkout normally sets up is treated as a bot # event and would not fire `pull_request` workflows on the resulting @@ -38,14 +42,24 @@ jobs: - name: Create Release Pull Request or Publish to npm id: changesets - uses: changesets/action@v1 + # v2, not v1: v1 bundles `@changesets/read@^0.6.7`, which parses EVERY + # `.changeset/*.md` as a changeset and fails the whole release on a + # repository that keeps `.changeset/CLAUDE.md` beside its changesets. + # `@changesets/read@1.0.0` — v2's — ignores `README.md`, `AGENTS.md`, + # `CLAUDE.md` and `GEMINI.md`. v2 also renamed every input. + uses: changesets/action@v2 with: + # An INPUT now: v2 defaults `github-token` to `${{ github.token }}`, + # so passing the PAT only through the environment below would + # silently fall back to the bot token, and a release PR opened by it + # starts no workflow runs — the exact thing the PAT prevents. + github-token: ${{ secrets.RELEASE_PAT }} # Use `pnpm run …` so we invoke the package.json scripts. Bare # `pnpm version` collides with pnpm's built-in `version` command. - version: pnpm run version - publish: pnpm run release - commit: "chore: release packages" - title: "chore: release packages" + version-script: pnpm run version + publish-script: pnpm run release + commit-message: "chore: release packages" + pr-title: "chore: release packages" env: # PAT rather than GITHUB_TOKEN: events triggered by GITHUB_TOKEN do # not start new workflow runs (anti-recursion safeguard), so the