Cut Release Tag #13
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: Cut Release Tag | |
| on: | |
| workflow_run: | |
| workflows: | |
| - CI | |
| types: | |
| - completed | |
| workflow_dispatch: | |
| inputs: | |
| bump: | |
| description: SemVer bump type | |
| required: false | |
| default: auto | |
| type: choice | |
| options: | |
| - auto | |
| - patch | |
| - minor | |
| - major | |
| permissions: | |
| actions: write | |
| contents: write | |
| jobs: | |
| cut-tag: | |
| if: | | |
| github.event_name == 'workflow_dispatch' || | |
| ( | |
| github.event_name == 'workflow_run' && | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'main' | |
| ) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} | |
| - name: Compute next version tag | |
| id: next_tag | |
| env: | |
| BUMP: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.bump || 'auto' }} | |
| TARGET_SHA: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| EXISTING_TAG="$(git tag --points-at "${TARGET_SHA}" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true)" | |
| if [[ -n "${EXISTING_TAG}" ]]; then | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| echo "latest_tag=${EXISTING_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${EXISTING_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "Target commit already tagged with ${EXISTING_TAG}" | |
| exit 0 | |
| fi | |
| LATEST_TAG="$(git tag --list 'v*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true)" | |
| if [[ -z "${LATEST_TAG}" ]]; then | |
| LATEST_TAG="v0.0.0" | |
| fi | |
| VERSION="${LATEST_TAG#v}" | |
| IFS='.' read -r MAJOR MINOR PATCH <<<"${VERSION}" | |
| EFFECTIVE_BUMP="${BUMP}" | |
| BUMP_REASON="Manual bump input" | |
| if [[ "${BUMP}" == "auto" ]]; then | |
| RANGE_SPEC="${LATEST_TAG}..${TARGET_SHA}" | |
| if [[ "${LATEST_TAG}" == "v0.0.0" ]]; then | |
| RANGE_SPEC="${TARGET_SHA}" | |
| elif ! git merge-base --is-ancestor "${LATEST_TAG}" "${TARGET_SHA}"; then | |
| RANGE_SPEC="${TARGET_SHA}" | |
| fi | |
| SUBJECTS="$(git log --format='%s' ${RANGE_SPEC} || true)" | |
| BODIES="$(git log --format='%b' ${RANGE_SPEC} || true)" | |
| if [[ -z "${SUBJECTS}" ]]; then | |
| EFFECTIVE_BUMP="patch" | |
| BUMP_REASON="No commit subjects found in range; defaulted to patch" | |
| elif grep -Eq '^[a-zA-Z]+(\([^)]+\))?!:' <<<"${SUBJECTS}" || grep -Eq 'BREAKING CHANGE:|BREAKING-CHANGE:' <<<"${BODIES}"; then | |
| EFFECTIVE_BUMP="major" | |
| BUMP_REASON="Detected breaking changes in commit messages" | |
| elif grep -Eq '^feat(\([^)]+\))?:' <<<"${SUBJECTS}"; then | |
| EFFECTIVE_BUMP="minor" | |
| BUMP_REASON="Detected feat commits" | |
| elif grep -Eq '^(fix|perf|revert)(\([^)]+\))?:' <<<"${SUBJECTS}"; then | |
| EFFECTIVE_BUMP="patch" | |
| BUMP_REASON="Detected fix/perf/revert commits" | |
| else | |
| EFFECTIVE_BUMP="patch" | |
| BUMP_REASON="No release commit type found; defaulted to patch" | |
| fi | |
| fi | |
| case "${EFFECTIVE_BUMP}" in | |
| patch) | |
| PATCH=$((PATCH + 1)) | |
| ;; | |
| minor) | |
| MINOR=$((MINOR + 1)) | |
| PATCH=0 | |
| ;; | |
| major) | |
| MAJOR=$((MAJOR + 1)) | |
| MINOR=0 | |
| PATCH=0 | |
| ;; | |
| *) | |
| echo "Unsupported bump type: ${EFFECTIVE_BUMP}" | |
| exit 1 | |
| ;; | |
| esac | |
| NEXT_TAG="v${MAJOR}.${MINOR}.${PATCH}" | |
| if git rev-parse "${NEXT_TAG}" >/dev/null 2>&1; then | |
| echo "Tag ${NEXT_TAG} already exists." | |
| exit 1 | |
| fi | |
| echo "skip=false" >> "$GITHUB_OUTPUT" | |
| echo "latest_tag=${LATEST_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${NEXT_TAG}" >> "$GITHUB_OUTPUT" | |
| echo "bump=${EFFECTIVE_BUMP}" >> "$GITHUB_OUTPUT" | |
| echo "bump_reason=${BUMP_REASON}" >> "$GITHUB_OUTPUT" | |
| - name: Create and push tag | |
| if: steps.next_tag.outputs.skip != 'true' | |
| env: | |
| TARGET_SHA: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| NEXT_TAG="${{ steps.next_tag.outputs.tag }}" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -a "${NEXT_TAG}" "${TARGET_SHA}" -m "Release ${NEXT_TAG}" | |
| git push origin "${NEXT_TAG}" | |
| - name: Trigger release workflows for new tag | |
| if: steps.next_tag.outputs.skip != 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| NEXT_TAG="${{ steps.next_tag.outputs.tag }}" | |
| gh workflow run "Release" --ref "${NEXT_TAG}" | |
| gh workflow run "NPM Publish" --ref "${NEXT_TAG}" | |
| - name: Summary | |
| run: | | |
| echo "Created tag: ${{ steps.next_tag.outputs.tag }}" | |
| echo "Previous tag: ${{ steps.next_tag.outputs.latest_tag }}" | |
| echo "Bump: ${{ steps.next_tag.outputs.bump }}" | |
| echo "Reason: ${{ steps.next_tag.outputs.bump_reason }}" |