Skip to content

Release

Release #91

Workflow file for this run

name: Release
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+*"
permissions:
contents: write
actions: read
jobs:
release:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get previous tag
id: get-previous-tag
run: |
# Get the current tag that triggered the workflow
CURRENT_TAG="${GITHUB_REF#refs/tags/}"
echo "current_tag=$CURRENT_TAG" >> $GITHUB_OUTPUT
# Get the previous tag (or use the first commit if no previous tag exists)
PREVIOUS_TAG=$(git tag --list --sort=-version:refname --merged "$CURRENT_TAG" | grep -v "^$CURRENT_TAG$" | head -n 1)
if [ -z "$PREVIOUS_TAG" ]; then
# No previous tag, use the root commit
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
fi
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
echo "Current tag: $CURRENT_TAG"
echo "Previous tag: $PREVIOUS_TAG"
- name: Generate release notes from conventional commits
id: generate-notes
run: |
CURRENT_TAG="${{ steps.get-previous-tag.outputs.current_tag }}"
PREVIOUS_TAG="${{ steps.get-previous-tag.outputs.previous_tag }}"
# Get all commits between previous and current tag
if [ "$PREVIOUS_TAG" = "$(git rev-list --max-parents=0 HEAD)" ]; then
# If previous_tag is the root commit, use it directly
COMMITS=$(git log --pretty=format:"%h|%an|%s" "$PREVIOUS_TAG..HEAD")
else
COMMITS=$(git log --pretty=format:"%h|%an|%s" "$PREVIOUS_TAG..$CURRENT_TAG")
fi
# Initialize temporary files for each section
TEMP_DIR=$(mktemp -d)
touch "$TEMP_DIR"/{breaking,features,bugfixes,performance,refactor,tests,documentation,chore,other}
# Categorize commits based on conventional commits
while IFS='|' read -r HASH AUTHOR SUBJECT; do
if [ -z "$SUBJECT" ]; then
continue
fi
# Build commit link
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}"
SHORT_HASH="${HASH:0:7}"
COMMIT_ENTRY="- ${SUBJECT} (${AUTHOR}) [${SHORT_HASH}](${REPO_URL}/commit/${HASH})"
# Check for breaking changes (takes precedence)
if [[ "$SUBJECT" =~ ^[a-z]+!: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/breaking"
fi
# Categorize by type
if [[ "$SUBJECT" =~ ^feat(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/features"
elif [[ "$SUBJECT" =~ ^fix(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/bugfixes"
elif [[ "$SUBJECT" =~ ^docs(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/documentation"
elif [[ "$SUBJECT" =~ ^perf(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/performance"
elif [[ "$SUBJECT" =~ ^refactor(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/refactor"
elif [[ "$SUBJECT" =~ ^test(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/tests"
elif [[ "$SUBJECT" =~ ^chore(\(.+\))?!?: ]]; then
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/chore"
else
echo "$COMMIT_ENTRY" >> "$TEMP_DIR/other"
fi
done <<< "$COMMITS"
# Build release notes file
RELEASE_NOTES_FILE=$(mktemp)
[ -s "$TEMP_DIR/breaking" ] && echo -e "## ⚠️ Breaking Changes\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/breaking" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/features" ] && echo -e "## ✨ New Features\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/features" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/bugfixes" ] && echo -e "## 🐛 Bug Fixes\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/bugfixes" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/performance" ] && echo -e "## ⚡ Performance Improvements\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/performance" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/refactor" ] && echo -e "## ♻️ Refactoring\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/refactor" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/tests" ] && echo -e "## ✅ Tests\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/tests" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/documentation" ] && echo -e "## 📚 Documentation\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/documentation" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/chore" ] && echo -e "## 🔧 Chores\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/chore" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
[ -s "$TEMP_DIR/other" ] && echo -e "## Other Changes\n" >> "$RELEASE_NOTES_FILE" && cat "$TEMP_DIR/other" >> "$RELEASE_NOTES_FILE" && echo "" >> "$RELEASE_NOTES_FILE"
# If release notes are empty, set default message
if [ ! -s "$RELEASE_NOTES_FILE" ]; then
echo "No changes in this release." >> "$RELEASE_NOTES_FILE"
fi
# Output the release notes using delimiter method for multi-line content
DELIMITER="EOF_$(date +%s)"
echo "release_notes<<$DELIMITER" >> $GITHUB_OUTPUT
cat "$RELEASE_NOTES_FILE" >> $GITHUB_OUTPUT
echo "$DELIMITER" >> $GITHUB_OUTPUT
# Cleanup
rm -rf "$TEMP_DIR"
rm "$RELEASE_NOTES_FILE"
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.get-previous-tag.outputs.current_tag }}
name: ${{ steps.get-previous-tag.outputs.current_tag }}
body: ${{ steps.generate-notes.outputs.release_notes }}
draft: false
prerelease: false
token: ${{ secrets.GITHUB_TOKEN }}