chore(release): migrate to Changesets v3 and changesets/action v2 - #848
Merged
Conversation
|
chybisov
force-pushed
the
chore/changesets-v3
branch
2 times, most recently
from
August 20, 2026 11:26
87e3120 to
18aae6a
Compare
Changesets CLI v2 -> v3 and changesets/action v1 -> v2. The action validates
the CLI major and refuses a v2 CLI, so both must move together.
.changeset/config.json:
- $schema -> the installed @changesets/config major (v4).
- privatePackages set explicitly to { version: true, tag: false }. v2 defaulted
an absent key to { version: true, tag: false }; v3 defaults it to
{ version: false, tag: false }. Without this, @lifi/widget-checkout,
@lifi/widget-provider-mesh and @lifi/widget-provider-transak silently stop
being versioned.
- format: false. The v3 "prettier" option is gone; "auto" deliberately excludes
Biome from its detection order, so it would be a no-op here. changeset:version
already ends with `pnpm check:write`.
publish.yaml — renamed every input and output the action renamed:
version -> version-script, publish -> publish-script,
commit -> commit-message, title -> pr-title,
createGithubReleases -> create-github-releases,
hasChangesets -> has-changesets, publishedPackages -> published-packages.
The output renames fail silently: a stale `outputs.hasChangesets` yields "",
so `'' == 'false'` is false and the release job would never run while CI stayed
green. Verified against the pinned dist that has-changesets is written before
any branching.
Dropped `env: GITHUB_TOKEN` from both steps — v2 throws when it is set and
differs from the github-token input. Kept push-with-git-cli: true so the push
mechanism does not change with the upgrade.
preview-publish: `changeset version` now exits 1 when there are no changesets,
which made the existing "no changeset" warning path unreachable. Added a
top-level .changeset/*.md check that gates the snapshot and detect steps.
Pre-mode changesets live in .changeset/pre/, so the glob stays non-recursive.
Examples nuxt, react-router and remix had no version field. react-router also
shadows its own react-router dependency, so v3 passed undefined into
node:util.styleText and crashed. Every other example already declares 0.0.0.
Also bumped pnpm/action-setup v6.0.9 -> v6.0.10 and
linear/linear-release-action v0.14.6 -> v0.15.1 (all six inputs the workflow
passes still exist). The other nine pinned actions already match their latest
release SHA.
lifinance/sdk already runs changesets/action v2 in production. Matching it rather than keeping a local variant: - Drop `push-with-git-cli: true` and take v2's default, so release commits and tags go through the GitHub API and are signed with GitHub's GPG key. The SDK does not set this input either. - Use the SDK's `find -maxdepth 1 ... -print -quit` form for the changeset presence check, and name the output `present`. Behaviour is unchanged — verified against README-only, .changeset/pre/-only, and real-changeset fixtures — but the two repos now read the same. - Carry over the SDK's comments explaining why neither step sets `env: GITHUB_TOKEN`.
chybisov
force-pushed
the
chore/changesets-v3
branch
from
August 20, 2026 12:06
18aae6a to
55ef262
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which Linear task is linked to this PR?
Release-tooling maintenance — no Linear task.
Why was it implemented this way?
@changesets/cliv2 → v3 andchangesets/actionv1 → v2. These are not separable: theaction reads the root
@changesets/clirange and throws if it intersects>=2.0.0-0 <3.0.0-0("Changesets CLI v2 is not supported; use Changesets action v1 instead"). Verified in the pinned
dist/.privatePackagesnow defaults differently, and the repo relies on the old default.privatePackagesresolves to@changesets/config@3.1.4(v2){ version: true, tag: false }@changesets/config@4.0.0(v3){ version: false, tag: false }Read from the two installed packages' compiled
dist/, not the docs.@lifi/widget-checkout,@lifi/widget-provider-meshand@lifi/widget-provider-transakareprivate: truebut not inignore, so they depend on that old default.I set it explicitly and A/B-tested it. Without the setting,
changeset versiondoes notmerely skip them — it hard-fails:
With
"privatePackages": { "version": true, "tag": false }, all three bump and get CHANGELOGs..changeset/config.json$schema→ the installed@changesets/configmajor (v4).privatePackages→ set explicitly, as above.format: false. The v3prettieroption is gone."auto"looked like the drop-inreplacement, but its detection order explicitly excludes Biome
(
defaultDetectOrder.filter((f) => f !== "biome")), so it would silently no-op here.changeset:versionalready ends withpnpm check:write.access: "public"andbaseBranch: "main"are unchanged (v3 only moved the default foraccesstorestricted; ours is explicit).publish.yaml— the renames that fail silentlyInputs throw loudly on the old names, so those are self-correcting. Outputs do not. A stale
needs.changesets.outputs.hasChangesetsyields'', and'' == 'false'is false — so thereleasejob would never run while CI stayed green, shipping nothing.version:version-script:publish:publish-script:commit:commit-message:title:pr-title:createGithubReleases:create-github-releases:outputs.hasChangesetsoutputs.has-changesetsoutputs.publishedPackagesoutputs.published-packagesTwo checks done as a result:
grep -rn 'hasChangesets\|publishedPackages\|createGithubReleases\|pull-request-number\|setup-git-user\|commit-mode' .github/→ empty, comments and input descriptions included.dist/index.js:has-changesetsis written before any branching(
a('published','false'), a('published-packages','[]'), a('has-changesets', String(S))), sothe gate always receives a value.
Dropped
env: GITHUB_TOKENfrom both steps — v2 throws when it is set and differs from thegithub-tokeninput. Safe because the action injectsGITHUB_TOKENinto the script'senvironment itself, on both paths:
That matters because
@changesets/changelog-githubneeds the token — runningpnpm changeset:versionwithout one fails outright.Push mechanism: v2's default (GitHub API). v2 pushes release commits and tags via the API
rather than the Git CLI, signing them with GitHub's GPG key. I first pinned the old behaviour with
push-with-git-cli: true, then dropped it after checkinglifinance/sdk— see below.CHANGESETS_OUTPUTnow carries the published-package list instead of stdout parsing, andour
publish-scriptis a two-level wrapper (changeset:publish→changeset:prepublish+changeset publish). Proved the variable survives the chain with a throwaway probe through theexact same nesting, including the
--config.verify-deps-before-run=falseform:Without this,
published-packageswould be[], no GitHub releases or tags would be created,and the Linear sync jobs would never fire — all while the job stayed green.
preview-publish— the v3 exit-1 landminechangeset versionnow exits 1 when there are no changesets, and this composite ranpnpm changeset version --snapshotunconditionally, with a "no changeset" warning in alater step. That warning path was unreachable under v3; the job would just fail.
Added a
Check for changesetsstep that gates the snapshot and detect steps. It is scoped totop-level
.changeset/*.md— pre-mode changesets live in.changeset/pre/— and skipsREADME.md. Confirmed.changeset/pre.jsonis still v3's pre-state file (only the changesetfiles moved), so the pre-mode guard above it stays valid.
Verified by fixture in all three states: README+config only →
present=false; a changeset in.changeset/pre/only →present=false; a real top-level changeset →present=true. Andconfirmed the underlying behaviour directly:
Aligned with
lifinance/sdklifinance/sdkalready runschangesets/action@v2in production, so it is the reference ratherthan a guess. Its
publish.yamlindependently matches every choice here —version-script,pr-title,commit-message,publish-script,create-github-releases, thehas-changesets/published-packagesoutputs, and noenv: GITHUB_TOKEN— and its.changeset/config.jsonusesthe same
@changesets/config@4.0.0$schemaand the sameformat: false. Itspreview-publishcomposite even has the same changeset-presence guard, reached independently.
Three points of alignment adopted from it:
push-with-git-cli: true. The SDK does not set it, so it runs v2's API-push default.find .changeset -maxdepth 1 -name '*.md' ! -name 'README.md' -print -quit, output namedpresent. Behaviour is identical to my original loop (re-verifiedagainst README-only,
.changeset/pre/-only, and real-changeset fixtures), but the two repos nowread the same.
env: GITHUB_TOKEN.The one thing the SDK does not need is
privatePackages— itsignorelist is empty and it hasno private-but-tracked packages. This repo has three, hence the explicit setting above.
Examples missing a
versionfieldexamples/nuxt,examples/react-routerandexamples/remixhad noversion. Every otherexample declares
0.0.0.react-routeris also namedreact-router, shadowing its ownreact-routerdependency, so v3 passedundefinedintonode:util.styleTextand crashed:v2 used chalk, which tolerated
undefined. Added"version": "0.0.0"to all three.Generated version file — no drift found
Checked rather than assumed.
git show --staton the lastchore: version packagescommit(
2388e1651) shows it did touchpackages/widget/src/config/version.tsandpackages/widget-light/src/config/version.ts, and both match theirpackage.jsononmain(4.5.0 / 4.3.2).
changeset:versionalready regenerates them viabuild:version, andversion-script:preserves that hook. Confirmed end-to-end below. No fix needed.Other pinned actions
pnpm/action-setupv6.0.9 → v6.0.10 andlinear/linear-release-actionv0.14.6 → v0.15.1 (allsix inputs the workflow passes still exist at the pinned SHA). The other nine pinned actions
already sit on their latest release SHA — verified by resolving each tag to its commit via
gh api, dereferencing annotated tags.Visual showcase (Screenshots or Videos)
Not applicable — release tooling.
Verification
pnpm install --frozen-lockfilepnpm dedupe --checkpnpm check(Biome)pnpm check:typespnpm check:circular-depspnpm build@lifi/widget/-provider/-provider-trontestspnpm changeset statusactionlintmain— zero newshellcheckon the new scriptRelease path exercised locally, which CI cannot do:
pnpm changeset:versionend-to-end → exit 0.@lifi/widget4.5.0 → 4.5.1 withsrc/config/version.tsregenerated to 4.5.1 in the same run (in sync). All threeprivate-but-tracked packages bumped 4.0.1 → 4.0.2 with CHANGELOGs. Example
@lifi/widgetpins correctly restored to
4.5.0byrestore-example-versions.sh. Changesets consumed.Then
git reset --hard; versions confirmed back at committed values.privatePackages→ hard failure, as quoted above.What CI still cannot cover
The
changesetsandreleasejobs are gated offpull_request, so a green check on this PRonly covers build/lint/types. Specifically unverified until a real release runs:
changesets/action@v2via the GitHub APIchangeset publishwritingCHANGESETS_OUTPUTthrough the wrapper (env propagation proven; the write itself is not)published-packagesChecklist before requesting a review