Saltar al contenido
mypipelines
Shared (cross-cutting)·Reusable workflow·on: workflow_call

Shared Semver

Shared - Semantic Versioning

.github/workflows/shared-semver.yml

.github/workflows/shared-semver.yml
name: Shared - Semantic Versioning
on:
workflow_call:
inputs:
runner:
description: 'Runner type'
required: false
type: string
default: 'ubuntu-latest'
update_changelog:
description: 'Generate and commit CHANGELOG.md'
required: false
type: boolean
default: false
tag_prefix:
description: 'Prefix for version tags'
required: false
type: string
default: 'v'
major_pattern:
description: 'Regex pattern for major version bump'
required: false
type: string
default: '(MAJOR|BREAKING CHANGE)'
minor_pattern:
description: 'Regex pattern for minor version bump'
required: false
type: string
default: '(feat)'
outputs:
version:
description: 'Semantic version (e.g. 1.2.3)'
value: ${{ jobs.semver.outputs.version }}
version_tag:
description: 'Semantic version tag (e.g. v1.2.3)'
value: ${{ jobs.semver.outputs.version_tag }}
# Least privilege for what this workflow actually does. Without it the
# job inherits whatever the calling repository's default happens to be,
# which on older repositories is write-all.
permissions:
contents: write
jobs:
semver:
name: Semantic Version
runs-on: ${{ inputs.runner }}
timeout-minutes: 10
outputs:
version: ${{ steps.semver.outputs.version }}
version_tag: ${{ steps.semver.outputs.version_tag }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Fetch all tags
run: git fetch --tags --force
- name: Calculate semantic version
id: semver
env:
TAG_PREFIX: ${{ inputs.tag_prefix }}
MAJOR_PATTERN: ${{ inputs.major_pattern }}
MINOR_PATTERN: ${{ inputs.minor_pattern }}
run: |
LAST_TAG=$(git tag -l "${TAG_PREFIX}*" --sort=-v:refname | head -n1)
if [ -z "$LAST_TAG" ]; then
LAST_TAG="${TAG_PREFIX}0.0.0"
echo "No previous tag found, starting from $LAST_TAG"
else
echo "Last tag: $LAST_TAG"
fi
LAST_VERSION="${LAST_TAG#"${TAG_PREFIX}"}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$LAST_VERSION"
if git rev-parse "$LAST_TAG" >/dev/null 2>&1; then
COMMITS=$(git log "${LAST_TAG}..HEAD" --pretty=format:'%s%n%b')
else
COMMITS=$(git log --pretty=format:'%s%n%b')
fi
if [ -z "$COMMITS" ]; then
echo "No new commits since $LAST_TAG"
VERSION="${MAJOR}.${MINOR}.${PATCH}"
elif echo "$COMMITS" | grep -qE "$MAJOR_PATTERN"; then
MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
VERSION="${MAJOR}.${MINOR}.${PATCH}"
elif echo "$COMMITS" | grep -qE "$MINOR_PATTERN"; then
MINOR=$((MINOR + 1)); PATCH=0
VERSION="${MAJOR}.${MINOR}.${PATCH}"
else
PATCH=$((PATCH + 1))
VERSION="${MAJOR}.${MINOR}.${PATCH}"
fi
echo "Calculated version: $VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "version_tag=${TAG_PREFIX}${VERSION}" >> "$GITHUB_OUTPUT"
- name: Export version
run: |
echo "VERSION=${{ steps.semver.outputs.version_tag }}" >> $GITHUB_ENV
echo "### Version: ${{ steps.semver.outputs.version_tag }}" >> $GITHUB_STEP_SUMMARY
- name: Update CHANGELOG.md
if: inputs.update_changelog
env:
TAG_PREFIX: ${{ inputs.tag_prefix }}
run: |
set -e
TODAY="$(date +%Y-%m-%d)"
VERSION_LINE="## ${VERSION} - ${TODAY}"
PREV_TAG=$(git tag -l "${TAG_PREFIX}*" --sort=-v:refname | head -n1)
if [ -n "$PREV_TAG" ] && git rev-parse "$PREV_TAG" >/dev/null 2>&1; then
RANGE="${PREV_TAG}..HEAD"
else
RANGE="$(git rev-list --max-parents=0 HEAD)^..HEAD"
fi
COMMITS="$(git log --pretty=format:'- %s (%h)' ${RANGE})"
echo "Preparing CHANGELOG for ${VERSION}"
if [ -f CHANGELOG.md ]; then
tail -n +1 CHANGELOG.md > /tmp/CHANGELOG_OLD.md
else
touch /tmp/CHANGELOG_OLD.md
fi
{
echo "${VERSION_LINE}"
echo ""
if [ -n "${COMMITS}" ]; then
echo "${COMMITS}"
else
echo "- No changes recorded."
fi
echo ""
cat /tmp/CHANGELOG_OLD.md
} > CHANGELOG.md
- name: Commit and push CHANGELOG
if: inputs.update_changelog
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add CHANGELOG.md
if ! git diff --cached --quiet; then
git commit -m "chore(release): ${VERSION} - update CHANGELOG [skip ci]"
git push
else
echo "No CHANGELOG changes to commit."
fi