Skip to content

Commit a698fea

Browse files
feat: add automated release workflow for module tags
Adds a GitHub Actions workflow that automatically creates releases when module tags are pushed. Features: - Triggers on release/<namespace>/<module>/v*.*.* tag pattern - Generates changelog filtered to only show changes for the specific module - Extracts PR numbers and GitHub usernames from commit messages - Creates GitHub release with proper title and changelog Example: pushing 'release/coder-labs/sourcegraph-amp/v1.0.1' will create a release with changelog showing only changes to the sourcegraph-amp module. Co-authored-by: matifali <[email protected]>
1 parent ac48f0d commit a698fea

File tree

1 file changed

+135
-0
lines changed

1 file changed

+135
-0
lines changed

.github/workflows/release.yml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: Create Release
2+
3+
on:
4+
push:
5+
tags:
6+
# Matches release/<namespace>/<resource_name>/<semantic_version>
7+
# (e.g., "release/coder-labs/sourcegraph-amp/v1.0.1")
8+
- "release/*/*/v*.*.*"
9+
10+
jobs:
11+
create-release:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: write
15+
pull-requests: read
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0 # Fetch full history for changelog generation
22+
23+
- name: Extract tag information
24+
id: tag_info
25+
run: |
26+
TAG=${GITHUB_REF#refs/tags/}
27+
echo "tag=$TAG" >> $GITHUB_OUTPUT
28+
29+
# Extract namespace, module, and version from tag
30+
# Format: release/namespace/module/version
31+
IFS='/' read -ra PARTS <<< "$TAG"
32+
NAMESPACE="${PARTS[1]}"
33+
MODULE="${PARTS[2]}"
34+
VERSION="${PARTS[3]}"
35+
36+
echo "namespace=$NAMESPACE" >> $GITHUB_OUTPUT
37+
echo "module=$MODULE" >> $GITHUB_OUTPUT
38+
echo "version=$VERSION" >> $GITHUB_OUTPUT
39+
echo "module_path=registry/$NAMESPACE/modules/$MODULE" >> $GITHUB_OUTPUT
40+
41+
# Create release title
42+
RELEASE_TITLE="$NAMESPACE/$MODULE $VERSION"
43+
echo "release_title=$RELEASE_TITLE" >> $GITHUB_OUTPUT
44+
45+
- name: Find previous tag
46+
id: prev_tag
47+
run: |
48+
NAMESPACE="${{ steps.tag_info.outputs.namespace }}"
49+
MODULE="${{ steps.tag_info.outputs.module }}"
50+
CURRENT_TAG="${{ steps.tag_info.outputs.tag }}"
51+
52+
# Find the previous tag for this specific module
53+
PREV_TAG=$(git tag -l "release/$NAMESPACE/$MODULE/v*" | grep -v "$CURRENT_TAG" | sort -V | tail -1)
54+
55+
if [ -z "$PREV_TAG" ]; then
56+
echo "No previous tag found, using initial commit"
57+
PREV_TAG=$(git rev-list --max-parents=0 HEAD)
58+
fi
59+
60+
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
61+
echo "Previous tag: $PREV_TAG"
62+
63+
- name: Generate changelog
64+
id: changelog
65+
run: |
66+
NAMESPACE="${{ steps.tag_info.outputs.namespace }}"
67+
MODULE="${{ steps.tag_info.outputs.module }}"
68+
MODULE_PATH="${{ steps.tag_info.outputs.module_path }}"
69+
PREV_TAG="${{ steps.prev_tag.outputs.prev_tag }}"
70+
CURRENT_TAG="${{ steps.tag_info.outputs.tag }}"
71+
72+
echo "Generating changelog for $MODULE_PATH between $PREV_TAG and $CURRENT_TAG"
73+
74+
# Get commits that affected the specific module path
75+
COMMITS=$(git log --oneline --no-merges "$PREV_TAG..$CURRENT_TAG" -- "$MODULE_PATH" | cut -d' ' -f1)
76+
77+
if [ -z "$COMMITS" ]; then
78+
echo "No commits found for this module"
79+
echo "changelog=No changes found for this module." >> $GITHUB_OUTPUT
80+
exit 0
81+
fi
82+
83+
# Generate changelog from commits
84+
CHANGELOG="## What's Changed\n\n"
85+
86+
for commit in $COMMITS; do
87+
# Get commit message
88+
COMMIT_MSG=$(git log --format="%s" -n 1 $commit)
89+
90+
# Extract PR number from commit message (handles various formats)
91+
PR_NUM=$(echo "$COMMIT_MSG" | grep -oE '\(#[0-9]+\)|#[0-9]+' | grep -oE '[0-9]+' | head -1 || echo "")
92+
93+
# Get GitHub username from commit (try multiple methods)
94+
# First try to get from commit trailer
95+
GH_USER=$(git log --format="%(trailers:key=Co-authored-by,valueonly)" -n 1 $commit | head -1 | grep -oE '@[^>]+' | cut -d'@' -f2 | cut -d'.' -f1 || echo "")
96+
97+
# If no co-author, try to extract from email
98+
if [ -z "$GH_USER" ]; then
99+
EMAIL=$(git log --format="%ae" -n 1 $commit)
100+
if [[ "$EMAIL" == *"@users.noreply.github.com" ]]; then
101+
GH_USER=$(echo "$EMAIL" | cut -d'@' -f1 | sed 's/^[0-9]*+//')
102+
else
103+
GH_USER=$(echo "$EMAIL" | cut -d'@' -f1)
104+
fi
105+
fi
106+
107+
# Fallback to commit author name if no GitHub username found
108+
if [ -z "$GH_USER" ]; then
109+
GH_USER=$(git log --format="%an" -n 1 $commit | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
110+
fi
111+
112+
# Format changelog entry
113+
if [ -n "$PR_NUM" ]; then
114+
CHANGELOG="${CHANGELOG}* $COMMIT_MSG by @$GH_USER in https://github.com/coder/registry/pull/$PR_NUM\n"
115+
else
116+
CHANGELOG="${CHANGELOG}* $COMMIT_MSG by @$GH_USER\n"
117+
fi
118+
done
119+
120+
# Add full changelog link
121+
CHANGELOG="${CHANGELOG}\n\n**Full Changelog**: https://github.com/coder/registry/compare/$PREV_TAG...$CURRENT_TAG"
122+
123+
# Save changelog to output (escape newlines for GitHub Actions)
124+
echo "changelog<<EOF" >> $GITHUB_OUTPUT
125+
echo -e "$CHANGELOG" >> $GITHUB_OUTPUT
126+
echo "EOF" >> $GITHUB_OUTPUT
127+
128+
- name: Create Release
129+
env:
130+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
131+
run: |
132+
gh release create "${{ steps.tag_info.outputs.tag }}" \
133+
--title "${{ steps.tag_info.outputs.release_title }}" \
134+
--notes "${{ steps.changelog.outputs.changelog }}" \
135+
--latest

0 commit comments

Comments
 (0)