This repository was archived by the owner on Jan 26, 2026. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 0
167 lines (142 loc) · 6.3 KB
/
main.yml
File metadata and controls
167 lines (142 loc) · 6.3 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
name: Create Release from CHANGELOG
on:
push:
branches: [main]
paths:
- "CHANGELOG.md" # Only trigger when CHANGELOG.md is updated
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history to get commit messages
- name: Extract latest version and notes
id: changelog
run: |
# Extract the first version number from CHANGELOG.md
VERSION=$(grep -m 1 '^## v' CHANGELOG.md | sed 's/^## \(v[0-9.]*[0-9]\(-[a-zA-Z0-9]*\)*\).*/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT
# Extract notes for this version (everything between this version header and the next version header)
NOTES=$(awk -v ver="$VERSION" '
BEGIN { found=0; capture=0; notes=""; }
$0 ~ "^## " ver { found=1; capture=1; next; }
$0 ~ /^## v/ && capture==1 { capture=0; }
capture==1 { notes = notes $0 "\n"; }
END { print notes; }
' CHANGELOG.md)
# Get recent commits since last tag with GitHub usernames
PREVIOUS_TAG=$(git describe --tags --abbrev=0 --match "v*" 2>/dev/null || echo "")
# Function to get GitHub username from email
get_github_username() {
local email="$1"
local commit_hash="$2"
# First try to extract username from GitHub noreply email
if [[ "$email" =~ ([0-9]+\+)?([^@]+)@users\.noreply\.github\.com ]]; then
echo "${BASH_REMATCH[2]}"
return
fi
# Try to get GitHub username via API using commit hash
local github_user=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$GITHUB_REPOSITORY/commits/$commit_hash" | \
jq -r '.author.login // empty' 2>/dev/null)
if [ -n "$github_user" ] && [ "$github_user" != "null" ]; then
echo "$github_user"
return
fi
# Fallback: try to get user by email via API
local api_user=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/search/users?q=$email+in:email" | \
jq -r '.items[0].login // empty' 2>/dev/null)
if [ -n "$api_user" ] && [ "$api_user" != "null" ]; then
echo "$api_user"
else
# Final fallback: use the part before @ in email
echo "${email%%@*}"
fi
}
# Get ALL conventional commits, excluding merge commits to avoid duplicates
if [ -z "$PREVIOUS_TAG" ]; then
COMMIT_DATA=$(git log --pretty=format:"%H|%ae|%s" --no-merges --grep="^\(feat\|fix\|docs\|style\|refactor\|perf\|test\|build\|ci\|chore\|revert\)")
else
COMMIT_DATA=$(git log ${PREVIOUS_TAG}..HEAD --pretty=format:"%H|%ae|%s" --no-merges --grep="^\(feat\|fix\|docs\|style\|refactor\|perf\|test\|build\|ci\|chore\|revert\)")
fi
# Initialize categorized commit arrays
FEATURES=""
BUGFIXES=""
OTHER_COMMITS=""
# Process ALL matching commits and categorize them
while IFS='|' read -r hash email subject; do
[ -z "$hash" ] && continue
# Get full commit message for PR detection
message=$(git show -s --format=%B $hash)
# Extract username
username=$(get_github_username "$email" "$hash")
# Check for ANY PR reference (#number)
if [[ "$message" =~ \#([0-9]+) ]]; then
pr_num=" (#${BASH_REMATCH[1]})"
else
pr_num=""
fi
# Categorize based on conventional commit type
if [[ "$subject" =~ ^feat(\(.+\))?:* ]]; then
FEATURES="${FEATURES} - ${subject}${pr_num} (@$username)\n"
elif [[ "$subject" =~ ^fix(\(.+\))?:* ]]; then
BUGFIXES="${BUGFIXES} - ${subject}${pr_num} (@$username)\n"
else
OTHER_COMMITS="${OTHER_COMMITS} - ${subject}${pr_num} (@$username)\n"
fi
done <<< "$COMMIT_DATA"
# Build categorized commits section
COMMITS=""
if [ -n "$FEATURES" ]; then
COMMITS="${COMMITS}#### 🚀 Features\n${FEATURES}\n"
fi
if [ -n "$BUGFIXES" ]; then
COMMITS="${COMMITS}#### 🐛 Bug Fixes\n${BUGFIXES}\n"
fi
if [ -n "$OTHER_COMMITS" ]; then
COMMITS="${COMMITS}#### 📝 Other Commits\n${OTHER_COMMITS}\n"
fi
# Combine CHANGELOG notes with categorized commit messages
if [ -n "$COMMITS" ]; then
FULL_NOTES="${NOTES}\n\n### Commits\n${COMMITS}"
else
FULL_NOTES="${NOTES}"
fi
# Save notes to output with correct GitHub multiline syntax
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "notes<<$EOF" >> $GITHUB_OUTPUT
echo -e "$FULL_NOTES" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
# For debugging
echo "Found version: $VERSION"
echo "Release notes excerpt: $(echo "$NOTES" | head -3)..."
echo "Commit messages excerpt: $(echo "$COMMITS" | head -3)..."
- name: Check for existing release
id: check_release
run: |
VERSION=${{ steps.changelog.outputs.version }}
if gh release view $VERSION &>/dev/null; then
echo "Release already exists: $VERSION"
echo "exists=true" >> $GITHUB_OUTPUT
else
echo "No existing release found for: $VERSION"
echo "exists=false" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
if: steps.check_release.outputs.exists == 'false'
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.changelog.outputs.version }}
name: "Release ${{ steps.changelog.outputs.version }}"
body: ${{ steps.changelog.outputs.notes }}
draft: false
prerelease: ${{ contains(steps.changelog.outputs.version, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}