Skip to content

Commit f761515

Browse files
ejntaylorclaude
andcommitted
Add one-click release workflow to prevent tag/version drift
Releases were being tagged by hand, decoupled from the package.json bump, so tags could (and did) disagree with the published version — the Publish guard rejected v0.3.3 because package.json still said 0.2.11. Add a dispatch-driven Release workflow that runs npm version to bump, commit, tag, publish, and cut the GitHub release from one source of truth, making the tag/version mismatch structurally impossible. Document the flow (and the manual fallback rules) in RELEASING.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 0d6d672 commit f761515

2 files changed

Lines changed: 118 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Release
2+
3+
# One-click release: bumps package.json, tags, publishes to npm, and cuts the
4+
# GitHub release from a single source of truth. Because the tag is derived from
5+
# `npm version`, the tag can never disagree with package.json.
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
bump:
11+
description: "Semver bump (ignored if 'version' is set)"
12+
type: choice
13+
options:
14+
- patch
15+
- minor
16+
- major
17+
default: patch
18+
version:
19+
description: "Explicit version, e.g. 0.3.3 (overrides bump). Leave blank to use bump."
20+
type: string
21+
required: false
22+
23+
permissions:
24+
contents: write
25+
id-token: write
26+
27+
concurrency:
28+
group: release
29+
cancel-in-progress: false
30+
31+
jobs:
32+
release:
33+
name: Release ${{ inputs.version || inputs.bump }}
34+
runs-on: ubuntu-latest
35+
environment: npm
36+
37+
steps:
38+
- name: Checkout
39+
uses: actions/checkout@v4
40+
with:
41+
fetch-depth: 0
42+
43+
- name: Setup Node
44+
uses: actions/setup-node@v4
45+
with:
46+
node-version: 24.x
47+
registry-url: https://registry.npmjs.org
48+
cache: npm
49+
50+
- name: Install dependencies
51+
run: npm ci
52+
53+
- name: Typecheck
54+
run: npm run typecheck
55+
56+
- name: Test
57+
run: npm test
58+
59+
- name: Build
60+
run: npm run build
61+
62+
- name: Verify package contents
63+
run: npm pack --dry-run
64+
65+
- name: Configure git
66+
run: |
67+
git config user.name "github-actions[bot]"
68+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
69+
70+
- name: Bump version and tag
71+
id: bump
72+
run: |
73+
if [ -n "${{ inputs.version }}" ]; then
74+
NEW=$(npm version "${{ inputs.version }}" -m "Release v%s")
75+
else
76+
NEW=$(npm version "${{ inputs.bump }}" -m "Release v%s")
77+
fi
78+
echo "tag=$NEW" >> "$GITHUB_OUTPUT"
79+
echo "Bumped to $NEW"
80+
81+
- name: Push commit and tag
82+
run: git push origin HEAD --follow-tags
83+
84+
- name: Publish to npm
85+
run: npm publish --access public --provenance
86+
87+
- name: Create GitHub release
88+
env:
89+
GH_TOKEN: ${{ github.token }}
90+
run: gh release create "${{ steps.bump.outputs.tag }}" --generate-notes --title "${{ steps.bump.outputs.tag }}"

RELEASING.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Releasing
2+
3+
The version in `package.json` is the single source of truth. The published npm
4+
version, the git tag, and the GitHub release must all match it — the `Publish`
5+
workflow enforces this and will fail the release if they drift.
6+
7+
## Recommended: one-click release
8+
9+
Run the **Release** workflow (Actions → Release → Run workflow) on the branch you
10+
want to release from (normally `main`):
11+
12+
- **bump**: `patch` / `minor` / `major`, or
13+
- **version**: an explicit version like `0.3.3` (overrides `bump`).
14+
15+
The workflow bumps `package.json`, commits, tags, pushes, publishes to npm, and
16+
cuts the GitHub release — all from the same version, so the tag can never
17+
disagree with `package.json`.
18+
19+
## Manual release (fallback)
20+
21+
If you create a release/tag by hand in the GitHub UI, you **must** bump
22+
`package.json` to the matching version first and merge that commit into the
23+
branch you tag. Cutting `v0.3.3` while `package.json` still says `0.2.11` is
24+
exactly what the guard rejects.
25+
26+
1. `npm version 0.3.3` on the release branch (bumps + commits + tags).
27+
2. `git push origin HEAD --follow-tags`.
28+
3. Create the GitHub release from tag `v0.3.3` → the `Publish` workflow runs.

0 commit comments

Comments
 (0)