login
RepoCritics — Review. Share. Archive. Every open-source repo.

changesets/action

Wiki: changesets/action

Source: https://github.com/changesets/action

Last synced 2026-07-17 · 1149 words · Edit wiki on GitHub →

changesets/action

> GitHub Action that turns pending changeset files into an auto-maintained "Version Packages" PR — merge the PR to version, changelog, and publish your packages to npm.

GitHub repo · Official website · License: MIT

Overview

changesets/action is the CI half of the Changesets release workflow for JavaScript/TypeScript repos, especially multi-package monorepos. Contributors commit small markdown "changeset" files declaring intent ("bump @scope/pkg minor: added X"); this action, running on every push to the base branch, collects them into a single pull request with all version bumps and changelog entries applied. Merging that PR triggers the same action to publish to npm, push git tags, and create GitHub releases[^1]. The repo dates to August 2019 and remains actively maintained (pushed this week); at ~1,000 stars the count badly understates adoption — as a CI dependency it sits in the release pipelines of much of the JS monorepo ecosystem (pnpm, emotion, XState, and most Changesets-org-adjacent projects), where nobody stars the action itself.

The design's defining tension: it drives releases through GitHub's PR model using GitHub's own token, and GitHub deliberately prevents GITHUB_TOKEN-created commits and PRs from triggering other workflows. So the Version Packages PR the action opens gets no CI runs by default — the single most common operational surprise, with a well-worn workaround (PAT or GitHub App token) that shifts secret-management burden onto you.

Versioning is currently split: the v1 line (stable, maintenance/v1 branch) targets Changesets v2, while main is the in-progress v2 targeting Changesets v3, published only as v2.0.0-next.* prereleases as of mid-2026[^2]. The v2 rewrite renames all inputs to kebab-case (publishpublish-script, versionversion-script), so examples on the internet are split between two incompatible input schemas.

Getting Started

Requires an existing Changesets setup (npx changeset init creates .changeset/). Then add .github/workflows/release.yml:

name: Release
on:
  push:
    branches: [main]

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
  contents: write
  pull-requests: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v6
        with:
          node-version: 26
      - run: npm ci
      - name: Create Release PR or Publish
        id: changesets
        uses: changesets/action@v1
        with:
          publish: npx changeset publish   # v2 prerelease: `publish-script:`
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

The NPM_TOKEN must be able to publish without 2FA-on-publish (an npm automation/granular token)[^1].

Architecture / How It Works

The action is a TypeScript program with one branching decision, evaluated on every push to the base branch:

