-
Notifications
You must be signed in to change notification settings - Fork 74
206 lines (174 loc) · 7.73 KB
/
generate-changelog.yml
File metadata and controls
206 lines (174 loc) · 7.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
name: Generate AI Changelog
on:
workflow_call:
inputs:
current_tag:
description: 'Current release tag (e.g., v11.2.0)'
required: true
type: string
previous_tag:
description: 'Previous tag to compare against (optional, auto-detected if not provided)'
required: false
type: string
default: ''
product_name:
description: 'Product name for the changelog'
required: false
type: string
default: 'UTMStack'
product_description:
description: 'Product description for context'
required: false
type: string
default: 'Unified Threat Management and SIEM Platform'
secrets:
OPENAI_API_KEY:
required: true
outputs:
changelog:
description: 'Generated changelog content'
value: ${{ jobs.generate-changelog.outputs.changelog }}
previous_tag:
description: 'The previous tag used for comparison'
value: ${{ jobs.generate-changelog.outputs.previous_tag }}
jobs:
generate-changelog:
name: Generate AI Changelog
runs-on: ubuntu-latest
outputs:
changelog: ${{ steps.generate-changelog.outputs.changelog }}
previous_tag: ${{ steps.get-tags.outputs.previous_tag }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get previous tag
id: get-tags
run: |
CURRENT_TAG="${{ inputs.current_tag }}"
echo "Current release tag: $CURRENT_TAG"
# Use provided previous_tag or auto-detect
if [ -n "${{ inputs.previous_tag }}" ]; then
PREVIOUS_TAG="${{ inputs.previous_tag }}"
echo "Using provided previous tag: $PREVIOUS_TAG"
else
# Get all tags sorted by version (newest first)
ALL_TAGS=$(git tag --sort=-v:refname)
FOUND_CURRENT=false
PREVIOUS_TAG=""
for tag in $ALL_TAGS; do
if [ "$FOUND_CURRENT" = true ]; then
PREVIOUS_TAG="$tag"
break
fi
if [ "$tag" = "$CURRENT_TAG" ]; then
FOUND_CURRENT=true
fi
done
if [ -z "$PREVIOUS_TAG" ]; then
# No previous tag found, get first commit
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD | head -1)
echo "No previous tag found, using first commit: $PREVIOUS_TAG"
fi
fi
echo "Previous tag/commit: $PREVIOUS_TAG"
echo "current_tag=$CURRENT_TAG" >> $GITHUB_OUTPUT
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
- name: Get commits between tags
id: get-commits
run: |
CURRENT_TAG="${{ steps.get-tags.outputs.current_tag }}"
PREVIOUS_TAG="${{ steps.get-tags.outputs.previous_tag }}"
echo "Getting commits between $PREVIOUS_TAG and $CURRENT_TAG"
# Get commit messages with hash, author, and message
COMMITS=$(git log ${PREVIOUS_TAG}..${CURRENT_TAG} --pretty=format:"- %h %s (%an)" --no-merges)
# Count commits
COMMIT_COUNT=$(git rev-list --count ${PREVIOUS_TAG}..${CURRENT_TAG} --no-merges)
echo "Found $COMMIT_COUNT commits"
# Save commits to file (to handle multiline)
echo "$COMMITS" > /tmp/commits.txt
# Also get changed files summary
CHANGED_FILES=$(git diff --stat ${PREVIOUS_TAG}..${CURRENT_TAG} | tail -1)
echo "changed_files=$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "commit_count=$COMMIT_COUNT" >> $GITHUB_OUTPUT
- name: Generate changelog with OpenAI
id: generate-changelog
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
PRODUCT_NAME: ${{ inputs.product_name }}
PRODUCT_DESCRIPTION: ${{ inputs.product_description }}
run: |
COMMITS=$(cat /tmp/commits.txt)
CURRENT_TAG="${{ steps.get-tags.outputs.current_tag }}"
PREVIOUS_TAG="${{ steps.get-tags.outputs.previous_tag }}"
COMMIT_COUNT="${{ steps.get-commits.outputs.commit_count }}"
CHANGED_FILES="${{ steps.get-commits.outputs.changed_files }}"
# Create the prompt
PROMPT="You are a product marketing writer creating release notes for end users of a software product.
Product: $PRODUCT_NAME - $PRODUCT_DESCRIPTION
Release: $CURRENT_TAG
Here are the commit messages from this release:
$COMMITS
Create user-friendly release notes in markdown format. This is for NON-TECHNICAL end users who want to know what's new and improved in the product.
IMPORTANT RULES:
1. ONLY include changes that DIRECTLY AFFECT END USERS - things they can see, use, or benefit from
2. COMPLETELY IGNORE internal/technical changes like:
- CI/CD, GitHub Actions, deployment pipelines
- Code refactoring, component restructuring
- Database migrations, backend infrastructure
- Internal API changes, gRPC, service communication
- Developer tooling, linting, formatting
- README updates, internal documentation
3. Write in simple, non-technical language
4. Focus on BENEFITS to the user, not implementation details
5. Group into these categories ONLY (skip empty categories):
- **What's New** - New features users can now use
- **Improved** - Enhancements to existing features
- **Fixed** - Bugs that were affecting users
6. Start with a brief 1-2 sentence summary of the release highlights
7. Use bullet points, be concise (one line per item)
8. Do NOT wrap output in markdown code blocks
9. Do NOT include commit hashes or author names
10. If most commits are internal/technical, just summarize with 'Minor improvements and bug fixes'
Write the release notes directly in markdown format, ready to be used as-is."
# Call OpenAI API
RESPONSE=$(curl -s https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "$(jq -n \
--arg prompt "$PROMPT" \
'{
model: "gpt-4o-mini",
messages: [
{role: "system", content: "You are a technical writer specializing in software changelogs."},
{role: "user", content: $prompt}
],
temperature: 0.3,
max_tokens: 2000
}')")
# Extract the changelog from response
CHANGELOG=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')
if [ -z "$CHANGELOG" ]; then
echo "Error: Failed to generate changelog"
echo "Response: $RESPONSE"
# Fallback to simple commit list
CHANGELOG="## What's Changed
$COMMITS
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${CURRENT_TAG}"
fi
# Add comparison link at the end
CHANGELOG="${CHANGELOG}
---
**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${CURRENT_TAG}"
# Save changelog to file
echo "$CHANGELOG" > /tmp/changelog.md
# Output changelog using multiline format
echo "changelog<<EOF" >> $GITHUB_OUTPUT
cat /tmp/changelog.md >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Output changelog preview
run: |
echo "## Generated Changelog Preview"
echo ""
cat /tmp/changelog.md