Skip to content

Commit d211e42

Browse files
committed
ci: add changelog script
1 parent bd2ddbe commit d211e42

File tree

3 files changed

+98
-1
lines changed

3 files changed

+98
-1
lines changed

.github/workflows/release.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,20 @@ jobs:
2222
with:
2323
go-version: stable
2424

25+
- name: Generate release notes
26+
run: scripts/gen-changelog.sh > release-notes.md
27+
env:
28+
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
29+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
30+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
31+
2532
- name: Run GoReleaser
2633
uses: goreleaser/goreleaser-action@v7
2734
with:
2835
distribution: goreleaser
2936
version: "~> v2"
30-
args: release --clean
37+
args: >-
38+
release --clean
39+
${{ hashFiles('release-notes.md') != '' && '--release-notes=release-notes.md' || '' }}
3140
env:
3241
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.goreleaser.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,24 @@ checksum:
4646

4747
changelog:
4848
sort: asc
49+
groups:
50+
- title: "New Features"
51+
regexp: '^feat'
52+
- title: "Bug Fixes"
53+
regexp: '^fix'
54+
- title: "Performance"
55+
regexp: '^perf'
56+
- title: "Refactor"
57+
regexp: '^refactor'
58+
- title: "Others"
59+
order: 999
4960
filters:
5061
exclude:
5162
- "^docs:"
5263
- "^test:"
5364
- "^chore:"
65+
- "^ci:"
66+
- "^style:"
5467

5568
release:
5669
github:

scripts/gen-changelog.sh

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/bin/sh
2+
#
3+
# Generate AI-summarized release notes from git commits.
4+
# Usage: scripts/gen-changelog.sh [previous_tag]
5+
#
6+
# Requires GEMINI_API_KEY (preferred), ANTHROPIC_API_KEY, or OPENAI_API_KEY.
7+
# Falls back to raw commit list if no API key is set.
8+
#
9+
set -e
10+
11+
PREV_TAG="${1:-$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")}"
12+
CURR_TAG="$(git describe --tags --abbrev=0 HEAD 2>/dev/null || echo "HEAD")"
13+
14+
if [ -n "$PREV_TAG" ]; then
15+
COMMITS=$(git log "${PREV_TAG}..${CURR_TAG}" --pretty=format:"- %s" --no-merges)
16+
RANGE="${PREV_TAG}..${CURR_TAG}"
17+
else
18+
COMMITS=$(git log --pretty=format:"- %s" --no-merges -50)
19+
RANGE="last 50 commits"
20+
fi
21+
22+
if [ -z "$COMMITS" ]; then
23+
echo "No commits found in range ${RANGE}"
24+
exit 0
25+
fi
26+
27+
TMPDIR=$(mktemp -d)
28+
trap 'rm -rf "$TMPDIR"' EXIT
29+
30+
cat > "$TMPDIR/prompt.txt" <<PROMPT_EOF
31+
You are a release note writer for a Go CLI tool called 'codebot' (an AI coding agent).
32+
Given the following git commits, generate clean release notes in Markdown.
33+
34+
Rules:
35+
- Group by: Features, Bug Fixes, Performance, Other (skip empty groups)
36+
- Each item: one concise line, no commit hashes, no author names
37+
- Remove conventional commit prefixes (feat:, fix:, etc.)
38+
- Merge related commits into one entry
39+
- Use imperative mood (Add, Fix, Update)
40+
- Output ONLY the markdown, no intro text
41+
42+
Commits (${RANGE}):
43+
${COMMITS}
44+
PROMPT_EOF
45+
46+
# Build JSON body with jq (reads from file to handle special chars).
47+
build_body() { jq -Rs "$1" < "$TMPDIR/prompt.txt" > "$TMPDIR/body.json"; }
48+
49+
# Extract text from JSON response (python3 handles control chars reliably).
50+
extract() { python3 -c "import json,sys; d=json.load(open('$TMPDIR/result.json')); print($1)"; }
51+
52+
# Try Gemini first, then Anthropic, then OpenAI.
53+
if [ -n "$GEMINI_API_KEY" ]; then
54+
API_URL="${GEMINI_BASE_URL:-https://generativelanguage.googleapis.com}/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}"
55+
build_body '{contents: [{parts: [{text: .}]}]}'
56+
curl -fsSL "$API_URL" -H "content-type: application/json" -d @"$TMPDIR/body.json" -o "$TMPDIR/result.json"
57+
extract "d['candidates'][0]['content']['parts'][0]['text']"
58+
59+
elif [ -n "$ANTHROPIC_API_KEY" ]; then
60+
API_URL="${ANTHROPIC_BASE_URL:-https://api.anthropic.com}/v1/messages"
61+
build_body '{model: "claude-sonnet-4-5-20250514", max_tokens: 1024, messages: [{role: "user", content: .}]}'
62+
curl -fsSL "$API_URL" -H "x-api-key: ${ANTHROPIC_API_KEY}" -H "anthropic-version: 2023-06-01" -H "content-type: application/json" -d @"$TMPDIR/body.json" -o "$TMPDIR/result.json"
63+
extract "d['content'][0]['text']"
64+
65+
elif [ -n "$OPENAI_API_KEY" ]; then
66+
API_URL="${OPENAI_BASE_URL:-https://api.openai.com}/v1/chat/completions"
67+
build_body '{model: "gpt-4o-mini", messages: [{role: "user", content: .}]}'
68+
curl -fsSL "$API_URL" -H "Authorization: Bearer ${OPENAI_API_KEY}" -H "content-type: application/json" -d @"$TMPDIR/body.json" -o "$TMPDIR/result.json"
69+
extract "d['choices'][0]['message']['content']"
70+
71+
else
72+
echo "## What's Changed"
73+
echo ""
74+
echo "$COMMITS"
75+
fi

0 commit comments

Comments
 (0)