forked from heavy-duty/box
The 0.7.0 ceremony exposed the gap: the release PR merged with four approvals and nothing happened, correctly, because publishing hung off a separate, manual, silent-when-forgotten tag push — the worst failure shape, no error and no red X. The ship decision already lives in the release PR, so the merge now IS the release. release.yml grows a second door: pull_request closed on main, gated on merged == true AND the hand-set release label (read from the event payload — no extra permission). Four asserts, in order, each fail-loud and creating nothing: VERSION at the merge commit is non--dev; VERSION changed in this PR (merge vs first parent — the -dev interlock that kills a mislabeled ordinary PR); the version's CHANGELOG.md section extracts non-empty via the existing release-notes.sh; and no tag or release exists yet. Then, in the same job, it creates the tag ref at the merge commit via the API and publishes with gh release create --verify-tag. Same-job on purpose: a GITHUB_TOKEN-created tag triggers no workflows (GitHub's anti-recursion), so the tag door can never fire off it and double-publish, and the no-existing assert covers a manual tag racing the merge. The tag-push path stays step-for-step identical as the documented manual fallback and backfill, gated to the push event so a closed PR never runs it against a branch ref. CONTRIBUTING.md's Releases section now reads "the maintainer's merge IS the release", with the manual tag ritual kept as the fallback. test/release.sh grep-pins the merged+labeled gate, all four asserts, the same-job tag+publish, and that the tag-push trigger survives — in the same daemon-free, fail-closed style. Fixes #96 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| scripts | ||
| workflows | ||
| labeler.yml | ||