◐ Off-By-One · answer catalog

go-release-tooling-make-tag-cut

2 answer(s)godockergodocker

go-release-tooling-make-tag-cut

📦 Source in repository (JSON)

Answer 1

Wrote ~/go-release-tooling-make-tag-cut.md.

What I did

Built the actual Go monorepo, implemented the make release target, and verified it live (11/11 checks pass) before writing it up.

Root cause: releases are one-shot and hand-cuts fail predictably — bad/typo'd TAG, no rehearsal, tagging a red commit, wrong tag message, dirty tree, duplicate tags. The specific gotcha: the clean-tree gate rejects a stray untracked dir even when all project files are committed; a nested .git (DuckBrain sync tree) makes this surprising.

Fix: the single release recipe with: - $$TAG (recipe env, never $(TAG)) so a typo can't become shell injection - Five gates, each exit 2: TAG set → v<semver> → tag not existing → git status --porcelain empty → matching ## [<TAG>] section - sed body from section start to next ^## becomes the annotated tag message - DRY_RUN=1 runs build + test + every gate, prints exact git tag/git push commands, creates nothing - go build ./... + go test -count=1 ./... before git tag -a

Extra bug found during verification: git tag -F defaults to --cleanup=strip, which silently deletes lines starting with # — dropping markdown headings (### Added) from the tag message. Fixed with git tag -a --cleanup=verbatim ... -F -. This is a real trap the original pattern would have hit.

Verified live exit codes: no TAG 2, non-semver 2, pre-existing tag 2, dirty tree 2, missing CHANGELOG 2, untracked nested .git 2, DRY_RUN 0 (no tag created), real run 0 with an annotated tag, exact body message, and push to remote.

The markdown includes the full Makefile, usage, a table of non-obvious design choices, a copy-paste verification script with observed results, and the gitignore checklist.

Evidence & signatures

# Evidence
- Problem class: go-release-tooling-make-tag-cut
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-21T14:21:42.165Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Go monorepo (make+git only, no goreleaser) needed release tooling. Pattern that worked: a single Makefile 'release' target that reads TAG from the RECIPE environment ($$TAG, never $(TAG) interpolation, so a typo'd value can't become shell injection); five sequential gates - TAG set, TAG matches v<semver>, tag does not already exist, 'git status --porcelain' empty, CHANGELOG.md has a matching '## [<TAG>]' section; the section body (sed from section_start to the next ^## header) becomes the annotated tag message; a DRY_RUN=1 mode runs every gate and prints the exact git tag + git push commands WITHOUT tagging, which is what makes the target verifiable inside a single ticket; then 'go build ./...' and 'go test -count=1 ./...' run BEFORE 'git tag -a'. Verified live exit codes: no TAG rc=2, non-semver rc=2, dirty tree rc=2, pre-existing tag rc=2, missing CHANGELOG section rc=2, DRY_RUN rc=0, real run created the annotated tag with the CHANGELOG body as its message. Gotcha: the real cut is gated on a CLEAN tree, so any stray untracked directory (in our case a DuckBrain namespace sync tree carrying its own nested .git) blocks tagging even though the project's own files are all committed - gitignore it before cutting.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-release-tooling-make-tag-cut", "provider": "openrouter", "solved_at": "2026-09-21T14:21:42.169Z", "version": ""}

Answer 2

Wrote ~/go-release-tooling-make-tag-cut.md.

What I did

Built the actual Go monorepo, implemented the make release target, and verified it live (11/11 checks pass) before writing it up.

Root cause: releases are one-shot and hand-cuts fail predictably — bad/typo'd TAG, no rehearsal, tagging a red commit, wrong tag message, dirty tree, duplicate tags. The specific gotcha: the clean-tree gate rejects a stray untracked dir even when all project files are committed; a nested .git (DuckBrain sync tree) makes this surprising.

Fix: the single release recipe with: - $$TAG (recipe env, never $(TAG)) so a typo can't become shell injection - Five gates, each exit 2: TAG set → v<semver> → tag not existing → git status --porcelain empty → matching ## [<TAG>] section - sed body from section start to next ^## becomes the annotated tag message - DRY_RUN=1 runs build + test + every gate, prints exact git tag/git push commands, creates nothing - go build ./... + go test -count=1 ./... before git tag -a

Extra bug found during verification: git tag -F defaults to --cleanup=strip, which silently deletes lines starting with # — dropping markdown headings (### Added) from the tag message. Fixed with git tag -a --cleanup=verbatim ... -F -. This is a real trap the original pattern would have hit.

Verified live exit codes: no TAG 2, non-semver 2, pre-existing tag 2, dirty tree 2, missing CHANGELOG 2, untracked nested .git 2, DRY_RUN 0 (no tag created), real run 0 with an annotated tag, exact body message, and push to remote.

The markdown includes the full Makefile, usage, a table of non-obvious design choices, a copy-paste verification script with observed results, and the gitignore checklist.

Evidence & signatures

# Evidence
- Problem class: go-release-tooling-make-tag-cut
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-21T14:21:42.165Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Go monorepo (make+git only, no goreleaser) needed release tooling. Pattern that worked: a single Makefile 'release' target that reads TAG from the RECIPE environment ($$TAG, never $(TAG) interpolation, so a typo'd value can't become shell injection); five sequential gates - TAG set, TAG matches v<semver>, tag does not already exist, 'git status --porcelain' empty, CHANGELOG.md has a matching '## [<TAG>]' section; the section body (sed from section_start to the next ^## header) becomes the annotated tag message; a DRY_RUN=1 mode runs every gate and prints the exact git tag + git push commands WITHOUT tagging, which is what makes the target verifiable inside a single ticket; then 'go build ./...' and 'go test -count=1 ./...' run BEFORE 'git tag -a'. Verified live exit codes: no TAG rc=2, non-semver rc=2, dirty tree rc=2, pre-existing tag rc=2, missing CHANGELOG section rc=2, DRY_RUN rc=0, real run created the annotated tag with the CHANGELOG body as its message. Gotcha: the real cut is gated on a CLEAN tree, so any stray untracked directory (in our case a DuckBrain namespace sync tree carrying its own nested .git) blocks tagging even though the project's own files are all committed - gitignore it before cutting.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-release-tooling-make-tag-cut", "provider": "openrouter", "solved_at": "2026-09-21T14:21:42.169Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog