chore(versioning): Add semantic versioning CI action (#3) #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - '.github/**' | |
| - '!.github/workflows/release.yml' | |
| workflow_dispatch: | |
| inputs: | |
| bump_type: | |
| description: 'Version bump type' | |
| required: false | |
| type: choice | |
| options: | |
| - auto | |
| - major | |
| - minor | |
| - patch | |
| default: 'auto' | |
| # Prevent concurrent releases | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| jobs: | |
| release: | |
| name: Create Release | |
| runs-on: ubuntu-latest | |
| # Only run if not a bot commit (to avoid release loops) | |
| if: ${{ !contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, 'chore(release)') }} | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@v2 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure Git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Check if release needed | |
| id: check_release | |
| run: | | |
| # Get commits since last tag | |
| LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [[ -z "$LAST_TAG" ]]; then | |
| echo "No previous tag found, release needed" | |
| echo "release_needed=true" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Check if there are conventional commits since last tag | |
| COMMITS=$(git log "$LAST_TAG"..HEAD --pretty=format:"%s" --no-merges) | |
| if echo "$COMMITS" | grep -qE "^(feat|fix|perf|refactor)(\([a-z-]+\))?(!)?:"; then | |
| echo "Found conventional commits, release needed" | |
| echo "release_needed=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "No conventional commits found, skipping release" | |
| echo "release_needed=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Bump version | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| id: bump_version | |
| run: | | |
| chmod +x scripts/bump-version.sh | |
| # Use manual bump type if provided, otherwise auto-detect | |
| BUMP_TYPE="${{ github.event.inputs.bump_type || 'auto' }}" | |
| if [[ "$BUMP_TYPE" == "auto" ]]; then | |
| ./scripts/bump-version.sh | |
| else | |
| ./scripts/bump-version.sh "$BUMP_TYPE" | |
| fi | |
| - name: Update CHANGELOG | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| run: | | |
| chmod +x scripts/update-changelog.sh | |
| ./scripts/update-changelog.sh "${{ steps.bump_version.outputs.new_version }}" | |
| - name: Update README badge | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| run: | | |
| VERSION="${{ steps.bump_version.outputs.new_version }}" | |
| # Update version badge in README.md | |
| sed -i "s/version-[0-9]\+\.[0-9]\+\.[0-9]\+-blue/version-${VERSION}-blue/" README.md | |
| echo "✓ Updated version badge to ${VERSION}" | |
| - name: Extract release notes | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| id: extract_notes | |
| run: | | |
| # Extract the latest version section from CHANGELOG | |
| VERSION="${{ steps.bump_version.outputs.new_version }}" | |
| # Create release notes file | |
| NOTES_FILE=$(mktemp) | |
| # Extract content between [VERSION] and next [VERSION] or end | |
| awk "/^## \[$VERSION\]/ { flag=1; next } /^## \[[0-9]/ { flag=0 } flag" CHANGELOG.md > "$NOTES_FILE" | |
| # Set output | |
| { | |
| echo 'notes<<EOF' | |
| cat "$NOTES_FILE" | |
| echo 'EOF' | |
| } >> $GITHUB_OUTPUT | |
| - name: Commit version bump | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| run: | | |
| git add VERSION CHANGELOG.md README.md | |
| git commit -m "chore(release): bump version to ${{ steps.bump_version.outputs.new_version }} [skip ci]" | |
| git push origin main | |
| - name: Create and push tag | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| run: | | |
| VERSION="${{ steps.bump_version.outputs.new_version }}" | |
| git tag -a "v$VERSION" -m "Release v$VERSION" | |
| git push origin "v$VERSION" | |
| - name: Create GitHub Release | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| uses: actions/create-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tag_name: v${{ steps.bump_version.outputs.new_version }} | |
| release_name: v${{ steps.bump_version.outputs.new_version }} | |
| body: ${{ steps.extract_notes.outputs.notes }} | |
| draft: false | |
| prerelease: false | |
| - name: Release summary | |
| if: steps.check_release.outputs.release_needed == 'true' | |
| run: | | |
| echo "## 🚀 Release v${{ steps.bump_version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Previous version:** ${{ steps.bump_version.outputs.old_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**New version:** ${{ steps.bump_version.outputs.new_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Bump type:** ${{ steps.bump_version.outputs.bump_type }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Release Notes" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "${{ steps.extract_notes.outputs.notes }}" >> $GITHUB_STEP_SUMMARY | |
| - name: Skip summary | |
| if: steps.check_release.outputs.release_needed != 'true' | |
| run: | | |
| echo "## ⏭️ Release Skipped" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "No conventional commits found since last release." >> $GITHUB_STEP_SUMMARY | |
| echo "Release will be created when feat/fix/perf/refactor commits are pushed." >> $GITHUB_STEP_SUMMARY |