Skip to content

chore(release): migrate to Changesets v3 and changesets/action v2 - #848

Merged
chybisov merged 2 commits into
chore/update-dependenciesfrom
chore/changesets-v3
Aug 20, 2026
Merged

chore(release): migrate to Changesets v3 and changesets/action v2#848
chybisov merged 2 commits into
chore/update-dependenciesfrom
chore/changesets-v3

Conversation

@chybisov

@chybisov chybisov commented Aug 20, 2026

Copy link
Copy Markdown
Member

Stacked on #847 — base is chore/update-dependencies. Merge that first.

Which Linear task is linked to this PR?

Release-tooling maintenance — no Linear task.

Why was it implemented this way?

@changesets/cli v2 → v3 and changesets/action v1 → v2. These are not separable: the
action reads the root @changesets/cli range 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/.

⚠️ The one change that would have broken the next release

privatePackages now defaults differently, and the repo relies on the old default.

absent privatePackages resolves 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-mesh and @lifi/widget-provider-transak are private: true but not in
ignore, so they depend on that old default.

I set it explicitly and A/B-tested it. Without the setting, changeset version does not
merely skip them — it hard-fails:

Error: Found mixed changeset update-provider-runtime-deps
Found ignored packages: @lifi/widget-provider-mesh
Found not ignored packages: @lifi/wallet-management @lifi/widget-provider …
Mixed changesets that contain both ignored and not ignored packages are not allowed

With "privatePackages": { "version": true, "tag": false }, all three bump and get CHANGELOGs.

.changeset/config.json

  • $schema → the installed @changesets/config major (v4).
  • privatePackages → set explicitly, as above.
  • format: false. The v3 prettier option is gone. "auto" looked like the drop-in
    replacement, but its detection order explicitly excludes Biome
    (defaultDetectOrder.filter((f) => f !== "biome")), so it would silently no-op here.
    changeset:version already ends with pnpm check:write.
  • access: "public" and baseBranch: "main" are unchanged (v3 only moved the default for
    access to restricted; ours is explicit).

publish.yaml — the renames that fail silently

Inputs throw loudly on the old names, so those are self-correcting. Outputs do not. A stale
needs.changesets.outputs.hasChangesets yields '', and '' == 'false' is false — so the
release job would never run while CI stayed green, shipping nothing.

old new
version: version-script:
publish: publish-script:
commit: commit-message:
title: pr-title:
createGithubReleases: create-github-releases:
outputs.hasChangesets outputs.has-changesets
outputs.publishedPackages outputs.published-packages

Two checks done as a result:

  1. grep -rn 'hasChangesets\|publishedPackages\|createGithubReleases\|pull-request-number\|setup-git-user\|commit-mode' .github/empty, comments and input descriptions included.
  2. Traced the pinned dist/index.js: has-changesets is written before any branching
    (a('published','false'), a('published-packages','[]'), a('has-changesets', String(S))), so
    the gate always receives a value.

Dropped env: GITHUB_TOKEN from both steps — v2 throws when it is set and differs from the
github-token input. Safe because the action injects GITHUB_TOKEN into the script's
environment itself, on both paths:

let w = {...process.env, GITHUB_TOKEN: t.getToken()};   // version path
p = {...,  env: {...process.env, GITHUB_TOKEN: r.getToken(), CHANGESETS_OUTPUT: f}};  // publish path

That matters because @changesets/changelog-github needs the token — running
pnpm changeset:version without 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 checking lifinance/sdk — see below.

CHANGESETS_OUTPUT now carries the published-package list instead of stdout parsing, and
our publish-script is a two-level wrapper (changeset:publishchangeset:prepublish +
changeset publish). Proved the variable survives the chain with a throwaway probe through the
exact same nesting, including the --config.verify-deps-before-run=false form:

INNER CHANGESETS_OUTPUT=/tmp/probe-changesets.ndjson
INNER CHANGESETS_OUTPUT=/tmp/probe-changesets.ndjson

Without this, published-packages would 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 landmine

changeset version now exits 1 when there are no changesets, and this composite ran
pnpm changeset version --snapshot unconditionally, with a "no changeset" warning in a
later step. That warning path was unreachable under v3; the job would just fail.