1. Changesets pending (.changeset/*.md files exist): run changeset version (or your version script), which consumes the changeset files, bumps package.json versions across the monorepo, and rewrites changelogs. The result is committed to a changeset-release/<branch> branch, force-pushed, and an open "Version Packages" PR is created or updated. The PR body aggregates the pending changelog entries. 2. No changesets pending and a publish script is configured: assume a Version Packages PR was just merged, run the publish script (conventionally changeset publish, which skips already-published versions), then push git tags and create GitHub releases for each published package, and expose published / published-packages outputs for downstream steps (notifications, deploys)[^1].

The action itself contains no versioning logic — semver math, changelog generation, and inter-package dependency bumping all live in the changesets CLI it shells out to. This keeps the action thin but couples its major versions to Changesets major versions (v1↔Changesets v2, v2↔Changesets v3)[^2].

Notable knobs: commit-mode: github-api routes commits/tags through the GitHub API instead of git CLI, producing GPG-signed commits attributed to the token owner (needed for orgs enforcing signed commits); pr-draft controls draft-PR behavior; the action writes an ~/.npmrc with NPM_TOKEN unless one already exists. Two sub-actions live in the same repo: pr-status (changeset status reporting in PRs) and pr-comment[^1].

Production Notes

The Version Packages PR gets no CI. With the default GITHUB_TOKEN, GitHub suppresses workflow triggers on the PR the action creates[^3]. Teams routinely merge release PRs that were never tested. Fixes: pass a PAT or GitHub App installation token as github-token, or trigger CI on push to changeset-release/**. Budget for token rotation either way.

Permissions. Under GitHub's restricted default token permissions you must grant contents: write and pull-requests: write, or the action fails with an unhelpful 403. commit-mode: github-api plus GitHub releases also needs the token to create releases.

Race conditions. Two pushes to main in quick succession can run the publish path concurrently. The README's concurrency: ${{ github.workflow }}-${{ github.ref }} line is not decoration — omit it and you will eventually see duplicate-publish errors. Relatedly, a changeset-free commit landing after a publish re-enters the publish branch; changeset publish tolerates this (skips existing versions), but custom publish scripts must be idempotent[^1].

Force-push semantics. The changeset-release/* branch is force-pushed on every base-branch push. Manual edits to the release PR branch (e.g., hand-tuning a changelog) are silently destroyed the next time anyone merges to main. Edit the changeset files on main instead.

Changelog generators need the token in env. @changesets/changelog-github (the common choice, links PRs/authors) reads GITHUB_TOKEN from the environment of the version step; forgetting it fails the version run.

Maintenance cadence. Release history shows long quiet stretches (one patch release in all of 2024's mid-year, v1.4.7→v1.4.9) followed by a marked 2026 revival: v1.6.0 through v1.9.0 shipped January–June 2026 alongside the v2 prerelease push[^2]. 120 open issues reflect a small-maintainer-team project carrying a large user base, not abandonment.

Pinning. @v1/@v2 are floating tags. Security-sensitive repos should pin to a commit SHA — this action handles your npm credentials.

When to Use / When Not

Use when:

  • You run a JS/TS monorepo publishing multiple interdependent npm packages and already use (or want) Changesets' contributor-authored release notes.
  • You want a human review gate (the Version Packages PR) between "code merged" and "version published".
  • You want batched releases: many merged PRs → one version PR → one publish.

Avoid when:

  • You want zero-touch per-commit releases from commit messages — semantic-release's model, no PR gate, no changeset files.
  • Your packages are not npm/JS: Changesets is JS-ecosystem-specific; release-please covers many languages.
  • You need immediate publishes; the PR-gate model adds a human merge step by design.
  • Your team won't reliably write changeset files — the whole pipeline silently does nothing without them (the pr-status sub-action or the changeset-bot mitigates this).

Alternatives

  • googleapis/release-please — same release-PR model, conventional-commit-driven and multi-language; use it outside the JS/Changesets ecosystem or when commit messages should drive versioning.
  • semantic-release/semantic-release — fully automated publish on every qualifying commit, no review gate; use when you want releases with zero human steps.
  • intuit/auto — label-driven releases from PR labels; use when PR metadata, not files or commit syntax, should decide bumps.
  • lerna/lerna — older monorepo versioning/publishing (lerna version/lerna publish); use for legacy Lerna repos not worth migrating.

History

| Version | Date | Notes | |---------|------|-------| | — | 2019-08 | Repo created; pre-1.0 usage via @master. | | v1.0.0 | 2021-11-25 | First tagged stable release[^2]. | | v1.4.0 | 2022-11-02 | v1 line matures; slow-cadence maintenance era begins. | | v1.4.9 | 2024-10-14 | One of only three releases across 2024 — quietest period. | | v1.5.0 | 2025-05-05 | Maintenance resumes; rapid 1.5.x patch series in May 2025. | | v1.6.0–v1.9.0 | 2026-01 → 2026-06 | Active 2026 feature cadence on the v1 line[^2]. | | v2.0.0-next.0 | 2026-06-23 | Changesets v3 compatibility; kebab-case input renames; v1 moves to maintenance/v1[^2]. |

References

[^1]: changesets/action README (main branch). https://github.com/changesets/action#readme [^2]: changesets/action releases (tag dates). https://github.com/changesets/action/releases [^3]: GitHub Docs, "Triggering a workflow from a workflow" — events from GITHUB_TOKEN do not create new workflow runs. https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow

Tags

github-actions, typescript, release-automation, versioning, changelog, monorepo, npm-publishing, ci-cd, semver, javascript