Cutting a release

Releases are driven by git tags. Pushing a tag matching v* (e.g. v2.1.1) triggers .github/workflows/release.yml, which:

  1. Verifies the tag's core X.Y.Z matches package.json version.
  2. Builds the macOS (arm64 + x64) DMGs and the Windows installer.
  3. Publishes a GitHub Release with those assets.

If the tag and package.json disagree, the verify job fails with:

Tag 'vX.Y.Z' does not match package.json version '...'.

One-tap script

Use scripts/release.sh. It enforces the same rules as CI before anything is pushed, so you never ship a mismatched tag.

# Release the version already committed to main:
./scripts/release.sh

# Bump to the next minor (X.Y.Z -> X.(Y+1).0), commit + push main, then release:
./scripts/release.sh --bump

# Bump to an explicit version, then release:
./scripts/release.sh --bump 2.1.3

What it does:

The published GitHub Release body is changelog first (auto-generated from the PRs/commits since the previous tag, via the GitHub API) followed by the macOS/Windows install instructions — see the Build release notes step in release.yml.

If main is branch-protected and the bump push is rejected, open a PR for the bump, merge it, then re-run ./scripts/release.sh (no --bump).

What scripts/bump-version.mjs updates

node scripts/bump-version.mjs <X.Y.Z> keeps the version consistent across:

Fixing a mismatched tag

If a tag already points at the wrong commit (version drift), move it to the commit whose package.json matches, then force-push:

git tag -f vX.Y.Z <commit>
git push origin vX.Y.Z --force