1+ name : Update Stable Branch
2+
3+ on :
4+ pull_request :
5+ types : [closed]
6+ branches :
7+ - main
8+
9+ jobs :
10+ update-stable :
11+ if : github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'stable-release')
12+ runs-on : ubuntu-latest
13+ permissions :
14+ contents : write
15+ pull-requests : read
16+
17+ steps :
18+ - uses : actions/checkout@v4
19+ with :
20+ fetch-depth : 0
21+ token : ${{ secrets.GITHUB_TOKEN }}
22+
23+ - name : Configure Git
24+ run : |
25+ git config --global user.name 'github-actions[bot]'
26+ git config --global user.email 'github-actions[bot]@users.noreply.github.com'
27+
28+ - name : Setup Node.js
29+ uses : actions/setup-node@v4
30+ with :
31+ node-version : ' 20'
32+ cache : ' npm'
33+
34+ - name : Determine Version Bump
35+ id : version_bump
36+ run : |
37+ if [[ "${{ contains(github.event.pull_request.labels.*.name, 'major') }}" == "true" ]]; then
38+ echo "bump=major" >> $GITHUB_OUTPUT
39+ elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'minor') }}" == "true" ]]; then
40+ echo "bump=minor" >> $GITHUB_OUTPUT
41+ else
42+ echo "bump=patch" >> $GITHUB_OUTPUT
43+ fi
44+
45+ - name : Get Current Version
46+ id : current_version
47+ run : |
48+ CURRENT_VERSION=$(node -p "require('./package.json').version")
49+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
50+
51+ - name : Install semver
52+ run : npm install -g semver
53+
54+ - name : Bump Version
55+ id : bump_version
56+ run : |
57+ NEW_VERSION=$(semver -i ${{ steps.version_bump.outputs.bump }} ${{ steps.current_version.outputs.version }})
58+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
59+
60+ - name : Update Package.json
61+ run : |
62+ NEW_VERSION=${{ steps.bump_version.outputs.new_version }}
63+ npm version $NEW_VERSION --no-git-tag-version --allow-same-version
64+
65+ - name : Generate Changelog
66+ id : changelog
67+ run : |
68+ # Get the latest tag
69+ LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
70+
71+ # Start changelog file
72+ echo "# Release v${{ steps.bump_version.outputs.new_version }}" > changelog.md
73+ echo "" >> changelog.md
74+
75+ if [ -z "$LATEST_TAG" ]; then
76+ echo "### 🎉 First Release" >> changelog.md
77+ echo "" >> changelog.md
78+ COMPARE_BASE="$(git rev-list --max-parents=0 HEAD)"
79+ else
80+ echo "### 🔄 Changes since $LATEST_TAG" >> changelog.md
81+ echo "" >> changelog.md
82+ COMPARE_BASE="$LATEST_TAG"
83+ fi
84+
85+ # Function to extract conventional commit type
86+ get_commit_type() {
87+ if [[ $1 =~ ^feat:|^feature: ]]; then echo "✨ Features";
88+ elif [[ $1 =~ ^fix: ]]; then echo "🐛 Bug Fixes";
89+ elif [[ $1 =~ ^docs: ]]; then echo "📚 Documentation";
90+ elif [[ $1 =~ ^style: ]]; then echo "💎 Styles";
91+ elif [[ $1 =~ ^refactor: ]]; then echo "♻️ Code Refactoring";
92+ elif [[ $1 =~ ^perf: ]]; then echo "⚡️ Performance Improvements";
93+ elif [[ $1 =~ ^test: ]]; then echo "✅ Tests";
94+ elif [[ $1 =~ ^build: ]]; then echo "🛠️ Build System";
95+ elif [[ $1 =~ ^ci: ]]; then echo "⚙️ CI";
96+ elif [[ $1 =~ ^chore: ]]; then echo "🔧 Chores";
97+ else echo "🔍 Other Changes";
98+ fi
99+ }
100+
101+ # Generate categorized changelog
102+ declare -A CATEGORIES
103+ declare -A COMMITS_BY_CATEGORY
104+
105+ # Get commits since last tag or all commits if no tag exists
106+ while IFS= read -r commit_line; do
107+ HASH=$(echo "$commit_line" | cut -d'|' -f1)
108+ MSG=$(echo "$commit_line" | cut -d'|' -f2)
109+ PR_NUM=$(echo "$commit_line" | cut -d'|' -f3)
110+
111+ CATEGORY=$(get_commit_type "$MSG")
112+ CATEGORIES["$CATEGORY"]=1
113+
114+ # Format commit message with PR link if available
115+ if [ -n "$PR_NUM" ]; then
116+ COMMITS_BY_CATEGORY["$CATEGORY"]+="- ${MSG#*: } ([#$PR_NUM](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/$PR_NUM))"$'\n'
117+ else
118+ COMMITS_BY_CATEGORY["$CATEGORY"]+="- ${MSG#*: }"$'\n'
119+ fi
120+ done < <(git log "${COMPARE_BASE}..HEAD" --pretty=format:"%H|%s|%(trailers:key=PR-Number,valueonly)" --reverse)
121+
122+ # Write categorized commits to changelog
123+ for category in "✨ Features" "🐛 Bug Fixes" "📚 Documentation" "💎 Styles" "♻️ Code Refactoring" "⚡️ Performance Improvements" "✅ Tests" "🛠️ Build System" "⚙️ CI" "🔧 Chores" "🔍 Other Changes"; do
124+ if [ -n "${COMMITS_BY_CATEGORY[$category]}" ]; then
125+ echo "#### $category" >> changelog.md
126+ echo "" >> changelog.md
127+ echo "${COMMITS_BY_CATEGORY[$category]}" >> changelog.md
128+ echo "" >> changelog.md
129+ fi
130+ done
131+
132+ # Add compare link if not first release
133+ if [ -n "$LATEST_TAG" ]; then
134+ echo "**Full Changelog**: [\`$LATEST_TAG..v${{ steps.bump_version.outputs.new_version }}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/$LATEST_TAG...v${{ steps.bump_version.outputs.new_version }})" >> changelog.md
135+ fi
136+
137+ # Save changelog content for the release
138+ CHANGELOG_CONTENT=$(cat changelog.md)
139+ echo "content<<EOF" >> $GITHUB_OUTPUT
140+ echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
141+ echo "EOF" >> $GITHUB_OUTPUT
142+
143+ - name : Commit Version Update
144+ run : |
145+ git add package.json package-lock.json
146+ git commit -m "chore: bump version to ${{ steps.bump_version.outputs.new_version }}"
147+ git push origin main
148+
149+ - name : Update Stable Branch
150+ run : |
151+ # Ensure stable branch exists
152+ git checkout stable 2>/dev/null || git checkout -b stable
153+ git merge main --no-ff -m "chore: merge main into stable for version ${{ steps.bump_version.outputs.new_version }}"
154+ git push origin stable
155+
156+ - name : Create and Push Tag
157+ run : |
158+ VERSION="v${{ steps.bump_version.outputs.new_version }}"
159+ git tag -a "$VERSION" -m "Release $VERSION"
160+ git push origin "$VERSION"
161+
162+ - name : Create GitHub Release
163+ env :
164+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
165+ run : |
166+ VERSION="v${{ steps.bump_version.outputs.new_version }}"
167+ gh release create "$VERSION" \
168+ --title "Release $VERSION" \
169+ --notes "${{ steps.changelog.outputs.content }}" \
170+ --target stable
0 commit comments