ci(release): derive release version from the tag, not the commit

The release job built whatever version package.json held at the tagged
commit — but main always carries a -develop suffix, so a vX.Y.Z bundle
would have reported X.Y.Z-develop. Make the tag the source of truth:
strip the suffix in the ephemeral CI checkout before building (never
committed), and fail fast when the tag base doesn't match package.json's
base (wrong-commit guard). Update CONTRIBUTING with the tag-driven flow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-25 16:08:24 +02:00
parent 664d0c02ba
commit d2f3e1ca27
2 changed files with 37 additions and 10 deletions
+18
View File
@@ -21,6 +21,24 @@ jobs:
- run: npm ci
# The tag is the source of truth for a release version. main always carries
# a `-develop` pre-release suffix, so strip it here (in the ephemeral CI
# checkout only — never committed) so the built bundle reports the bare
# X.Y.Z. Guard against tagging the wrong commit: the tag's base must match
# package.json's base version.
- name: Align package.json version to the tag
env:
TAG_NAME: ${{ github.ref_name }}
run: |
VERSION="${TAG_NAME#v}"
PKG_BASE="$(node -p 'require("./package.json").version.split("-")[0]')"
if [ "$VERSION" != "$PKG_BASE" ]; then
echo "Tag $TAG_NAME (base $VERSION) does not match package.json base ($PKG_BASE)." >&2
echo "Tag the commit whose package.json is ${VERSION}-develop." >&2
exit 1
fi
npm version "$VERSION" --no-git-tag-version --allow-same-version
- run: npm run build
- name: Locate bundled output
+19 -10
View File
@@ -75,21 +75,30 @@ Common types: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`.
## Releasing
The running version is read from `package.json` `version` and inlined at build
time (footer, `/health`, `/api/v1/stats`). Between releases the working tree
carries a `-develop` pre-release suffix so a dev build is never mistaken for a
shipped one — `0.3.0-develop` sorts _below_ `0.3.0` per SemVer, meaning "heading
toward 0.3.0, not yet released".
time (footer, `/health`, `/api/v1/stats`). `main` **always** carries a
`-develop` pre-release suffix (e.g. `0.3.0-develop`) so a dev build is never
mistaken for a shipped one — `0.3.0-develop` sorts _below_ `0.3.0` per SemVer,
meaning "heading toward 0.3.0, not yet released".
To cut a release `X.Y.Z`:
**The git tag is the source of truth for a release version**, not a commit on
`main`. The Release workflow (`.github/workflows/release.yml`) triggers on a
`v*` tag, strips the `-develop` suffix in its ephemeral checkout so the published
bundle reports the bare `X.Y.Z`, then builds and creates the GitHub Release. It
fails fast if the tag's base doesn't match `package.json`'s base version, which
catches tagging the wrong commit. You never commit a bare `X.Y.Z` to `main`.
To cut release `X.Y.Z` (its base must equal `main`'s current `X.Y.Z-develop`):
```bash
npm version X.Y.Z --no-git-tag-version # drop the -develop suffix
# commit, tag vX.Y.Z, push, deploy (npm run deploy)
npm version X.Y+1.0-develop --no-git-tag-version # reopen the next cycle
git tag vX.Y.Z && git push origin vX.Y.Z # the workflow aligns + builds + publishes
```
So `main` should always read `*-develop`; only a tagged release commit carries a
bare `X.Y.Z`.
Then reopen the next cycle on `main`:
```bash
npm version <next>-develop --no-git-tag-version # e.g. 0.4.0-develop (or 0.3.1-develop for a patch line)
# commit + push
```
## Reporting bugs and requesting features