Skip to content

Commit 791973e

Browse files
authored
fix(ci): restore conventional commit categorization (#79)
1 parent 7dde060 commit 791973e

1 file changed

Lines changed: 97 additions & 51 deletions

File tree

.github/workflows/generate-changelog.yml

Lines changed: 97 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,43 @@ jobs:
3030
3131
echo "Previous tag: ${PREVIOUS_TAG:-'(none - first release)'}"
3232
33-
# Get commit SHAs since last release
33+
# Get commit data since last release
3434
if [ -z "$PREVIOUS_TAG" ]; then
35-
COMMIT_SHAS=$(git log --pretty=format:"%H" --no-merges)
35+
COMMIT_RANGE="HEAD"
3636
else
37-
COMMIT_SHAS=$(git log ${PREVIOUS_TAG}..HEAD --pretty=format:"%H" --no-merges)
37+
COMMIT_RANGE="${PREVIOUS_TAG}..HEAD"
3838
fi
3939
40-
echo "Found $(echo "$COMMIT_SHAS" | wc -l) commits since last release"
40+
# Collect highlights from PR comments and categorize commits
41+
# Each category stores entries with issue number prefix for sorting: "NNNN|entry"
42+
declare -a HIGHLIGHTS
43+
declare -a BUGS # fix
44+
declare -a IMPROVEMENTS # perf, refactor
45+
declare -a FEATURES # feat
46+
declare -a MAINTENANCE # docs, style, test, build, ci, chore
47+
declare -a OTHER # anything else (except revert which is omitted)
4148
42-
# Collect all closed issues by tracing commits -> PRs -> issues
43-
declare -A CLOSED_ISSUES # issue_number -> issue_title
44-
declare -a HIGHLIGHTS # array of highlight messages
49+
# Process each commit
50+
while IFS= read -r line; do
51+
[ -z "$line" ] && continue
52+
53+
SHA=$(echo "$line" | cut -d'|' -f1)
54+
SUBJECT=$(echo "$line" | cut -d'|' -f2)
55+
SHORT_SHA=$(echo "$line" | cut -d'|' -f3)
56+
BODY=$(echo "$line" | cut -d'|' -f4-)
4557
46-
for SHA in $COMMIT_SHAS; do
4758
echo "Processing commit: $SHA"
4859
49-
# Find PR associated with this commit (squash merge)
50-
PR_DATA=$(gh pr list --search "$SHA" --state merged --json number,body --limit 1 2>/dev/null || echo "[]")
60+
# Skip revert commits
61+
if echo "$SUBJECT" | grep -qE "^revert(\(|:)"; then
62+
echo " Skipping revert commit"
63+
continue
64+
fi
5165
66+
# Check for PR and /release-note comments
67+
PR_DATA=$(gh pr list --search "$SHA" --state merged --json number --limit 1 2>/dev/null || echo "[]")
5268
if [ "$PR_DATA" != "[]" ] && [ -n "$PR_DATA" ]; then
5369
PR_NUMBER=$(echo "$PR_DATA" | jq -r '.[0].number // empty')
54-
PR_BODY=$(echo "$PR_DATA" | jq -r '.[0].body // ""')
55-
5670
if [ -n "$PR_NUMBER" ]; then
5771
echo " Found PR #$PR_NUMBER"
5872
@@ -63,44 +77,44 @@ jobs:
6377
echo " Found /release-note: $RELEASE_NOTE"
6478
HIGHLIGHTS+=("$RELEASE_NOTE")
6579
fi
66-
67-
# Extract issue numbers from PR body (Fixes #XX, Closes #XX, Resolves #XX)
68-
ISSUE_NUMBERS=$(echo "$PR_BODY" | grep -oiE "(fixes|closes|resolves)\s*#[0-9]+" | grep -oE "[0-9]+" || echo "")
69-
70-
for ISSUE_NUM in $ISSUE_NUMBERS; do
71-
if [ -z "${CLOSED_ISSUES[$ISSUE_NUM]}" ]; then
72-
echo " Found linked issue #$ISSUE_NUM"
73-
74-
# Get issue title
75-
ISSUE_TITLE=$(gh issue view "$ISSUE_NUM" --json title --jq '.title' 2>/dev/null || echo "")
76-
77-
if [ -n "$ISSUE_TITLE" ]; then
78-
CLOSED_ISSUES[$ISSUE_NUM]="$ISSUE_TITLE"
79-
fi
80-
fi
81-
done
8280
fi
8381
fi
84-
done
8582
86-
# Also check for issues closed directly (not via PR) in the commit range
87-
# by looking at commit messages for "Fixes #XX" patterns
88-
for SHA in $COMMIT_SHAS; do
89-
COMMIT_MSG=$(git log -1 --pretty=format:"%B" "$SHA")
90-
ISSUE_NUMBERS=$(echo "$COMMIT_MSG" | grep -oiE "(fixes|closes|resolves)\s*#[0-9]+" | grep -oE "[0-9]+" || echo "")
83+
# Extract issue number from commit body for sorting
84+
ISSUE_NUM=$(echo "$BODY" | grep -oiE "(fixes|closes|resolves)\s*#[0-9]+" | grep -oE "[0-9]+" | head -1 || echo "99999")
85+
ISSUE_REF=$(echo "$BODY" | grep -oiE "(fixes|closes|resolves)\s*#[0-9]+" | head -1 || echo "")
9186
92-
for ISSUE_NUM in $ISSUE_NUMBERS; do
93-
if [ -z "${CLOSED_ISSUES[$ISSUE_NUM]}" ]; then
94-
echo "Found issue #$ISSUE_NUM in commit message"
87+
# Build commit entry
88+
if [ -n "$ISSUE_NUM" ] && [ "$ISSUE_NUM" != "99999" ]; then
89+
ENTRY="- #${ISSUE_NUM} - $SUBJECT ($SHORT_SHA)"
90+
else
91+
ENTRY="- $SUBJECT ($SHORT_SHA)"
92+
fi
9593
96-
ISSUE_TITLE=$(gh issue view "$ISSUE_NUM" --json title --jq '.title' 2>/dev/null || echo "")
94+
# Store with issue number prefix for sorting
95+
SORTABLE_ENTRY="${ISSUE_NUM}|${ENTRY}"
96+
97+
# Categorize by conventional commit prefix
98+
if echo "$SUBJECT" | grep -qE "^fix(\(|:)"; then
99+
BUGS+=("$SORTABLE_ENTRY")
100+
elif echo "$SUBJECT" | grep -qE "^(perf|refactor)(\(|:)"; then
101+
IMPROVEMENTS+=("$SORTABLE_ENTRY")
102+
elif echo "$SUBJECT" | grep -qE "^feat(\(|:)"; then
103+
FEATURES+=("$SORTABLE_ENTRY")
104+
elif echo "$SUBJECT" | grep -qE "^(docs|style|test|build|ci|chore)(\(|:)"; then
105+
MAINTENANCE+=("$SORTABLE_ENTRY")
106+
else
107+
OTHER+=("$SORTABLE_ENTRY")
108+
fi
109+
done < <(git log ${COMMIT_RANGE} --pretty=format:"%H|%s|%h|%b---END---" --no-merges | sed 's/---END---/\n/g')
97110
98-
if [ -n "$ISSUE_TITLE" ]; then
99-
CLOSED_ISSUES[$ISSUE_NUM]="$ISSUE_TITLE"
100-
fi
101-
fi
102-
done
103-
done
111+
# Helper function to sort entries by issue number and format output
112+
sort_entries() {
113+
local -n arr=$1
114+
if [ ${#arr[@]} -gt 0 ]; then
115+
printf '%s\n' "${arr[@]}" | sort -t'|' -k1 -n | cut -d'|' -f2-
116+
fi
117+
}
104118
105119
# Build changelog
106120
CHANGELOG=""
@@ -114,13 +128,45 @@ jobs:
114128
CHANGELOG="${CHANGELOG}"$'\n'
115129
fi
116130
117-
# Add Closed Issues section (always show if there are any)
118-
if [ ${#CLOSED_ISSUES[@]} -gt 0 ]; then
119-
CHANGELOG="${CHANGELOG}### 📋 Closed Issues"$'\n\n'
120-
# Sort issue numbers for consistent ordering
121-
for ISSUE_NUM in $(echo "${!CLOSED_ISSUES[@]}" | tr ' ' '\n' | sort -n); do
122-
CHANGELOG="${CHANGELOG}- #${ISSUE_NUM} - ${CLOSED_ISSUES[$ISSUE_NUM]}"$'\n'
123-
done
131+
# Add categorized sections in order: Bug Fixes, Performance & Improvements, New Features, Maintenance, Other
132+
if [ ${#BUGS[@]} -gt 0 ]; then
133+
CHANGELOG="${CHANGELOG}### 🐛 Bug Fixes"$'\n\n'
134+
while IFS= read -r entry; do
135+
CHANGELOG="${CHANGELOG}${entry}"$'\n'
136+
done < <(sort_entries BUGS)
137+
CHANGELOG="${CHANGELOG}"$'\n'
138+
fi
139+
140+
if [ ${#IMPROVEMENTS[@]} -gt 0 ]; then
141+
CHANGELOG="${CHANGELOG}### ⚡ Performance & Improvements"$'\n\n'
142+
while IFS= read -r entry; do
143+
CHANGELOG="${CHANGELOG}${entry}"$'\n'
144+
done < <(sort_entries IMPROVEMENTS)
145+
CHANGELOG="${CHANGELOG}"$'\n'
146+
fi
147+
148+
if [ ${#FEATURES[@]} -gt 0 ]; then
149+
CHANGELOG="${CHANGELOG}### 🎉 New Features"$'\n\n'
150+
while IFS= read -r entry; do
151+
CHANGELOG="${CHANGELOG}${entry}"$'\n'
152+
done < <(sort_entries FEATURES)
153+
CHANGELOG="${CHANGELOG}"$'\n'
154+
fi
155+
156+
if [ ${#MAINTENANCE[@]} -gt 0 ]; then
157+
CHANGELOG="${CHANGELOG}### 🔧 Maintenance"$'\n\n'
158+
while IFS= read -r entry; do
159+
CHANGELOG="${CHANGELOG}${entry}"$'\n'
160+
done < <(sort_entries MAINTENANCE)
161+
CHANGELOG="${CHANGELOG}"$'\n'
162+
fi
163+
164+
if [ ${#OTHER[@]} -gt 0 ]; then
165+
CHANGELOG="${CHANGELOG}### 📦 Other"$'\n\n'
166+
while IFS= read -r entry; do
167+
CHANGELOG="${CHANGELOG}${entry}"$'\n'
168+
done < <(sort_entries OTHER)
169+
CHANGELOG="${CHANGELOG}"$'\n'
124170
fi
125171
126172
# If changelog is empty, use a fun message

0 commit comments

Comments
 (0)