Added a Check for changesets step that gates the snapshot and detect steps. It is scoped to
top-level .changeset/*.md — pre-mode changesets live in .changeset/pre/ — and skips
README.md. Confirmed .changeset/pre.json is still v3's pre-state file (only the changeset
files 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. And
confirmed the underlying behaviour directly:

$ pnpm changeset version --snapshot "preview-testsha"
No unreleased changesets found.
🦋 Exited with code 1

Aligned with lifinance/sdk

lifinance/sdk already runs changesets/action@v2 in production, so it is the reference rather
than a guess. Its publish.yaml independently matches every choice here — version-script,
pr-title, commit-message, publish-script, create-github-releases, the has-changesets /
published-packages outputs, and no env: GITHUB_TOKEN — and its .changeset/config.json uses
the same @changesets/config@4.0.0 $schema and the same format: false. Its preview-publish
composite even has the same changeset-presence guard, reached independently.

Three points of alignment adopted from it:

  • Dropped push-with-git-cli: true. The SDK does not set it, so it runs v2's API-push default.
  • Same guard implementationfind .changeset -maxdepth 1 -name '*.md' ! -name 'README.md' -print -quit, output named present. Behaviour is identical to my original loop (re-verified
    against README-only, .changeset/pre/-only, and real-changeset fixtures), but the two repos now
    read the same.
  • Carried over its comments explaining why neither step sets env: GITHUB_TOKEN.

The one thing the SDK does not need is privatePackages — its ignore list is empty and it has
no private-but-tracked packages. This repo has three, hence the explicit setting above.

Examples missing a version field

examples/nuxt, examples/react-router and examples/remix had no version. Every other
example declares 0.0.0. react-router is also named react-router, shadowing its own
react-router dependency, so v3 passed undefined into node:util.styleText and crashed:

TypeError [ERR_INVALID_ARG_TYPE]: The "text" argument must be of type string. Received undefined
    at styleText (node:util:271:3)

v2 used chalk, which tolerated undefined. Added "version": "0.0.0" to all three.

The four must depend on the current version of … warnings (connectkit, vue,
react-router, svelte shadowing their own npm deps) are pre-existing and non-fatal —
identical on main. Renaming those packages to *-example would clear them, but that is
out of scope here.

Generated version file — no drift found

Checked rather than assumed. git show --stat on the last chore: version packages commit
(2388e1651) shows it did touch packages/widget/src/config/version.ts and
packages/widget-light/src/config/version.ts, and both match their package.json on main
(4.5.0 / 4.3.2). changeset:version already regenerates them via build:version, and
version-script: preserves that hook. Confirmed end-to-end below. No fix needed.

Other pinned actions

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 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

Gate Result
pnpm install --frozen-lockfile pass
pnpm dedupe --check pass
pnpm check (Biome) pass
pnpm check:types pass
pnpm check:circular-deps pass
pnpm build pass
@lifi/widget / -provider / -provider-tron tests 71 / 17 / 9 pass
pnpm changeset status pass (exit 0; crashed before the version-field fix)
YAML parse, all 9 workflows + 2 composite actions 0 failures
actionlint 9 findings, byte-identical to main — zero new
shellcheck on the new script clean
Aikido Security pass

Release path exercised locally, which CI cannot do:

  • pnpm changeset:version end-to-end → exit 0. @lifi/widget 4.5.0 → 4.5.1 with
    src/config/version.ts regenerated to 4.5.1 in the same run (in sync). All three
    private-but-tracked packages bumped 4.0.1 → 4.0.2 with CHANGELOGs. Example @lifi/widget
    pins correctly restored to 4.5.0 by restore-example-versions.sh. Changesets consumed.
    Then git reset --hard; versions confirmed back at committed values.
  • Snapshot path run without a changeset → confirmed exit 1, validating the new guard.
  • A/B without privatePackages → hard failure, as quoted above.

What CI still cannot cover

The changesets and release jobs are gated off pull_request, so a green check on this PR
only covers build/lint/types. Specifically unverified until a real release runs:

  • the version PR being created and pushed by changesets/action@v2 via the GitHub API
  • changeset publish writing CHANGESETS_OUTPUT through the wrapper (env propagation proven; the write itself is not)
  • GitHub Release creation and git tag push
  • the Linear sync jobs reading published-packages

Checklist before requesting a review

  • I have performed a self-review and testing of my code.
  • This pull request is focused and addresses a single problem.
  • If this PR modifies the Widget API or adds new features that require documentation, I have updated the documentation in the public-docs repository.

@changeset-bot

changeset-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 55ef262

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chybisov
chybisov force-pushed the chore/changesets-v3 branch 2 times, most recently from 87e3120 to 18aae6a Compare August 20, 2026 11:26
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
chybisov force-pushed the chore/changesets-v3 branch from 18aae6a to 55ef262 Compare August 20, 2026 12:06
@chybisov
chybisov merged commit a814018 into chore/update-dependencies Aug 20, 2026
2 checks passed
@chybisov
chybisov deleted the chore/changesets-v3 branch August 20, 2026 12:13
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.

1 participant