Created
March 30, 2026 10:11
-
-
Save o6uoq/15f15e86a5eb4ffceb10bc8e9a5517af to your computer and use it in GitHub Desktop.
Conventional Commits → Semver Bump Calculator
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
| # ============================================================================ | |
| # Conventional Commits → Semver Bump Calculator | |
| # ============================================================================ | |
| # Drop this into your workflow. On PR merge to main, it reads the commits | |
| # from the merged PR and outputs the bump type (major|minor|patch) plus | |
| # the next version string. | |
| # | |
| # Follows the Conventional Commits v1.0.0 spec: | |
| # - fix: → patch | |
| # - feat: → minor | |
| # - BREAKING CHANGE: (footer) or feat!:/fix!:/etc. → major | |
| # - Anything else (chore, docs, ci, etc.) → patch (safe default) | |
| # | |
| # Outputs: | |
| # steps.semver.outputs.bump → major | minor | patch | |
| # steps.semver.outputs.version → e.g. 1.3.0 | |
| # steps.semver.outputs.previous → e.g. 1.2.4 | |
| # | |
| # Prerequisites: | |
| # - CURRENT_VERSION env var: set this to your artifact's current version. | |
| # Pull it from wherever you track it — a VERSION file, your last Nexus | |
| # tag, a git tag, whatever. The snippet doesn't care where it comes from. | |
| # ============================================================================ | |
| name: Build on PR Merge | |
| on: | |
| pull_request: | |
| types: [closed] | |
| branches: [main] | |
| jobs: | |
| build: | |
| if: github.event.pull_request.merged == true | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| # Fetch full history so we can read the PR's commits | |
| fetch-depth: 0 | |
| # ---------------------------------------------------------------- | |
| # Set your current version here. Replace this with however you | |
| # track it — VERSION file, last Nexus tag, git tag, etc. | |
| # ---------------------------------------------------------------- | |
| # Examples: | |
| # CURRENT_VERSION=$(cat VERSION) | |
| # CURRENT_VERSION=$(curl -s https://nexus.example.com/api/latest/my-image | jq -r .version) | |
| # CURRENT_VERSION=$(git tag -l 'my-service-v*' --sort=-v:refname | head -1 | sed 's/my-service-v//') | |
| # ---------------------------------------------------------------- | |
| - name: Calculate semver bump from PR commits | |
| id: semver | |
| env: | |
| CURRENT_VERSION: "0.0.0" # ← replace with your lookup | |
| PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -euo pipefail | |
| echo "::group::PR Commits" | |
| # Get commit messages from this PR only (base...head) | |
| COMMITS=$(git log --format="%s%n%b" "${PR_BASE_SHA}..${PR_HEAD_SHA}") | |
| echo "$COMMITS" | |
| echo "::endgroup::" | |
| # Determine bump type — highest wins | |
| BUMP="patch" | |
| while IFS= read -r line; do | |
| # Skip empty lines | |
| [[ -z "$line" ]] && continue | |
| # Major: BREAKING CHANGE footer or ! after type | |
| if echo "$line" | grep -qE '^BREAKING CHANGE:|^BREAKING-CHANGE:'; then | |
| BUMP="major" | |
| break | |
| fi | |
| if echo "$line" | grep -qE '^[a-z]+(\(.+\))?!:'; then | |
| BUMP="major" | |
| break | |
| fi | |
| # Minor: feat | |
| if echo "$line" | grep -qE '^feat(\(.+\))?:'; then | |
| [[ "$BUMP" != "major" ]] && BUMP="minor" | |
| fi | |
| # patch: fix, or anything else with a type — already the default | |
| done <<< "$COMMITS" | |
| # Apply bump to current version | |
| IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" | |
| MAJOR=${MAJOR:-0}; MINOR=${MINOR:-0}; PATCH=${PATCH:-0} | |
| case "$BUMP" in | |
| major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; | |
| minor) MINOR=$((MINOR + 1)); PATCH=0 ;; | |
| patch) PATCH=$((PATCH + 1)) ;; | |
| esac | |
| NEXT_VERSION="${MAJOR}.${MINOR}.${PATCH}" | |
| echo "Previous : $CURRENT_VERSION" | |
| echo "Bump : $BUMP" | |
| echo "Next : $NEXT_VERSION" | |
| # Set outputs for downstream steps | |
| echo "bump=$BUMP" >> "$GITHUB_OUTPUT" | |
| echo "version=$NEXT_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "previous=$CURRENT_VERSION" >> "$GITHUB_OUTPUT" | |
| # ---------------------------------------------------------------- | |
| # Use the outputs downstream, e.g.: | |
| # ---------------------------------------------------------------- | |
| # - name: Build and push image | |
| # run: | | |
| # docker build -t my-registry/my-image:${{ steps.semver.outputs.version }} . | |
| # docker push my-registry/my-image:${{ steps.semver.outputs.version }} | |
| # ---------------------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment