Files
pnpm/__utils__/scripts/package.json
Zoltan Kochan 76083acd54 chore(release): track consumed changesets per branch to prevent re-application after cherry-pick (#11479)
* chore(release): wrap changeset version with cross-branch consumed-id ledger

When a fix is cherry-picked from main to a release branch (or vice
versa), the changeset file ends up on both branches. The release
branch's release consumes and deletes its copy, but the cherry-picked
copy on main survives the merge back and would be re-applied on the
next main release.

Introduce a small wrapper around `changeset version` that maintains a
per-branch ledger at .changeset/.released/<branch>.txt. Each entry is a
consumed changeset id; the file is written only by the branch it is
named after, so the records merge across branches without conflicts.

Before running `changeset version` the wrapper reads the union of every
ledger file, hides matching .changeset/<id>.md files (rename to
.md.released), then runs `changeset version` against the remaining set.
Newly consumed ids are appended to the current branch's ledger; hidden
files are removed afterward (their consumption is already on record
elsewhere). On failure the hidden files are restored to keep the
working tree clean.

* docs: move release-ledger explanation out of AGENTS.md

AGENTS.md is for instructions to AI agents working on the codebase, but
the cross-branch ledger is release machinery that the maintainer running
`pnpm bump` interacts with — agents authoring changesets do not need to
know about it. Move the explanation to where someone runs into it:

- .changeset/.released/README.md — discovered by anyone exploring the
  directory.
- A short doc-comment header at the top of __utils__/scripts/src/bump.ts
  pointing readers there.

* fix(scripts): harden bump wrapper edge cases from PR review

- Use url.pathToFileURL(realpathSync(...)) to compare against
  import.meta.url so the direct-invocation guard works on Windows
  paths and through symlinks (Copilot review).
- hideReleased() now iterates the changeset directory and filters by
  the released set instead of iterating the (potentially long) ledger
  and probing existsSync per entry (Copilot review).
- hideReleased() restores already-renamed files if a later rename
  throws, so a partial failure leaves the .changeset directory in its
  original state (CodeRabbit review).
- Move deleteHidden() into a finally so the .md.released files are
  cleaned up even if appendReleased() throws after a successful
  changeset version run (CodeRabbit review).
- Add a unit test that forces hideReleased() to fail mid-loop and
  asserts the rollback.
2026-05-06 01:34:52 +02:00

1.0 KiB