Skip to content

Commit 810b143

Browse files
authored
feat(release): auto-generate release notes from Conventional Commits (#78)
The release body was a static 'Installation + Binaries' template — every release looked identical with zero mention of what changed. Append a 'What's Changed' section generated from `git log <prev-tag>..<version>`, grouped by conventional-commit type (Features / Bug Fixes / Performance / Refactors / Docs / Tests / Chores / CI), with a Full Changelog compare link. Skipped on the first release (no prior tag to compare). Non-conventional commits are dropped — CLAUDE.md already enforces the convention so this should be rare, and silence is better than a misleading row. Codifies the pattern in docs/HARNESS.md so the next agent that goes to edit release notes manually finds the generator step instead.
1 parent 6045a5f commit 810b143

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ jobs:
180180
181181
- name: Write release notes
182182
run: |
183+
# Static template — installation + binaries.
183184
cat > release_notes.md << 'EOF'
184185
## Installation
185186
@@ -196,6 +197,35 @@ jobs:
196197
EOF
197198
sed -i 's/^ //' release_notes.md
198199
200+
# Auto-generated changelog from Conventional Commits since the
201+
# previous tag, grouped by type. Non-conventional commits are
202+
# intentionally dropped — they should be rare (CLAUDE.md enforces
203+
# the convention) and silence is better than a misleading row.
204+
# Skipped entirely on the first release (no prior tag).
205+
VERSION="${{ needs.detect-release-type.outputs.version }}"
206+
PREV_TAG=$(git describe --tags --abbrev=0 "${VERSION}^" 2>/dev/null || echo "")
207+
if [ -n "$PREV_TAG" ]; then
208+
{
209+
echo ""
210+
echo "## What's Changed"
211+
echo ""
212+
for entry in "feat:Features" "fix:Bug Fixes" "perf:Performance" "refactor:Refactors" "docs:Docs" "test:Tests" "chore:Chores" "ci:CI"; do
213+
prefix="${entry%%:*}"
214+
heading="${entry#*:}"
215+
lines=$(git log --pretty='format:- %s' "${PREV_TAG}..${VERSION}" | grep -E "^- ${prefix}(\(|:)" || true)
216+
if [ -n "$lines" ]; then
217+
echo "### ${heading}"
218+
echo ""
219+
printf '%s\n' "$lines"
220+
echo ""
221+
fi
222+
done
223+
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${VERSION}"
224+
} >> release_notes.md
225+
fi
226+
echo "----- generated release_notes.md -----"
227+
cat release_notes.md
228+
199229
- name: Create Release
200230
uses: softprops/action-gh-release@v2
201231
with:

docs/HARNESS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Three regulation categories:
5252
| Behav. | L5 destructive (real installs) | release tags, manual dispatch | `make test-destructive` |
5353
| Behav. | curl\|bash smoke (install.sh + mock server) | every PR | `.github/workflows/test.yml` `curl-bash-smoke` job |
5454
| Behav. | Auto-release sensor — tag + dispatch `release.yml` when unreleased commits trip a threshold (`>=5 feat/fix`, `>=7 days + new`, or any `fix:` for patch fast lane) | push to `main` | `.github/workflows/auto-release.yml` |
55+
| Behav. | Release notes — Conventional Commits since previous tag, grouped by type (Features / Bug Fixes / etc) + Full Changelog link, appended to the install-instructions template | tag push or `workflow_dispatch` | `.github/workflows/release.yml` (`Write release notes` step) |
5556
| Behav. | Old-CLI compat (previous release × current mock server) | every PR | `.github/workflows/test.yml` `cli-compat` job |
5657
| Feedfwd. | Agent conventions | every AI turn | `CLAUDE.md`, `AGENTS.md` |
5758
| Feedfwd. | Skills | model-loaded | `.claude/skills/*` |

0 commit comments

Comments
 (0)