A GitHub Action to automatically bump and tag master, on merge, with the latest SemVer formatted version. Works on any platform.
This is a copy of mathieudutour/github-tag-action, with a handful of open community pull requests cherry-picked in and additional fixes made with the help of Claude Code. See Credits for details.
name: Bump version
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Bump version and push tag
id: tag_version
uses: ihabsoliman/github-tag-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Create a GitHub release
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
with:
tag: ${{ steps.tag_version.outputs.new_tag }}
name: Release ${{ steps.tag_version.outputs.new_tag }}
body: ${{ steps.tag_version.outputs.changelog }}- github_token (required) - Required for permission to tag the repo. Usually
${{ secrets.GITHUB_TOKEN }}. - commit_sha (optional) - The commit SHA value to add the tag. If specified, it uses this value instead GITHUB_SHA. It could be useful when a previous step merged a branch into github.ref.
- source (optional) - Path to the local git checkout used for local-git operations (the
git logfallback of the compare API, the shallow-clone probe, andtag_context: branchancestry checks). GitHub API calls are repository-global and unaffected (default:.).
- fetch_all_tags (optional) - By default, this action fetch the last 100 tags from Github. Sometimes, this is not enough and using this action will fetch all tags recursively (default:
false).
- release_branches (optional) - Comma separated list of branches (JavaScript regular expression accepted) that will generate the release tags. Other branches and pull-requests generate versions postfixed with the commit hash and do not generate any repository tag. Examples:
^master$or.*or^release.*,^hotfix.*,^master$... (default:^master$,^main$). - pre_release_branches (optional) - Comma separated list of branches (JavaScript regular expression accepted) that will generate the pre-release tags.
-
scopes (optional) - Comma separated list of scopes (JavaScript regular expression accepted) to consider when tagging, and to include in the changelog. If this option is specified, then commits with scopes not matching this list will not be analyzed nor included in the changelog.
-
branch_history (optional) - Which commits feed the commit analyzer and the changelog (default:
compare).compare- commits between the previous tag and the tagged commit (existing behaviour).last- only the tagged commit itself (useful for squash-merge workflows).full- every commit reachable from the tagged commit, narrowed todefault_branch..<commit>whendefault_branchis set and differs from the current branch. Requires a full clone (fetch-depth: 0); falls back tocomparewith a warning on a shallow checkout.
Unrecognized values fall back to
comparewith a warning. -
default_branch (optional) - The repository's default branch (e.g.
main). Only used bybranch_history: full, to narrow the range to the commits unique to the current branch. Not auto-detected.
-
default_bump (optional) - Which type of bump to use when none is explicitly provided when commiting to a release branch (default:
patch). You can also setfalseto avoid generating a new tag when none is explicitly provided. Can bepatch, minor or major. -
default_prerelease_bump (optional) - Which type of bump to use when none is explicitly provided when commiting to a prerelease branch (default:
prerelease). You can also setfalseto avoid generating a new tag when none is explicitly provided. Can beprerelease, prepatch, preminor or premajor. -
default_draft_bump (optional) - Which type of bump to use when none is explicitly provided when commiting to a prerelease branch with no previous prerelease version (default:
prerelease). You can also setfalseto avoid generating a new tag when none is explicitly provided. Can beprerelease, patch, minor or major. -
force_bump (optional) - If specified, it ignores the type of bump provided when committing to a release branch, as well as
default_bump. You can also setfalseto avoid generating a new tag. Can bepatch, minor or major. -
force_prerelease_bump (optional) - If specified, it ignores the type of bump provided when committing to a release branch, as well as
default_bump. You can also setfalseto avoid generating a new tag. Can beprerelease, prepatch, preminor or premajor. -
custom_tag (optional) - Custom tag name. If specified, it overrides bump settings.
-
force_update (optional) - Updates the sha of a tag if it already exists (default:
false). -
create_annotated_tag (optional) - Boolean to create an annotated rather than a lightweight one (default:
false). -
tag_message (optional) - Message for the annotated tag object (default: the tag name). Only used when
create_annotated_tagistrue; setting it withcreate_annotated_tag: falselogs a warning and is otherwise ignored. -
tag_prefix (optional) - A prefix to the tag name (default:
v). -
initial_version (optional) - The version to assume as the previous version when no matching tag exists yet, i.e. the base for the very first tag this action creates (default:
0.0.0). Must be a valid semver; a leadingvis stripped. The action fails if it is not valid semver. -
tag_search_pattern (optional) - A glob pattern to filter tags to consider for version bumping (e.g.
v0.*). Useful for projects with multiple major versions supported simultaneously with different root commits. -
tag_context (optional) - Which tags to consider when picking the previous tag (default:
repo).repo- considers every tag in the repository.branch- considers only tags that are ancestors of the tagged commit.
Unrecognized values fall back to
repowith a warning. -
append_to_pre_release_tag (optional) - A suffix to the pre-release tag name (default:
<branch>). -
commit_analyzer_preset (optional) - A supported
conventional-changelogpreset (default:angular). See
-
custom_release_rules (optional) - Comma separated list of release rules.
Format:
<keyword>:<release_type>:<changelog_section>where<changelog_section>is optional. The<changelog_section>will default to the convention associated with the selectedcommit_analyzer_preset. Two of the most common conventions:Examples:
hotfix:patch,pre-feat:preminor,bug:patch:Bug Fixes,chore:patch:Chores
- push_tag (optional) - Push the tag to the remote. If false, tag is created but not pushed. (default:
true)
- dry_run (optional) - Do not perform tagging, just calculate next version and changelog, then exit
- soft_fail (optional) - If true, an unrecoverable error (after retries/fallbacks) is reported as a warning and the step exits successfully without creating a tag, instead of failing the job (default:
false).
- new_tag - The value of the newly calculated tag. Note that if there hasn't been any new commit, this will be
undefined. - new_version - The value of the newly created tag without the prefix. Note that if there hasn't been any new commit, this will be
undefined. - previous_tag - The value of the previous tag (or
v0.0.0if none). Note that ifcustom_tagis set, this will beundefined. - previous_version - The value of the previous tag (or
0.0.0if none) without the prefix. Note that ifcustom_tagis set, this will beundefined. - release_type - The computed release type (
major,minor,patchorcustom- can be prefixed withpre). - changelog - The conventional changelog since the previous tag.
- tag_created - Whether a tag was actually created/pushed (
true/false). Other outputs (new_tag,new_version, etc.) may be populated even when this isfalse(e.g.dry_run, or asoft_fail'd error) - check this output before acting on them.
Note: This action creates a lightweight tag by default.
The action will parse the new commits since the last tag using the semantic-release conventions.
semantic-release uses the commit messages to determine the type of changes in the codebase. Following formalized conventions for commit messages, semantic-release automatically determines the next semantic version number.
By default semantic-release uses Angular Commit Message Conventions, but different conventions can be used via the commit_analyzer_preset option.
Here is an example of the release type that will be done based on a commit messages, using the default settings:
| Commit message | Release type |
|
Patch Release |
|
Minor Release |
|
Major Release |
If no commit message contains any information, then default_bump will be used.
main is protected by a repo ruleset (required review, signed commits, linear history) that
blocks direct pushes, so cutting a release is a two-step, PR-based process rather than a single
click:
- Prepare — run the
Prepare releaseworkflow (gh workflow run prepare-release.yml, or via the Actions tab). It does adry_runof this action againstmain's current HEAD to compute the next version from the conventional commits since the last tag, bumpspackage.json'sversionaccordingly, and opens achore(release): vX.Y.Zpull request with the changelog as its description.package.json's version is cosmetic only (the package isprivate: trueand never published to npm) — it exists so the release PR has something to review and merge. - Review and merge — review the PR like any other and merge it (squash or rebase, per the ruleset). Merging is the point of no return: it's what actually cuts the release.
- Release — merging fires
Releaseautomatically. It runs this action for real (not a dry run) against the merge commit, which computes the same version again and creates + pushes thevX.Y.Ztag directly onmain's new HEAD — no synthetic or detached commits involved. It then creates a GitHub release from that tag viancipollo/release-action, using this action'schangelogoutput as the release body. - Publish — the new GitHub release (
release: published) triggersPublish Immutable Action Versionautomatically, which publishes that tag to GitHub's immutable actions registry.
Every push to main and every PR also runs Check dist,
which rebuilds lib/ and fails if the committed output doesn't match a fresh build — this is
what makes it safe for release.yml to tag main's HEAD directly without rebuilding first.
If you want consumers to be able to pin to a floating major tag (e.g. uses: owner/repo@v1) the
way most GitHub Actions do, run
Update major version tag after cutting a release:
give it the major tag (v1) and the target (v1.4.2), and it force-moves v1 to point at that
commit. This repo doesn't do this by default — it's a manual, deliberate step, not part of the
automated release above.
This is a fork of mathieudutour/github-tag-action, snapshotted after upstream became unmaintained, with a set of open community pull requests cherry-picked in.
anothrNick/github-tag-action - a similar action using a Dockerfile (hence not working on macOS)