release-tag · git:20260606.7d504a8 · 2026-06-06 · sha256 e73964f75654a627
release-tag git:20260606.7d504a8A
Immutable. This exact content is served forever at /api/v1/blob/e73964f75654a627.
--- name: release-tag description: Cut a release — determine the SemVer bump from history, update version files across any stack, refresh the changelog, create an annotated (optionally signed) Git tag, and push the release safely after pre-flight checks. Use when tagging a version, bumping the version, or preparing a release. argument-hint: "(optional) explicit version (e.g. 1.4.0) or bump level (major|minor|patch)" --- Take the repo from "ready" to a clean, tagged, pushed release. Releases are outward-facing and effectively irreversible once published — so every step is gated by pre-flight checks and explicit confirmation. ## Hard rules — never break these - **Never release a dirty or unverified tree.** Working tree clean, on the correct branch, up to date with the remote, and tests/CI green — all true before tagging. - **Tags are immutable once published.** Never move or overwrite a pushed tag; if a release is wrong, cut a new patch version. Force-retagging breaks everyone who already pulled. - **Follow SemVer honestly.** The bump must reflect what actually changed (see Step 2), not a convenient number. - **Confirm before the commit, the tag, and the push** — these are the irreversible, outward-facing actions. - **No AI/co-author trailers** in the release commit or tag message. - **Publishing is downstream.** Tagging ≠ publishing to a registry. Don't run `npm publish` / `cargo publish` / etc. unless explicitly asked — most pipelines publish from CI on tag. ## Step 1 — Pre-flight - `git status` → clean tree (or stash/commit deliberate changes first). - On the release branch (usually `main`/`master` or a `release/*` branch) and synced: `git fetch && git status` shows up to date. - Latest tag and history since it: `git describe --tags --abbrev=0`. - Tests, linters, and build pass locally; CI on the release commit is green. ## Step 2 — Determine the version Derive the SemVer bump from the commits since the last tag: - **MAJOR** — any breaking change (`!` marker, `BREAKING CHANGE:` footer, removed/incompatible API). - **MINOR** — new backward-compatible features (`feat`). - **PATCH** — backward-compatible fixes only (`fix`, `perf`). - Pre-1.0: minor/patch carry the instability caveat; respect the project's stated policy. - Honor pre-release / build identifiers if used (`1.4.0-rc.1`, `+build.5`) and any explicit version the user gives. ## Step 3 — Bump the version files (stack-aware) Update wherever the canonical version lives — detect by the manifests present: | Stack | Where / how | | ---------------- | ------------------------------------------------------------------------- | | Node / npm | `package.json` (+ workspace pkgs); `npm version <v> --no-git-tag-version`.| | Rust | `Cargo.toml` `[package] version` (+ `Cargo.lock`) | | Python | `pyproject.toml`, `setup.py`/`setup.cfg`, or `__version__`/`_version.py` | | Go | usually tag-driven (no file); else a `version` const / `VERSION` | | Java | `pom.xml` `<version>`, or `gradle.properties`/`build.gradle` | | .NET | `*.csproj` `<Version>`, `Directory.Build.props` | | PHP | `composer.json` `version` (often omitted in favor of tags) | | Ruby | `*.gemspec` / `lib/<gem>/version.rb` | | Generic | a top-level `VERSION` file | Keep lockfiles consistent. **Monorepos:** prefer the configured tool (changesets, Lerna, Nx release, Turborepo) and per-package versioning over hand edits. Use the native bump command when one exists, but suppress its auto-tag/commit so the steps below stay explicit and controlled. ## Step 4 — Update the changelog Refresh `CHANGELOG.md`/release notes for this version (delegate to the **changelog** skill if available): move `[Unreleased]` items under the new `## [X.Y.Z] - <date>` heading, call out breaking changes and migration steps. ## Step 5 — Commit, then tag (with confirmation) 1. Stage only the version + changelog files (explicit paths — never `git add -A`), and confirm. 2. Release commit: `chore(release): vX.Y.Z` (or the repo's convention). 3. Annotated, optionally signed tag — never lightweight: - `git tag -a vX.Y.Z -m "Release vX.Y.Z"` (add `-s` to sign if the project signs releases). - Match the existing tag prefix style (`v1.4.0` vs `1.4.0` — check `git tag`). 4. Sanity-check: `git show vX.Y.Z` points at the release commit and the message is right. ## Step 6 — Push (with confirmation) 1. Confirm branch + tag + remote. 2. Push the commit and the tag: `git push origin <branch>` then `git push origin vX.Y.Z` (or `--follow-tags`). Avoid `git push --tags` (it pushes *all* tags). 3. If CI builds/publishes/creates the release from the tag, say so and let it run; don't duplicate the publish manually. Report the released version, the tag, the commit it points to, and what (if anything) downstream automation will now do.