Skip to content

ci: release through the Changesets Version Packages PR - #81

Merged
rafa-thayto merged 2 commits into
mainfrom
rafa-thayto/changesets-release-flow
Oct 27, 2026
Merged

rafa-thayto merged 2 commits into
mainfrom
rafa-thayto/changesets-release-flow

Conversation

@rafa-thayto

Copy link
Copy Markdown
Contributor

Why

#77 (EUR), #78 (wallet self-custody) and #80 (GBP) are merged but were never published. publish.yaml only published when someone hand-edited version in package.json, and nothing ever ran changeset version. So 40 changesets piled up, and every Publish run skipped because 5.3.1 was already on npm.

What changes

publish.yaml now follows the official Changesets trusted-publishing layout (changesets/action v2). This is the same model Clerk uses: a bot opens a "Version packages" PR and a human merges it to release.

Job When Does Permissions
select-mode every push to main changesets pending → version; version not on npm → publish contents: read
version version opens or updates chore(release): version packages with SDK_SYNC_PAT, so CI runs on it. @changesets/changelog-github links PRs and authors contents/pull-requests: write
pack publish type-check, test, build, pack the exact release commit contents: read
publish after pack publishes the packed tarball with npm trusted publishing (OIDC), tags vX.Y.Z, creates the GitHub release contents: write, only job with id-token: write

Hardening:

  • permissions: {} by default, set per job.
  • Actions pinned by SHA.
  • persist-credentials: false on checkout.
  • No cache restore in release jobs (setup-bun no-cache, package-manager-cache: false).
  • npm ≥ 11.5.1 is checked before publish.
  • A missing SDK_SYNC_PAT now fails the run and pages Slack, instead of falling back to GITHUB_TOKEN and opening a Version PR with no CI.

Kept on purpose:

  • The file name publish.yaml, because npm's trusted publisher is bound to it.
  • name: Publish, because pipeline-alert.yml matches on it.
  • registry-url with no NODE_AUTH_TOKEN.

Also:

  • api-sync writes .changeset/api-sync-<spec sha>.md instead of bumping package.json. Syncs still auto-merge, but now release through the same Version PR. The file name comes from the delivered spec, so the determinism proof stays byte-identical. I checked this end to end with a modified spec: two runs gave identical output, package.json was untouched, and the committed snapshot is a no-op.
  • Changeset Status is a new, non-blocking PR comment that says whether a PR has a changeset. A PR merged without one releases nothing.
  • @changesets/cli 2.29.6 → 3.0.3, which action v2 requires.
  • 37 changesets deleted. Each one was added in a commit already included in v5.3.1 or earlier, and they are notes only, not code. Consuming them would have produced a bogus 6.0.0, because 4 of them are major. The 3 unreleased ones (fix: add EUR currency support #77, feat(wallets): add self-custody support to blockchain wallets #78, feat: add GBP currency support #80) are kept. CHANGELOG.md now links the GitHub releases for 4.1.0–5.3.1.
  • Docs updated: CLAUDE.md (Releasing) and .changeset/README.md, including a runbook for a release that half-fails.

Acceptance plan (none of this has run on GitHub yet)

Everything so far was verified locally:

  • scratch runs of changeset status, version, publish-plan and pack
  • the api-sync end-to-end run
  • actionlint
  • type-check, lint, 187 tests, sync:check and contract-check

What only real runs can confirm:

  1. Merging this PR: Publish picks version and opens chore(release): version packages. That PR should be authored by the PAT owner, show Main CI checks, set package.json to 5.4.0, and add three CHANGELOG entries linking feat(wallets): add self-custody support to blockchain wallets #78, feat: add GBP currency support #80 and fix: add EUR currency support #77 (previewed locally). If the CI checks are missing, the token path is wrong.
  2. Merging the Version PR: pack → publish → npm 5.4.0, tag v5.4.0 and a GitHub release. If publish fails with E404 or ENEEDAUTH, suspect the npm trusted-publisher binding or the npm version. A failure after npm succeeds is covered by the runbook.

Notes:

  • The Node 18/20/22 test matrix still installs @changesets/cli v3, which needs Node ≥ 22.11. Bun ignores engines, and no test runs the CLI.
  • I couldn't read the repo's Actions settings (403). Because the Version PR uses the PAT, "Allow GitHub Actions to create and approve pull requests" isn't needed.

Follow-ups (from the SRE review, deliberately not done here)

  • api-sync gh pr merge --auto merges instantly, because main has no required checks. This predates this PR.
  • A Version PR merged before the workflow regenerates it skips a version. That's standard Changesets behaviour, and branch protection would prevent it.
  • An optional environment: approval gate on publish. It needs repo settings plus a matching npm trusted-publisher config.
  • SHA-pinning the other six workflows, plus Dependabot for actions. Dependabot PRs would fail Snyk's secrets: inherit today.

Publish only ran `npm publish` when someone hand-edited the version in
package.json, so changesets piled up unconsumed (40 of them) and merged work
(#77, #78, #80) never shipped: every run skipped because 5.3.1 was already on
npm.

publish.yaml now follows the official Changesets trusted-publishing layout
(changesets/action v2, split jobs):
- select-mode: pending changesets -> version, unpublished version -> publish
- version: opens/updates "chore(release): version packages" with SDK_SYNC_PAT
  so CI runs on it; changelog-github links PRs and authors
- pack: type-check, test, build, pack the exact commit being released
- publish: the only job with id-token: write; publishes the packed tarball via
  npm OIDC, tags vX.Y.Z and creates the GitHub release
Actions are pinned by SHA, permissions default to none, checkout does not
persist credentials, and release jobs restore no cache.

api-sync now writes .changeset/api-sync-<spec sha>.md instead of bumping
package.json, so syncs release through the same PR. The name is derived from
the delivered spec, keeping the determinism proof byte-identical.

Also: @changesets/cli 2.29.6 -> 3.0.3 (required by action v2), drop the 37
changesets that already shipped in <= 5.3.1 (consuming them would have cut a
bogus 6.0.0), backfill CHANGELOG links for 4.1.0-5.3.1, document the flow.
- setup-bun no-cache: the publish job (id-token: write) restored and ran a
  cached bun binary; package-manager-cache only covered setup-node
- require SDK_SYNC_PAT instead of silently falling back to GITHUB_TOKEN, which
  would open the Version Packages PR with no CI; a missing PAT now fails and
  pages via pipeline-alert.yml
- drop the version step's GITHUB_TOKEN env: the action overrides it
- add the non-blocking Changeset Status PR comment, since a PR merged without a
  changeset silently releases nothing
- rename scripts/api-sync/version.ts to changeset.ts (it no longer versions)
- document the half-failed release recovery and the CLI's Node requirement
@BernardoSM

BernardoSM commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@rafa-thayto
rafa-thayto merged commit 6ab9478 into main Oct 27, 2026
11 checks passed
@rafa-thayto
rafa-thayto deleted the rafa-thayto/changesets-release-flow branch October 27, 2026 19:27
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.

2 participants