Skip to content

Commit 07b8a4c

Browse files
automate including changelog content in release workflow (#368)
GitOrigin-RevId: 81ea71f266b5cbf42b6de5aa8759a46f9d790c0c
1 parent d082172 commit 07b8a4c

4 files changed

Lines changed: 175 additions & 1 deletion

File tree

.github/workflows/release.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,23 @@ jobs:
3030
with:
3131
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
3232
passphrase: ${{ secrets.PASSPHRASE }}
33+
- name: Extract release notes from CHANGELOG.md
34+
id: release_notes
35+
run: |
36+
VERSION="${GITHUB_REF_NAME#v}"
37+
if bin/extract-release-notes.sh "$VERSION" CHANGELOG.md > release-notes.md; then
38+
printf '\n---\n**Full Changelog:** [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md)\n' >> release-notes.md
39+
echo "found=true" >> "$GITHUB_OUTPUT"
40+
else
41+
echo "::warning::No CHANGELOG.md entry found for ${VERSION}. Release will be created without notes."
42+
echo "found=false" >> "$GITHUB_OUTPUT"
43+
fi
3344
- name: Run GoReleaser
3445
uses: goreleaser/goreleaser-action@5742e2a039330cbb23ebf35f046f814d4c6ff811 # v5.1.0
3546
with:
36-
args: release --clean
47+
args: >-
48+
release --clean
49+
${{ steps.release_notes.outputs.found == 'true' && '--release-notes=release-notes.md' || '' }}
3750
env:
3851
# GitHub sets the GITHUB_TOKEN secret automatically.
3952
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.goreleaser.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ archives:
3131
checksum:
3232
name_template: '{{ .ProjectName }}_{{ .Version }}_SHA256SUMS'
3333
algorithm: sha256
34+
release:
35+
name_template: "v{{ .Version }}"
36+
changelog:
37+
disable: true
3438
signs:
3539
- artifacts: checksum
3640
args:

bin/extract-release-notes.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/bin/sh
2+
# Extracts the release notes for a given version from CHANGELOG.md.
3+
# Usage: bin/extract-release-notes.sh <version> <changelog-file>
4+
# Example: bin/extract-release-notes.sh 2.15.0 CHANGELOG.md
5+
#
6+
# Prints the extracted notes to stdout. Exits non-zero if no entry is found.
7+
8+
set -e
9+
10+
VERSION="${1:?Usage: extract-release-notes.sh <version> <changelog-file>}"
11+
CHANGELOG="${2:?Usage: extract-release-notes.sh <version> <changelog-file>}"
12+
13+
if [ ! -f "$CHANGELOG" ]; then
14+
echo "Error: file not found: $CHANGELOG" >&2
15+
exit 1
16+
fi
17+
18+
# Escape dots for regex (2.15.0 -> 2\.15\.0)
19+
VERSION_RE=$(echo "$VERSION" | sed 's/\./\\./g')
20+
21+
# Extract content between this version's heading and the next ## heading,
22+
# stripping leading and trailing blank lines.
23+
NOTES=$(awk "
24+
/^## \\[${VERSION_RE}\\]/ { found=1; next }
25+
/^## \\[/ { if (found) exit }
26+
found { lines[++n] = \$0 }
27+
END {
28+
# Trim leading blank lines
29+
start = 1
30+
while (start <= n && lines[start] ~ /^[[:space:]]*$/) start++
31+
# Trim trailing blank lines
32+
end = n
33+
while (end >= start && lines[end] ~ /^[[:space:]]*$/) end--
34+
for (i = start; i <= end; i++) print lines[i]
35+
}
36+
" "$CHANGELOG")
37+
38+
if [ -z "$(echo "$NOTES" | tr -d '[:space:]')" ]; then
39+
echo "Error: no CHANGELOG.md entry found for version ${VERSION}" >&2
40+
exit 1
41+
fi
42+
43+
printf '%s\n' "$NOTES"

bin/extract-release-notes_test.sh

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/bin/sh
2+
# Tests for bin/extract-release-notes.sh
3+
set -e
4+
5+
SCRIPT="$(dirname "$0")/extract-release-notes.sh"
6+
PASS=0
7+
FAIL=0
8+
9+
assert_eq() {
10+
test_name="$1"
11+
expected="$2"
12+
actual="$3"
13+
if [ "$expected" = "$actual" ]; then
14+
PASS=$((PASS + 1))
15+
else
16+
FAIL=$((FAIL + 1))
17+
echo "FAIL: $test_name" >&2
18+
echo " expected: $(echo "$expected" | head -3)" >&2
19+
echo " actual: $(echo "$actual" | head -3)" >&2
20+
fi
21+
}
22+
23+
assert_fails() {
24+
test_name="$1"
25+
shift
26+
if "$@" > /dev/null 2>&1; then
27+
FAIL=$((FAIL + 1))
28+
echo "FAIL: $test_name (expected non-zero exit)" >&2
29+
else
30+
PASS=$((PASS + 1))
31+
fi
32+
}
33+
34+
TMPDIR=$(mktemp -d)
35+
trap 'rm -rf "$TMPDIR"' EXIT
36+
37+
# --- Test: extracts correct version section ---
38+
cat > "$TMPDIR/changelog.md" << 'EOF'
39+
# Changelog
40+
41+
## [2.15.0] - 2026-03-23
42+
43+
### Added
44+
45+
- Feature A
46+
- Feature B
47+
48+
### Fixed
49+
50+
- Bug fix C
51+
52+
## [2.14.0] - 2026-03-13
53+
54+
### Added
55+
56+
- Feature D
57+
EOF
58+
59+
RESULT=$(sh "$SCRIPT" "2.15.0" "$TMPDIR/changelog.md")
60+
assert_eq "extracts correct version" "### Added
61+
62+
- Feature A
63+
- Feature B
64+
65+
### Fixed
66+
67+
- Bug fix C
68+
" "$RESULT
69+
"
70+
71+
# --- Test: extracts last version (no trailing heading) ---
72+
RESULT=$(sh "$SCRIPT" "2.14.0" "$TMPDIR/changelog.md")
73+
assert_eq "extracts last version" "### Added
74+
75+
- Feature D" "$RESULT"
76+
77+
# --- Test: fails on missing version ---
78+
assert_fails "missing version exits non-zero" sh "$SCRIPT" "9.9.9" "$TMPDIR/changelog.md"
79+
80+
# --- Test: fails on empty changelog ---
81+
cat > "$TMPDIR/empty.md" << 'EOF'
82+
# Changelog
83+
EOF
84+
assert_fails "empty changelog exits non-zero" sh "$SCRIPT" "1.0.0" "$TMPDIR/empty.md"
85+
86+
# --- Test: fails on missing file ---
87+
assert_fails "missing file exits non-zero" sh "$SCRIPT" "1.0.0" "$TMPDIR/nonexistent.md"
88+
89+
# --- Test: dots are literal, not regex wildcards ---
90+
cat > "$TMPDIR/tricky.md" << 'EOF'
91+
## [2X15Y0] - 2026-01-01
92+
93+
### Added
94+
95+
- Should not match
96+
97+
## [2.15.0] - 2026-03-23
98+
99+
### Added
100+
101+
- Correct match
102+
EOF
103+
104+
RESULT=$(sh "$SCRIPT" "2.15.0" "$TMPDIR/tricky.md")
105+
assert_eq "dots are literal" "### Added
106+
107+
- Correct match" "$RESULT"
108+
109+
# --- Test: no args ---
110+
assert_fails "no args exits non-zero" sh "$SCRIPT"
111+
112+
# --- Summary ---
113+
echo "${PASS} passed, ${FAIL} failed"
114+
[ "$FAIL" -eq 0 ]

0 commit comments

Comments
 (0)