Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 55 additions & 16 deletions skills/setup-changesets/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: setup-changesets
description: "Use this skill when setting up changesets, release CI, or migrating from another release tool."
description: "Use this skill when setting up changesets, release CI, upgrading the Changesets CLI across a major, or migrating from another release tool. Also when a release job fails with a Changesets CLI/action version mismatch."
---

# Setup Changesets
Expand All @@ -10,10 +10,11 @@ description: "Use this skill when setting up changesets, release CI, or migratin
- First-time changesets setup in a repo (single package or monorepo)
- Adding or fixing the CI release workflow (`changesets/action` on GitHub, or direct `version`/`publish` elsewhere)
- Migrating from semantic-release, release-it, lerna, release-please, or similar tools
- Upgrading the CLI across a major, or fixing a `changesets/action` ↔ CLI version mismatch

Not this skill: adding a changeset to an existing PR → use **`add-changeset`** instead.

Trigger phrases: `'add changesets'`, `'set up releases'`, `'configure versioning'`, shared `<org>/.github` release workflow.
Trigger phrases: `'add changesets'`, `'set up releases'`, `'configure versioning'`, `'upgrade changesets'`, shared `<org>/.github` release workflow.

## Instructions

Expand All @@ -26,6 +27,8 @@ Trigger phrases: `'add changesets'`, `'set up releases'`, `'configure versioning
| Package manager | `pnpm-lock.yaml`, `bun.lock`/`bun.lockb`, `yarn.lock`, `package-lock.yaml` |
| Monorepo | `pnpm-workspace.yaml`, `workspaces` in root `package.json`, or `bun.workspace.ts` |
| Already initialized | `.changeset/` directory exists |
| CLI major | `@changesets/cli` range in root `package.json` |
| Action major | `changesets/action@` ref in the release workflow — follow a `uses:` to the shared workflow first |
| CI platform | `.github/workflows/` → GitHub Actions; `.gitlab-ci.yml` → GitLab; `.circleci/config.yml` → CircleCI; `bitbucket-pipelines.yml` → Bitbucket; `azure-pipelines.yml` → Azure; `Jenkinsfile` → Jenkins; `.travis.yml` → Travis; `.drone.yml` → Drone; none → ask |

**Competing release tools** — check `package.json` deps and config files:
Expand All @@ -48,6 +51,7 @@ Trigger phrases: `'add changesets'`, `'set up releases'`, `'configure versioning
| Condition | Action |
|---|---|
| `.changeset/` already exists | Skip Step 2 init; audit config/scripts/CI only |
| CLI major ≠ action major, or CLI still v2 | Read `references/migration/cli-v3-upgrade.md` and upgrade — see the pairing table in Step 5. Do not re-run init |
| Competing tool detected | Ask: migrate to changesets? **No** → stop. **Yes** → read only the matching `references/migration/<tool>.md` file(s), apply removal, then continue |
| Non-GitHub CI and user expects a Version Packages PR | Explain that pattern is GitHub-only; proceed with `references/ci/_common.md` or stop |

Expand All @@ -65,13 +69,13 @@ Skip if `.changeset/` already exists.

```bash
# pnpm
pnpm dlx @changesets/cli init
pnpm dlx @changesets/cli@3 init

# bun
bunx @changesets/cli init
bunx @changesets/cli@3 init

# npm / yarn
npx @changesets/cli init
npx @changesets/cli@3 init
```

Creates `.changeset/config.json` and `.changeset/README.md`.
Expand All @@ -84,7 +88,7 @@ Replace the generated config. Set `baseBranch` to the repo's default branch if n

```json
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"$schema": "https://unpkg.com/@changesets/config@4.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"access": "public",
Expand All @@ -96,7 +100,7 @@ Replace the generated config. Set `baseBranch` to the repo's default branch if n

```json
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"$schema": "https://unpkg.com/@changesets/config@4.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"access": "public",
Expand Down Expand Up @@ -132,6 +136,24 @@ If a build must run before publish: `"release": "<pm> build && changeset publish

### Step 5 — CI release workflow

#### The CLI and the action must match majors

`changesets/action` refuses a mismatched CLI:

```
Error: This version of the Changesets action is designed to work with Changesets CLI v3.
Changesets CLI v2 is not supported; use Changesets action v1 instead, which is compatible with CLI v2.
```

| `@changesets/cli` | `changesets/action` | Input names |
|---|---|---|
| v2 | `@v1` | `version:`, `publish:`, `commit:`, `GITHUB_TOKEN` env var |
| v3 | `@v2` | `version-script:`, `publish-script:`, `commit-message:`, `github-token:` input |

The renamed inputs are **silently ignored** under the wrong major, so a half-done bump leaves the action running with no publish script. Change both sides in one commit.

New repos get **CLI v3 + action v2**. Staying on v2 is not a safe hold: npm 12 changed `npm info --json` to emit an array, and CLI 2.x reads `.versions` off the parsed object — it comes back `undefined`, every version looks unpublished, and the release republishes into npm's E403.

#### GitHub Actions — Option A (inline)

Create `.github/workflows/release.yml`:
Expand Down Expand Up @@ -163,12 +185,12 @@ jobs:
- run: <build-command> # remove if no build step

- name: Create Release PR or Publish
uses: changesets/action@v1
uses: changesets/action@v2
with:
version: <pm> run version
publish: <pm> run release
version-script: <pm> run version
publish-script: <pm> run release
github-token: ${{ secrets.GITHUB_TOKEN }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
```

