-
Notifications
You must be signed in to change notification settings - Fork 101
Expand file tree
/
Copy pathgenerate_changelog.sh
More file actions
executable file
·168 lines (137 loc) · 5.03 KB
/
generate_changelog.sh
File metadata and controls
executable file
·168 lines (137 loc) · 5.03 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
#!/bin/bash
# Generate CHANGELOG.md from git commit history
# Follows Conventional Commits format
set -e
CHANGELOG_FILE="CHANGELOG.md"
TEMP_FILE="${CHANGELOG_FILE}.tmp"
# Function to generate changelog section for a range of commits
function generate_section_for_commits() {
local from_ref="$1"
local to_ref="$2"
# Build git log range
if [ -z "$to_ref" ]; then
local range="$from_ref"
else
local range="${to_ref}..${from_ref}"
fi
# Get commits and categorize them
local features=""
local fixes=""
local breaking=""
local others=""
# Process commits
while IFS= read -r commit; do
if [ -z "$commit" ]; then
continue
fi
local message=$(echo "$commit" | cut -d' ' -f2-)
local commit_hash=$(echo "$commit" | cut -d' ' -f1)
# Get repository URL from git remote
local repo_url=$(git remote get-url origin | sed 's/\.git$//' | sed 's/git@github\.com:/https:\/\/github\.com\//')
# Categorize commit based on conventional commit format
case "$message" in
feat*|feature*)
features="${features}- ${message} ([${commit_hash:0:7}](${repo_url}/commit/${commit_hash}))\n"
;;
fix*)
fixes="${fixes}- ${message} ([${commit_hash:0:7}](${repo_url}/commit/${commit_hash}))\n"
;;
*"BREAKING CHANGE"*|*"!"*)
breaking="${breaking}- ${message} ([${commit_hash:0:7}](${repo_url}/commit/${commit_hash}))\n"
;;
chore*|docs*|style*|refactor*|test*|build*|ci*)
others="${others}- ${message} ([${commit_hash:0:7}](${repo_url}/commit/${commit_hash}))\n"
;;
"Merge pull request"*)
# Extract PR info
local pr_info=$(echo "$message" | sed 's/Merge pull request #\([0-9]*\) from .*/PR #\1/')
others="${others}- ${pr_info}: ${message} ([${commit_hash:0:7}](${repo_url}/commit/${commit_hash}))\n"
;;
*)
others="${others}- ${message} ([${commit_hash:0:7}](${repo_url}/commit/${commit_hash}))\n"
;;
esac
done < <(git log --oneline --no-merges "$range" 2>/dev/null || true)
# Output categorized changes
local has_content=false
if [ -n "$breaking" ]; then
echo "### 💥 BREAKING CHANGES"
echo ""
echo -e "$breaking"
has_content=true
fi
if [ -n "$features" ]; then
echo "### ✨ Features"
echo ""
echo -e "$features"
has_content=true
fi
if [ -n "$fixes" ]; then
echo "### 🐛 Bug Fixes"
echo ""
echo -e "$fixes"
has_content=true
fi
if [ -n "$others" ]; then
echo "### 📝 Other Changes"
echo ""
echo -e "$others"
has_content=true
fi
if [ "$has_content" = false ]; then
echo "- No notable changes"
echo ""
fi
echo ""
}
# Get the current version from Cargo.toml
CURRENT_VERSION=$(grep "^version" Cargo.toml | sed 's/version = "\(.*\)"/\1/')
# Create header
cat > "$TEMP_FILE" << EOF
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
EOF
# Get all tags sorted by version (newest first)
TAGS=$(git tag -l --sort=-version:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+' | head -20)
# If no tags exist, show unreleased changes
if [ -z "$TAGS" ]; then
echo "## [Unreleased]" >> "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
generate_section_for_commits "HEAD" "" >> "$TEMP_FILE"
else
# Get latest tag
LATEST_TAG=$(echo "$TAGS" | head -1)
# Check if there are unreleased changes
UNRELEASED_COMMITS=$(git log --oneline "${LATEST_TAG}..HEAD" --pretty=format:"%s" | wc -l)
if [ "$UNRELEASED_COMMITS" -gt 0 ]; then
echo "## [Unreleased]" >> "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
generate_section_for_commits "HEAD" "$LATEST_TAG" >> "$TEMP_FILE"
fi
# Generate sections for each tag
PREV_TAG=""
TAGS_ARRAY=($TAGS)
for i in "${!TAGS_ARRAY[@]}"; do
TAG="${TAGS_ARRAY[$i]}"
TAG_DATE=$(git log -1 --format="%ai" "$TAG" | cut -d' ' -f1)
CLEAN_VERSION=$(echo "$TAG" | sed 's/^v//')
echo "## [$CLEAN_VERSION] - $TAG_DATE" >> "$TEMP_FILE"
echo "" >> "$TEMP_FILE"
# Get the next (older) tag for the range
if [ $((i + 1)) -lt ${#TAGS_ARRAY[@]} ]; then
PREV_TAG="${TAGS_ARRAY[$((i + 1))]}"
else
PREV_TAG=""
fi
generate_section_for_commits "$TAG" "$PREV_TAG" >> "$TEMP_FILE"
done
fi
# Replace the old changelog with the new one
mv "$TEMP_FILE" "$CHANGELOG_FILE"
echo "✅ CHANGELOG.md has been generated successfully!"
echo "📄 Preview:"
echo "----------------------------------------"
head -20 "$CHANGELOG_FILE"
echo "----------------------------------------"