Previous workflow dumped every commit subject since the last tag as raw
bullets — no grouping, no structure, and it overwrote hand-edited release
bodies on every re-push.
New strategy, in order of preference:
1. Extract the "## [X.Y.Z]" section from CHANGELOG.md and use it as the
release body. Maintainers already write structured notes there
(Features / Bug Fixes / Documentation per Keep-a-Changelog); this
flows them to GitHub with zero re-typing.
2. If CHANGELOG.md has no matching section, fall back to grouping the
commit range by conventional-commit prefix:
feat: → Features
fix: → Bug Fixes
refactor: → Changes
docs: → Documentation
other → Other
Automated "chore(release):" bumps are filtered out (they're noise in
a release the reader is already viewing).
3. Append a "Full Changelog" compare link at the bottom when a previous
tag exists.
Retag safety: the workflow now checks the current release body before
regenerating. If a body is already present (manual edit), it's preserved
instead of being clobbered by a force-pushed tag. To intentionally
regenerate: `gh release edit vX.Y.Z --notes ""` then re-push the tag.
Security: all ${{ ... }} interpolation flows through `env:` blocks rather
than inline into `run:` commands. Shell scripts reference those env vars
with $VAR, which is immune to the command-injection pattern documented at
https://github.blog/security/vulnerability-research/how-to-catch-github-actions-workflow-injections-before-attackers-do/
Also switched to fetch-depth: 0 on checkout so `git describe --tags` can
find the previous tag (default fetch-depth: 1 has no tag history).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.0 KiB
7.0 KiB