Expand Down Expand Up @@ -202,7 +224,7 @@ npm/yarn:
cache: npm # or: yarn
```

**Token note:** `GITHUB_TOKEN` is enough for most repos. If branch protection requires CI on the **Version Packages** PR, use a PAT with `repo` scope as `RELEASE_TOKEN` and pass `GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}`.
**Token note:** `GITHUB_TOKEN` is enough for most repos. If branch protection requires CI on the **Version Packages** PR, use a PAT with `repo` scope as `RELEASE_TOKEN` and pass it as the action's `github-token:` input.

#### GitHub Actions — Option B (shared workflow)

Expand Down Expand Up @@ -240,12 +262,12 @@ jobs:
# Add package manager setup, install, build
- name: Create Release PR or Publish
id: changesets
uses: changesets/action@v1
uses: changesets/action@v2
with:
version: <pm> run version
publish: <pm> run release
version-script: <pm> run version
publish-script: <pm> run release
github-token: ${{ secrets.GITHUB_TOKEN }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
```

Expand All @@ -263,6 +285,8 @@ jobs:
secrets: inherit
```

**A shared workflow makes the action version a fleet-wide decision.** Bumping `changesets/action` here breaks the release job in every consumer still on the old CLI, and the failure surfaces in *their* repos, not this one — each on its next push to `main`, not at merge time. Bump the consumers' CLI in the same pass.

#### Other CI platforms

Read `references/ci/_common.md` for the non-GitHub pattern, then the platform file:
Expand All @@ -284,10 +308,25 @@ Read `references/ci/_common.md` for the non-GitHub pattern, then the platform fi

### Step 7 — Verify

Assert the pairing first — the one check that catches a setup which looks complete and fails on its first release:

```bash
jq -r '.devDependencies["@changesets/cli"] // .dependencies["@changesets/cli"]' package.json
grep -rn 'changesets/action@' .github/workflows/ # follow a `uses:` to the shared workflow first
```

`cli ^3` needs `action@v2` with the `-script` input names; `cli ^2` needs `action@v1`. A mismatch is the failure, whatever else is green.

```bash
npx changeset add --empty
ls .changeset/
gh workflow list # GitHub only
```

On an existing repo with pending changesets, confirm they still parse — this is what proves an upgrade did not strand a release:

```bash
<pm> exec changeset status
```

Tell the user to add future changesets via the **`add-changeset`** skill. Never manually edit `CHANGELOG.md` or version bumps — the Version Packages PR is fully generated.
45 changes: 45 additions & 0 deletions skills/setup-changesets/references/migration/cli-v3-upgrade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Upgrade Changesets CLI v2 → v3

Go forward. Pinning `changesets/action` back to `@v1` is a fallback only if a floor below blocks v3, and it leaves the repo exposed to the npm 12 republish bug (see the pairing table in SKILL.md Step 5).

1. **Check the floors** — the only things that can block the upgrade:

| Requirement | v3 floor |
|---|---|
| Node | `^22.11 \|\| ^24 \|\| >=26` |
| npm | `>=10.9.0` |
| pnpm | `>=10.0.0` |
| yarn | `>=4.5.2` — Yarn Classic support dropped entirely |

`"engines": { "node": ">=22" }` nominally allows 22.0, below the floor. Check what CI actually pins before widening it.

2. **Bump both pins** — `"@changesets/cli": "^3.0.0"` in `package.json`, and the `.changeset/config.json` `$schema` to `@changesets/config@4.0.0`.

3. **Bump the action to `@v2` and rename its inputs** (pairing table, SKILL.md Step 5). If the workflow is shared from `<org>/.github`, it is often already on v2 — that is what turned the repo red, and only the CLI side needs fixing.

4. **Walk the breaking changes against what the repo actually uses.** Most repos need no config migration beyond the schema URL; every common key (`changelog`, `commit`, `fixed`, `linked`, `access`, `baseBranch`, `updateInternalDependencies`, `ignore`) survives into config v4 unchanged.

| Change | What to do |
|---|---|
| `prettier` option removed | Replaced by `format`: `"auto"` (default, detects the project's formatter), `"prettier"`, `"oxfmt"`, `"deno"`, `"dprint"`, or `false`. Repos on biome or another unsupported formatter leave it at `auto` — it finds nothing and skips. |
| Private packages no longer versioned by default | Set `privatePackages: true` (or `{ version, tag }`) if you relied on bumps or tags for them. A private package already in `ignore` is unaffected. |
| `changeset tag` → `changeset git-tag` | Grep scripts and CI for `changeset tag`. |
| `--sinceMaster` removed from `changeset status` | Use `--since=main`. |
| `changeset version` exits 1 when no changesets | Breaks `changeset version && <next>` chains run outside the action. The action only invokes the version script when changesets exist. |
| `snapshot.useCalculatedVersion` | Replaces the removed `___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH.useCalculatedVersionForSnapshots`. |
| Prerelease changesets move to `.changeset/pre/` | `pre.json`'s `initialVersions` is gone. Only matters mid-prerelease — exit the prerelease first if you can. |
| Peer dependency bumps now `patch`, not `major` | Expect smaller bumps for packages with peer dependents. |
| Published as ESM | Fine for CLI use; matters only if something `require()`s changesets packages programmatically. |

5. **Grep for other consumers.** Scripts wrapping `changeset version`, or reading the versions it decides, run inside the same `version` script and break silently rather than loudly.

6. **Prove the pending changesets survive.** `changeset status` only parses them — run the real thing and throw it away:

```bash
<pm> exec changeset status # parses every pending changeset, prints the bumps
<pm> run version # full version + changelog + any wrapper scripts
git diff # confirm versions, changelog, derived files
git checkout -- .changeset packages
```

Commit the CLI bump, the schema URL, and the lockfile. A devDependency-only change needs no changeset of its own.