@continuous-excellence/tagger
v3.1.5
Published
Deterministic semantic versioning from git history. Platform-neutral CLI for calculating versions based on commit messages, with zero configuration required.
Maintainers
Readme
Tagger CLI
An opinionated program for automatic semantic versioning via git tags and information in commits.
Installation
Tagger is available as both an npm package and a JVM distribution.
NPM Installation
You can install the tool using any NPM-like system.
Local Example
npm i -D @continuous-excellence/tagger # this will install it into a project as a dev dependency
npx tagger calculate-version # You can use npx to run a project's programs easilyGlobal Example
npm i -g @continuous-excellence/tagger # this will install it globally into npm
tagger calculate-version # Now it should be available via NPM's path on your shell.Migration from git-semver-tagger
The package has been renamed to @continuous-excellence/tagger. The old git-semver-tagger package is deprecated. To
migrate:
# Uninstall old package
npm uninstall git-semver-tagger
# Install new scoped package
npm install @continuous-excellence/taggerThe CLI command remains tagger - no changes needed to your scripts or workflows.
JVM Distribution
For environments without Node.js or when you prefer a JVM-based tool, pre-built JVM distributions are available.
Install from GitHub Releases
Download the latest release from GitHub Releases:
# Download the latest version (replace 3.1.2 with current version)
VERSION=3.1.2
curl -L -O https://github.com/robertfmurdock/ze-great-tools/releases/download/${VERSION}/tagger-cli-jvm.zip
# Verify checksum (optional but recommended)
curl -L -O https://github.com/robertfmurdock/ze-great-tools/releases/download/${VERSION}/tagger-cli-jvm.zip.sha256
sha256sum -c tagger-cli-jvm.zip.sha256
# Extract to installation directory
unzip tagger-cli-jvm.zip -d ~/.local/share/
# Add to PATH (add this line to your ~/.bashrc or ~/.zshrc)
export PATH="$HOME/.local/share/tagger-cli-jvm/bin:$PATH"
# Verify installation
tagger --versionDistribution contents:
bin/tagger- Shell script wrapper for Unix-like systemsbin/tagger.bat- Batch script wrapper for Windowslib/- All required JVM dependencies
Requirements: Java Runtime Environment (JRE) 8 or higher
Build from Source
Alternatively, you can build the JVM distribution locally:
# Clone the repository
git clone https://github.com/robertfmurdock/ze-great-tools.git
cd ze-great-tools
# Build the JVM distribution
./gradlew :command-line-tools:tagger-cli:jvmDistZip
# Extract the distribution
unzip command-line-tools/tagger-cli/build/distributions/tagger-cli-jvm.zip -d ~/.local/share/
# Add to PATH
export PATH="$HOME/.local/share/tagger-cli-jvm/bin:$PATH"
tagger --versionCommands
Calculate Version
The calculate-version command will generate a new version number based on all of the commits since the last tag, and
output as a string.
Basic usage:
tagger calculate-versionOutput: 1.2.3-SNAPSHOT
Tag
The tag command will create a tag with the given version and push it back to the repository.
We recommend this command only is run after the build is validated. Use discernment to decide if it should happen before publication of artifacts, or afterward.
Basic usage:
tagger tag --version 1.2.3 --release-branch mainFor complete option documentation and automation guidance, run tagger calculate-version --help or tagger tag --help.
Configuration File
Tagger supports a .tagger JSON configuration file at your repository root to eliminate repetitive command-line
options.
Creating a Configuration File
Generate a template with default values:
tagger generate-settings-file --fileThis creates .tagger in the current directory. Edit it to customize behavior.
(--file='' is also supported for backward compatibility.)
Print to stdout without creating file:
tagger generate-settings-fileMerge new defaults into existing file:
tagger generate-settings-file --file --mergeConfiguration Options
The .tagger file supports these fields:
{
"releaseBranch": "main",
"implicitPatch": true,
"allowDetachedHead": false,
"forceSnapshot": false,
"majorRegex": "\\[major\\]",
"minorRegex": "\\[minor\\]|\\[feature\\]",
"patchRegex": "\\[patch\\]|\\[fix\\]|\\[bug\\]",
"noneRegex": "\\[none\\]",
"versionRegex": null,
"userName": null,
"userEmail": null,
"warningsAsErrors": false
}Field descriptions:
releaseBranch: Branch name for release versions (other branches get -SNAPSHOT)implicitPatch: Auto-bump patch version when no version-tagged commits exist (default: true)allowDetachedHead: Allow version calculation in detached HEAD state (default: false)forceSnapshot: Always add -SNAPSHOT suffix regardless of conditions (default: false)majorRegex: Pattern to detect major version bumps in commit messagesminorRegex: Pattern to detect minor version bumpspatchRegex: Pattern to detect patch version bumpsnoneRegex: Pattern to detect commits that don't affect versionversionRegex: Unified regex with named groups (major, minor, patch, none). Overrides individual regex patterns if set.userName: Git user name for creating tags (defaults to git config)userEmail: Git user email for creating tags (defaults to git config)warningsAsErrors: Enable strict mode - treat warnings as errors (exit non-zero) (default: false). See Warnings and Strict Mode for details.
Note: Command-line options always override .tagger file settings.
Example Workflows
Minimal config for standard workflow:
{
"releaseBranch": "main"
}Then use simplified commands:
tagger calculate-version # no --release-branch needed
tagger tag --version 1.2.3 # no --release-branch neededCustom regex patterns for your commit convention:
{
"releaseBranch": "production",
"majorRegex": "\\bBREAKING[: ]",
"minorRegex": "\\bfeat[:(]",
"patchRegex": "\\bfix[:(]"
}Structured Output
Both commands support structured output via the --format flag. Use --format=json for advanced build automation that
needs explicit status envelopes and error handling.
Format Options
--format=text(default): Human-readable text output--format=json: Structured JSON output
Calculate Version JSON Output
Example command:
tagger calculate-version --format=jsonSuccess response:
{
"status": "success",
"data": {
"version": "1.2.3-SNAPSHOT",
"snapshot": true,
"snapshotReasons": [
"DIRTY",
"AHEAD"
]
}
}Error response:
{
"status": "error",
"error": "HEAD is detached (not pointing at any branch)",
"code": "CONFIGURATION_ERROR"
}Tag JSON Output
Example command:
tagger tag --version 1.2.3 --release-branch main --format=jsonSuccess response:
{
"status": "success",
"data": {
"tag": "1.2.3"
}
}Error response:
{
"status": "error",
"error": "Failed to create tag",
"code": "TAG_ERROR"
}Build Automation Examples
Extract version in GitHub Actions:
- name: Calculate version
id: version
run: |
VERSION=$(tagger calculate-version)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Use version
run: echo "Building version ${{ steps.version.outputs.version }}"Extract version in bash:
# Get version directly (text format, detailed status goes to stderr)
VERSION=$(tagger calculate-version)
echo "Building version: $VERSION"
# Use JSON format for complex scripting that needs to handle errors programmatically
if ! OUTPUT=$(tagger calculate-version --format=json 2>&1); then
STATUS=$(echo "$OUTPUT" | jq -r '.status')
if [ "$STATUS" = "error" ]; then
ERROR_MSG=$(echo "$OUTPUT" | jq -r '.error')
echo "Version calculation failed: $ERROR_MSG"
exit 1
fi
fiError handling:
OUTPUT=$(tagger calculate-version --format=json 2>&1)
STATUS=$(echo "$OUTPUT" | jq -r '.status')
if [ "$STATUS" = "error" ]; then
ERROR_MSG=$(echo "$OUTPUT" | jq -r '.error')
ERROR_CODE=$(echo "$OUTPUT" | jq -r '.code')
echo "Error ($ERROR_CODE): $ERROR_MSG"
exit 1
fiWarnings and Strict Mode
Tagger emits warnings to stderr when it detects potentially risky conditions. By default, warnings don't fail the build (exit 0), but you can enable strict mode to treat warnings as errors.
Warning Classes
Deprecation Warnings
Issued when using deprecated flags or options:
⚠️ --disable-detached is deprecated and will be removed in a future version. Use --allow-detached-head=false instead.Cause: Using old CLI syntax that's scheduled for removal.
Action: Update your commands or config files to use the replacement option.
Release Risk Warnings
Issued when potentially unsafe operations are explicitly allowed:
⚠️ Running with allowDetachedHead on release branch. This may produce inconsistent versions if tags are not synchronized.Cause: Using --allow-detached-head on a release branch in detached HEAD state.
Action: Consider whether detached HEAD is appropriate for your workflow. For CI builds on release branches, this usually indicates a configuration issue.
Policy Violation Warnings (tag command)
The tag command issues warnings (not errors) when it cannot create a tag due to policy constraints:
⚠️ Cannot create tag: not on release branch 'main' (current branch: feature/xyz)Cause: Attempting to create a release tag from a non-release branch, a commit that's already tagged, or a snapshot version.
Action: Switch to the release branch, use --release-branch with the correct branch name, or adjust your tagging
workflow.
Strict Mode (warningsAsErrors)
Enable strict mode to fail builds when warnings are present. This is useful for CI/CD pipelines where you want to enforce clean execution.
Command-line flag:
tagger calculate-version --warnings-as-errors
tagger tag --version 1.2.3 --warnings-as-errorsConfiguration file:
{
"warningsAsErrors": true
}Behavior changes with strict mode enabled:
- Exit code: Changes from 0 to 1 when warnings are present
- JSON output:
calculate-versionreturns error status instead of success when warnings escalate:{ "status": "error", "error": "Warnings treated as errors", "code": "WARNINGS_AS_ERRORS", "warnings": [ "Running with allowDetachedHead on release branch..." ] } - Stderr diagnostics: Warnings remain visible for troubleshooting
Build automation example:
# Strict mode catches deprecation warnings before they become breaking changes
tagger calculate-version --warnings-as-errors --format=json > version.json
if [ $? -ne 0 ]; then
echo "Version calculation failed or warnings present:"
cat version.json | jq -r '.error, .warnings[]?'
exit 1
fiWhen to use strict mode:
- Automated build pipelines where warnings indicate configuration drift
- During migration from deprecated options (fail fast on old syntax)
- When enforcing "clean build" policies across teams
When NOT to use strict mode:
- Local development workflows where warnings are informational
- Gradual rollout of new configuration options
- When using
allowDetachedHeadintentionally and warnings are expected
Common Errors and Troubleshooting
Lightweight Tags
Error: found N tag(s) (...) but it is/they are lightweight.
Cause: Tagger requires annotated tags (created with git tag -a) because they store metadata like tagger name,
email, and timestamp. Lightweight tags (created with git tag <name>) don't include this information.
Solution: Recreate the tag(s) as annotated tags. The error message will provide exact commands, for example:
git tag -d 1.0.0
git tag -a 1.0.0 <sha> -m "1.0.0"
git push --force origin 1.0.0Replace <sha> with the commit hash where the tag should point (often the same commit the lightweight tag pointed to).
Permission Errors When Pushing Tags
Error: Command failed: git push --tags (exit code 128) or exit code 403
Common causes:
- The account running tagger doesn't have push permission on the repository
- CI/CD pipelines often use restricted service accounts by default
Solutions:
For Azure DevOps:
- Go to Project Settings → Repositories → Security
- Find the Build Service identity (e.g.,
<Project Name> Build Service) - Grant both
ContributeandCreate tagpermissions - Ensure "Allow scripts to access the OAuth token" is enabled in your pipeline YAML:
- checkout: self persistCredentials: true
For GitHub Actions:
- Add write permissions to your workflow job:
jobs: build: permissions: contents: write # Required for pushing tags - Ensure you're not using a read-only
GITHUB_TOKEN
For GitLab CI:
variables:
GIT_STRATEGY: clone
before_script:
- git config --global user.email "[email protected]"
- git config --global user.name "CI Bot"No Tags Exist
Error: repository has no tags.
Cause: This is a new repository or no tags have been created yet.
Solution: Create an initial tag manually to establish the version baseline:
git tag -a 0.1.0 -m "Initial version"
git push origin 0.1.0After this, tagger can calculate subsequent versions automatically.
Help
For detailed option documentation, snapshot reason explanations, and structured output field definitions, use the built-in help:
tagger --help
tagger calculate-version --help
tagger tag --helpDocumentation is also available as markdown files in the repository:
- Tagger Guide - Fit assessment, philosophy, and workflow guidance
- Tagger Help - Main command overview and options
- Calculate Version Help - Snapshot reasons, remediation, and version calculation details
- Tag Help - Workflow, version override guidance, and tagging options
