changesette
A version and changelog manager for single-package applications, using the same changeset file format as changesets and shipped as a single dependency-free Rust binary. The name is changeset + the diminutive suffix -ette (as in diskette).
changesette reads changeset files, bumps the version in package.json, and generates CHANGELOG.md. It never touches lockfiles; regenerating a lockfile such as package-lock.json is your package manager's job. changesette performs no git operations and no network access; commits, pull requests, tags, and releases belong to your workflows.
Install
GitHub Actions (verifies the build provenance of the downloaded archive; GitHub-hosted runners are assumed):
uses: iorate/changesette/setup@v1
Shell script (macOS / Linux):
|
PowerShell (Windows):
powershell -ExecutionPolicy Bypass -c "irm https://github.com/iorate/changesette/releases/latest/download/changesette-installer.ps1 | iex"
Homebrew:
npm:
Cargo (requires Rust 1.85+):
Example workflow
On every push to main, maintains a Version PR that applies the pending changesets; merging it creates a GitHub Release (and its tag) with the changelog section as the notes.
name: Version
on:
push:
branches:
- main
concurrency: version
jobs:
version:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: iorate/changesette/setup@v1
- id: version
run: |
next="$(changesette version)"
echo "next=$next" >> "$GITHUB_OUTPUT"
if [[ -n "$next" ]]; then
delim="$(openssl rand -hex 16)"
{
echo "changelog<<$delim"
changesette changelog "$next"
echo "$delim"
} >> "$GITHUB_OUTPUT"
npm install --package-lock-only # if you use npm
fi
- uses: peter-evans/create-pull-request@v8
with:
branch: changesette/release
commit-message: Release v${{ steps.version.outputs.next }}
title: Release v${{ steps.version.outputs.next }}
body: ${{ steps.version.outputs.changelog }}
delete-branch: true
- if: steps.version.outputs.next == ''
run: |
version="$(changesette current)"
if ! gh release view "v$version" > /dev/null 2>&1; then
gh release create "v$version" \
--target "$GITHUB_SHA" \
--notes "$(changesette changelog "$version")"
fi
env:
GH_TOKEN: ${{ github.token }}
CLI
changesette init
Creates the .changeset/ directory with a README.md. Does nothing if the directory already exists.
changesette [add] [--bump <major|minor|patch>] [--message <text>]
Creates a changeset file in .changeset/ and prints its path. --bump and --message have the short forms -b and -m. When run in a terminal, missing flags are prompted for interactively; submitting an empty summary opens your editor for a multi-line one.
changesette version [--dry-run]
Applies all pending changesets: bumps package.json, inserts the new section into CHANGELOG.md, and deletes the consumed changesets. Prints the next version, or nothing when there were no changesets and nothing was changed. --dry-run (short form -n) prints the plan to stderr without changing any files. Lockfiles are not updated; if you use npm, run npm install --package-lock-only afterwards.
changesette current
Prints the current version from package.json.
changesette changelog <version>
Prints the ## <version> section of CHANGELOG.md.
Differences from changesets
changesette shares the changeset file format with changesets, but is deliberately much smaller. Coming from changesets, expect the following:
- Single package only: no monorepo / workspace support.
- No configuration:
.changeset/config.jsonis not read, and there is nothing to configure. - No pre-release mode (
pre.json). - No
nonebump type and no empty changesets; both are errors. - Changelog entries are plain summaries: no auto-generated PR / commit / author links and no changelog plugins.
- No lockfile syncing, by design:
changesettewrites onlypackage.json,CHANGELOG.md, and the changeset files; updating derived files is the package manager's responsibility. If you use npm, runnpm install --package-lock-onlyafterchangesette version. - The CLI is not command-compatible with
changeset; only the changeset files are interchangeable.