Provides a reusable semantic versioning action for use across repositories
This action generates a semantic version of the form vMAJOR.MINOR.PATCH (e.g. v1.2.3).
The bump type is determined from commit messages:
- Major — triggered by
BREAKING CHANGE:or any type ending in!:(e.g.fix!:,feat!:). Increments MAJOR and resets MINOR and PATCH to0. - Minor — triggered by
feat:orfeat(<scope>):. Increments MINOR and resets PATCH to0. - Patch — any other commit message: increments PATCH.
Repository structure:
action.yml— The action definition with inputs, outputs, and composite steps.scripts/— PowerShell scripts executed by the action:compute-next-version.ps1— Calculates the next version based on commit history.create-and-push-tag.ps1— Creates and pushes the Git tag.create-github-release.ps1— Creates the GitHub release with release notes.write-step-summary.ps1— Generates the GitHub Actions step summary.
# action.yml in a consumer repository
name: Release
on:
push:
branches:
- main
permissions:
contents: write
jobs:
version:
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
bump_type: ${{ steps.version.outputs.bump_type }}
previous_tag: ${{ steps.version.outputs.previous_tag }}
commit_subject: ${{ steps.version.outputs.commit_subject }}
release_id: ${{ steps.version.outputs.release_id }}
steps:
- name: Generate semantic version
id: version
uses: glueckkanja/action-semver-version@SHA # v1.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
prefix: license-module # optional: prefix for tags like license-module-vX.Y.Z
suppress_release: "false" # optional: set true to skip creating a GitHub release
is_draft_release: "false" # optional: set true to create a draft GitHub release
suppress_tag: "false" # optional: set true to skip both tag and release creation
check_last_commit_only: "false" # optional: set true to only inspect the latest commit
is_prerelease: "false" # optional: set true to generate a prerelease version
prerelease_name: "" # optional: set a prerelease suffix name (for example 'rc', 'alpha', 'beta'); keep empty to create a prerelease without suffix
asset_path: "" # optional: path to a file or directory to attach to the release as assets
publish:
runs-on: ubuntu-latest
needs: version
steps:
- name: Show generated version
run: |
echo "Version: ${{ needs.version.outputs.version }}"
echo "Tag: ${{ needs.version.outputs.tag }}"
echo "Bump: ${{ needs.version.outputs.bump_type }}"
echo "Prev tag: ${{ needs.version.outputs.previous_tag }}"
echo "Commit: ${{ needs.version.outputs.commit_subject }}"
echo "Release ID: ${{ needs.version.outputs.release_id }}"contents: write— Required on the caller's job. Allows the action to create tags and releases. Pass${{ secrets.GITHUB_TOKEN }}via thegithub_tokeninput.
github_token(string, required) – GitHub token withcontents: writepermission. Pass${{ secrets.GITHUB_TOKEN }}.prefix(string, default: empty) – Optional prefix prepended to generated tags (for examplelicense-module-vX.Y.Z).suppress_release(string"true"/"false", default:"false") – When"true", skips creating a GitHub release while still creating tags (unless suppressed below).is_draft_release(string"true"/"false", default:"false") – When"true", creates the GitHub release as a draft.suppress_tag(string"true"/"false", default:"false") – When"true", skips creating both the Git tag and the GitHub release.check_last_commit_only(string"true"/"false", default:"false") – When"true", only the most recent commit is inspected to determine the bump type instead of all commits since the previous tag.is_prerelease(string"true"/"false", default:"false") – When"true", generates a prerelease version (for example1.2.3-rc.1).prerelease_name(string, default: empty) – Optional prerelease identifier name (for examplerc,alpha,beta). If set, the version includes a suffix (for example1.8.2-rc.1). If empty, the version remains plain semver (for example1.8.2) while still creating a prerelease release.asset_path(string, default: empty) – Path to a file or directory to attach to the release as assets. When set, the release is created as a draft first and assets are uploaded before publication. Ifis_draft_releaseis"true", the release remains a draft after assets are uploaded.
version– The calculated semantic version (for example1.2.3).tag– The tag name that would be created (for examplev1.2.3ormodule-v1.2.3).bump_type– The bump classification applied (major,minor, orpatch).previous_tag– The most recent matching tag prior to this run, if any.commit_subject– The commit message subject that determined the bump decision.release_id– Numeric ID of the created GitHub release. Empty when release creation is suppressed viasuppress_releaseorsuppress_tag.
The run summary also includes a direct link to the created GitHub release for quick access.
The action inspects commit messages and applies the following precedence:
- MAJOR: If any commit subject starts with
BREAKING CHANGE:or its first word ends with!:(for examplefeat!:,fix!:,chore!:,feat(scope)!:). - MINOR: If any commit subject starts with
feat:orfeat(<scope>):, case-insensitive. - PATCH: Any other commit message.
If no existing tags matching v* are found, versioning starts from 0.0.0.
Each run also publishes a Git tag matching the new version. By default tags look like vX.Y.Z, but you can provide a prefix input (for example license-module) to emit tags such as license-module-vX.Y.Z. When no prefix is set, the action creates a GitHub release with auto-generated release notes. When a prefix is set, the GitHub release is still created, but auto-generated release notes are skipped. If a tag for the computed version already exists, the action aborts with an error. It also fails if a release for the generated tag already exists, because an existing release may be immutable and unable to receive assets.
The tag used as the start point for auto-generated release notes depends on whether prefix and prerelease_name are set. This lookup only applies when prefix is empty, because prefixed releases do not generate release notes:
- No
prefixand noprerelease_name— the predecessor is taken from GitHub's release metadata, so release notes compare against something of the same "level":- For a stable release (
is_prerelease: "false"): the release currently flagged as latest on GitHub (viaGET /repos/{owner}/{repo}/releases/latest). - For a prerelease (
is_prerelease: "true", no suffix name): the newest non-draft release (stable or prerelease) whose tag matchesv<major>.<minor>.<patch>, ordered by publication date. - If the GitHub API returns no matching release (brand-new repo, nothing flagged yet, API error), the action falls back to the git-tag-based predecessor described below.
- For a stable release (
- No
prefixand aprerelease_nameset — name-neighbour matching from local tags: the newest prerelease tag with the same base version and suffix name is used, falling back to the newest matching stable tag.
The previous_tag output always reflects the newest matching stable tag from local git history, regardless of which mode is used for the release notes predecessor.