chore(scripts): harden release.sh against double-release and inline version bumps

Two new pre-flight guards in scripts/release.sh, evaluated right after the
branch check:

1. Reject if HEAD subject matches `(X.Y.Z)` — historical pattern where the
   feature commit itself bumped the version (e.g. `feat(...) (0.9.14)`).
   Forces every release to land in a dedicated `chore(release): X.Y.Z`
   commit so the changelog + tag point at a clean release boundary.

2. Reject if HEAD is already `chore(release): …` — prevents re-running the
   script with no new commits since the previous release (would otherwise
   produce an empty release on top of itself).

Scope deliberately `chore(scripts)` (not `chore(release)`) so this very
commit doesn't trip guard 2 the next time release.sh runs.
This commit is contained in:
Deivid Soto 2026-05-27 16:37:03 +02:00
parent 116a348670
commit fceadd2009

View file

@ -55,6 +55,17 @@ fi
CURRENT_BRANCH=$(git branch --show-current)
[ "$CURRENT_BRANCH" = "main" ] || warn "Not on main branch (current: $CURRENT_BRANCH)"
HEAD_SUBJECT=$(git log -1 --pretty=%s)
if [[ "$HEAD_SUBJECT" =~ \(([0-9]+\.[0-9]+\.[0-9]+)\) ]]; then
die "HEAD commit subject contains inline version bump: \"$HEAD_SUBJECT\"
Release contract: version bumps MUST live in a dedicated 'chore(release): X.Y.Z' commit.
Revert the inline bump and re-run this script — it will create the proper commit."
fi
if [[ "$HEAD_SUBJECT" =~ ^chore\(release\): ]]; then
die "HEAD is already a chore(release) commit: \"$HEAD_SUBJECT\"
Nothing new to release. Add commits since the last release or amend intentionally outside this script."
fi
# ── Resolve version ────────────────────────────────────────────────
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
LATEST_VERSION="${LATEST_TAG#v}"