forked from heavy-duty/box
feat(release): publish the tagged version's changelog section (#83)
On a bare X.Y.Z tag push (the 0.6.0 tag set the no-'v' precedent), release.yml asserts the tag names the tree's own VERSION — a mismatch fails loudly and creates NOTHING — then publishes the GitHub release with that version's CHANGELOG.md section as the body: the curated prose, not the generated PR list. No assets, because for a pure-bash tree GitHub's source tarball for the tag IS the package. The extraction lives in .github/scripts/release-notes.sh, a file of its own so test/release.sh drives the same code against fixtures and the real changelog — it refuses a missing or empty section, so a tag whose release ritual was skipped fails before anything is created. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
88db8cab04
commit
dd51b06f3a
2 changed files with 69 additions and 0 deletions
30
.github/scripts/release-notes.sh
vendored
Normal file
30
.github/scripts/release-notes.sh
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# release-notes.sh <version> [<changelog>] — print exactly <version>'s
|
||||
# section of the changelog: every line between its '## <version> — <date>'
|
||||
# header and the next '## '. This is what release.yml hands to
|
||||
# 'gh release create', so the release notes are the curated prose we wrote,
|
||||
# not the PR list GitHub would generate (#83). Fails loudly when the section
|
||||
# is missing or empty — a tag without its changelog section is a release
|
||||
# ritual skipped, and an empty release body would paper over it.
|
||||
#
|
||||
# A file of its own (not inlined in release.yml) so test/release.sh drives
|
||||
# the same extraction against fixtures and the real CHANGELOG.md.
|
||||
|
||||
ver="${1:-}"
|
||||
changelog="${2:-CHANGELOG.md}"
|
||||
[ -n "$ver" ] || { echo "usage: release-notes.sh <version> [<changelog>]" >&2; exit 2; }
|
||||
[ -f "$changelog" ] || { echo "release-notes: no such file: $changelog" >&2; exit 1; }
|
||||
|
||||
# $2 of a section header ('## 0.6.0 — 2026-07-18') is the bare version —
|
||||
# compared WHOLE, so 0.6.0 can never match a 0.6.0-rc1 section (or vice
|
||||
# versa), and no regex-escaping of dots. sed drops the blank padding under
|
||||
# the header; the command substitution eats the trailing blanks.
|
||||
notes="$(awk -v ver="$ver" '
|
||||
/^## / { grab = ($2 == ver); next }
|
||||
grab { print }
|
||||
' "$changelog" | sed '/./,$!d')"
|
||||
|
||||
[ -n "$notes" ] || { echo "release-notes: $changelog has no section for '$ver' — the release PR stamps the Unreleased section with version + date BEFORE the tag (#83)" >&2; exit 1; }
|
||||
printf '%s\n' "$notes"
|
||||
39
.github/workflows/release.yml
vendored
Normal file
39
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
name: release
|
||||
# The release publisher (#83), on a bare X.Y.Z tag push (the 0.6.0 tag set
|
||||
# the precedent — no 'v' prefix). Two facts, then one act: the tag must name
|
||||
# the tree's own VERSION (a mismatch fails loudly and creates NOTHING — a
|
||||
# wrong release is worse than a missing one), and the release body is that
|
||||
# version's CHANGELOG.md section (.github/scripts/release-notes.sh, shared
|
||||
# with test/release.sh) — the curated prose, not the generated PR list. No
|
||||
# assets are uploaded: for a pure-bash tree, GitHub's source tarball for the
|
||||
# tag IS the package, and install.sh downloads exactly that.
|
||||
on:
|
||||
push:
|
||||
tags: ["[0-9]*.[0-9]*.[0-9]*"]
|
||||
|
||||
permissions:
|
||||
contents: write # gh release create
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: the tag must name the tree's VERSION
|
||||
run: |
|
||||
ver="$(cat VERSION)"
|
||||
if [ "$GITHUB_REF_NAME" != "$ver" ]; then
|
||||
echo "tag '$GITHUB_REF_NAME' does not match VERSION '$ver' — creating nothing." >&2
|
||||
echo "A release is a PR, then a tag (#83): the release PR bumps VERSION and stamps the changelog; the tag goes on its MERGE commit. Delete this tag and re-tag the right commit." >&2
|
||||
exit 1
|
||||
fi
|
||||
- name: release notes — the version's own CHANGELOG.md section
|
||||
# release-notes.sh fails loudly on a missing/empty section, which
|
||||
# fails the release here — before anything is created.
|
||||
run: |
|
||||
bash .github/scripts/release-notes.sh "$GITHUB_REF_NAME" > "$RUNNER_TEMP/notes.md"
|
||||
cat "$RUNNER_TEMP/notes.md"
|
||||
- name: create the release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh release create "$GITHUB_REF_NAME" --verify-tag --title "$GITHUB_REF_NAME" --notes-file "$RUNNER_TEMP/notes.md"
|
||||
Loading…
Reference in a new issue