Skip to content

Commit 9297d1b

Browse files
2 parents a20f167 + f903344 commit 9297d1b

2 files changed

Lines changed: 167 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
name: Update CHANGELOG on push to main
2+
on:
3+
push:
4+
branches:
5+
- main
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
update-changelog:
12+
# avoid running when the actor is the actions bot (prevents loop)
13+
if: github.actor != 'github-actions[bot]'
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout repository (full history)
17+
uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Update CHANGELOG
22+
# Explicitly use bash so bash-only features (declare -A, [[ =~ ]]) are supported.
23+
shell: bash
24+
env:
25+
BEFORE: ${{ github.event.before }}
26+
AFTER: ${{ github.sha }}
27+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28+
# Change this path if your changelog lives in your package folder:
29+
CHANGELOG_PATH: CHANGELOG.md
30+
run: |
31+
set -euo pipefail
32+
33+
BEFORE=${BEFORE:-}
34+
AFTER=${AFTER:-}
35+
CHANGELOG_FILE="${CHANGELOG_PATH:-CHANGELOG.md}"
36+
37+
# Compute range (handle initial commit)
38+
# GitHub uses an all-zero SHA for "before" on the initial push.
39+
# Use a POSIX-safe check for an all-zero value to be robust.
40+
if [ -z "$BEFORE" ] || printf '%s' "$BEFORE" | grep -Eq '^0+$'; then
41+
RANGE="$AFTER"
42+
else
43+
RANGE="$BEFORE..$AFTER"
44+
fi
45+
46+
# Collect commits, skip merges
47+
# Format: short-hash|||subject|||author
48+
raw_commits=$(git log --no-merges --pretty=format:'%h|||%s|||%an' "$RANGE" || true)
49+
50+
if [[ -z "$raw_commits" ]]; then
51+
echo "No commits to add to changelog."
52+
exit 0
53+
fi
54+
55+
declare -A sections
56+
sections["Added"]=""
57+
sections["Changed"]=""
58+
sections["Deprecated"]=""
59+
sections["Removed"]=""
60+
sections["Fixed"]=""
61+
sections["Security"]=""
62+
sections["Other"]=""
63+
64+
# Helper: map conventional commit type to Keep-a-Changelog section
65+
map_type_to_section() {
66+
local t="$1"
67+
case "$t" in
68+
feat) echo "Added" ;;
69+
fix) echo "Fixed" ;;
70+
perf) echo "Changed" ;;
71+
refactor) echo "Changed" ;;
72+
docs) echo "Changed" ;;
73+
chore) echo "Other" ;;
74+
style) echo "Other" ;;
75+
test) echo "Other" ;;
76+
deprecate|deprecated) echo "Deprecated" ;;
77+
remove|removed) echo "Removed" ;;
78+
security) echo "Security" ;;
79+
*) echo "Other" ;;
80+
esac
81+
}
82+
83+
# Build entries
84+
while IFS= read -r line; do
85+
# split components
86+
IFS='|||' read -r short subject author <<< "$line"
87+
88+
# try to parse conventional commit: type(scope)?: subject
89+
if [[ "$subject" =~ ^([a-zA-Z0-9_-]+)(\([^)]+\))?:[[:space:]]*(.*) ]]; then
90+
type="${BASH_REMATCH[1]}"
91+
desc="${BASH_REMATCH[3]}"
92+
section=$(map_type_to_section "$type")
93+
else
94+
desc="$subject"
95+
section="Other"
96+
fi
97+
98+
entry="- ${desc} — ${author} (${short})"
99+
sections["$section"]+="${entry}\n"
100+
done <<< "$raw_commits"
101+
102+
# Build the Unreleased block with only non-empty sections
103+
DATE=$(date -u +"%Y-%m-%d")
104+
new_block="## Unreleased — ${DATE}\n\n"
105+
106+
for sec in "Added" "Changed" "Deprecated" "Removed" "Fixed" "Security" "Other"; do
107+
if [[ -n "${sections[$sec]}" ]]; then
108+
new_block+=$(printf "### %s\n\n%s\n" "$sec" "$(echo -e "${sections[$sec]}" | sed '/^\s*$/d')")
109+
fi
110+
done
111+
112+
new_block="${new_block}\n"
113+
114+
# Create or prepend to CHANGELOG_FILE
115+
if [[ -f "$CHANGELOG_FILE" ]]; then
116+
tmp=$(mktemp)
117+
printf "%b\n" "$new_block" > "$tmp"
118+
cat "$CHANGELOG_FILE" >> "$tmp"
119+
mv "$tmp" "$CHANGELOG_FILE"
120+
else
121+
printf "%b\n" "$new_block" > "$CHANGELOG_FILE"
122+
fi
123+
124+
# Commit and push
125+
git config user.name "github-actions[bot]"
126+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
127+
128+
git add "$CHANGELOG_FILE"
129+
if git diff --staged --quiet; then
130+
echo "No changes to commit."
131+
exit 0
132+
fi
133+
134+
git commit -m "chore(changelog): update for push ${AFTER}"
135+
# If push is blocked by branch protection, this push will fail.
136+
git push origin HEAD:main

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
```markdown
2+
# Changelog
3+
4+
All notable changes to this project will be documented in this file.
5+
6+
The format is based on "Keep a Changelog" (https://keepachangelog.com/en/1.0.0/)
7+
and this project adheres to Semantic Versioning (https://semver.org/).
8+
9+
## [Unreleased]
10+
11+
### Added
12+
-
13+
14+
### Changed
15+
-
16+
17+
### Deprecated
18+
-
19+
20+
### Removed
21+
-
22+
23+
### Fixed
24+
-
25+
26+
### Security
27+
-
28+
29+
## [0.0.0] - YYYY-MM-DD
30+
- Initial empty changelog.
31+
```

0 commit comments

Comments
 (0)