Skip to content

arcmantle/prospector

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

Prospector

Semantic versioning made simple. Calculate versions automatically from your git history using trunk-based development.

What is Prospector?

Prospector is a tool that generates semantic versions (semver) for your project based on git commits. Instead of manually updating version numbers, Prospector reads your git history and commit messages to calculate the current version automatically.

Perfect for:

  • Trunk-based development workflows
  • Automated CI/CD pipelines
  • Projects that want automatic versioning without manual intervention
  • Teams that need consistent versioning across repositories

Quick Start

# Install
pnpm add -D @arcmantle/prospector

# Get current version
prospector
# Output: 1.2.5

# See what's next
prospector --suggest minor
# Output: 1.3.0

Using in GitHub Actions

The primary way to use Prospector is in CI/CD pipelines. Add it to your workflow to automatically version your packages:

name: Publish Package

on:
  push:
    branches: [main]

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required: fetch all history for version calculation

      - uses: actions/setup-node@v4
        with:
          node-version: 24
          registry-url: https://registry.npmjs.org/

      # Calculate version using Prospector
      - name: Calculate version
        id: version
        uses: arcmantle/prospector@v1
        with:
          dir: '.'  # Optional: defaults to repository root

      # Use the calculated version
      - name: Set package version
        run: |
          echo "Setting version to ${{ steps.version.outputs.version }}"
          npm version ${{ steps.version.outputs.version }} --no-git-tag-version

      - name: Publish to NPM
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

GitHub Action Inputs

Input Description Default
dir Repository directory path '.'
branch Main branch name(s) 'main' (also recognizes 'master')
prefix Version tag prefix 'v'
no-commit-bumps Disable commit message bumping false

GitHub Action Outputs

Output Description
version The calculated semantic version (e.g., 1.2.5)
branch Current branch name
is-main-branch 'true' if on main branch, 'false' otherwise
commits-since-tag Number of commits since last version tag

Example: Conditional Publishing

Only publish on main branch with new commits:

- name: Calculate version
  id: version
  uses: arcmantle/prospector@v1

- name: Publish to NPM
  if: steps.version.outputs.is-main-branch == 'true' && steps.version.outputs.commits-since-tag != '0'
  run: npm publish
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Key Features

  • 🚀 Smart Version Calculation: Automatically determines versions from git history
  • 💬 Commit Message Bumps: Use [major], [minor], [patch] in commit messages for explicit version control
  • 🏷️ Tag-Based Releases: Create git tags for official version markers
  • 🌿 Branch-Aware: Different versioning for main branch vs feature branches
  • ⚡ Lightning Fast: Optimized for repositories with millions of commits
  • 🔧 Flexible: CLI tool or programmatic API
  • 📦 Zero Config: Works out of the box with sensible defaults
  • ⚙️ GitHub Action: Use in CI/CD pipelines (see GitHub Action docs)

How It Works

Three Simple Rules

  1. On main branch: Version increments based on commits and tags
  2. On feature branches: Version gets a prerelease suffix (e.g., 1.2.3-feature.5)
  3. Git tags set versions: Create tags like v1.0.0 to mark releases

Example

# Start: No tags, 10 commits on main
prospector → 0.0.10

# Tag a release
git tag v1.0.0

# Make 3 more commits
prospector → 1.0.3

# Create feature branch with 2 commits
git checkout -b feature/awesome
prospector → 1.0.1-feature-awesome.2

# Merge back to main
git checkout main
git merge feature/awesome
prospector → 1.0.4

Installation

# npm
npm install -D @arcmantle/prospector

# pnpm
pnpm add -D @arcmantle/prospector

# yarn
yarn add -D @arcmantle/prospector

Or use as a GitHub Action - see GitHub Action docs for details.

Usage

Command Line Interface

# Get the current version
prospector

# Output as JSON
prospector --json

# Show detailed information
prospector --detailed

# Suggest next version for a release
prospector --suggest minor
prospector --suggest major

# Use custom repository path
prospector --dir /path/to/repo

# Use custom branch name and tag prefix
prospector --branch master --prefix version-

# Default supports both 'main' and 'master' automatically
# No need to specify --branch unless using a different name

CLI Options

  • -h, --help - Show help message
  • -d, --dir <path> - Git repository path (default: current directory)
  • -b, --branch <name> - Main/trunk branch name (default: both main and master are recognized)
  • -p, --prefix <prefix> - Version tag prefix (default: v)
  • --json - Output as JSON
  • --detailed - Output detailed information
  • --suggest <type> - Suggest next version (major, minor, or patch)
  • --no-commit-bumps - Disable commit message based version bumping

Programmatic API

import { calculateVersion, suggestVersionBump } from '@arcmantle/prospector';

// Get current version
const info = await calculateVersion({
  cwd: '/path/to/repo',
  tagPrefix: 'v'
});

console.log(info.version);      // "1.2.5"
console.log(info.commitBumps);  // { major: 0, minor: 2, patch: 1, explicitVersion: null }

// Suggest next version
const next = await suggestVersionBump('minor');
console.log(next); // "1.3.0"

Configuration Options

await calculateVersion({
  // Repository path (default: current directory)
  cwd: '/path/to/repo',

  // Main branch names (default: ['main', 'master'])
  mainBranch: 'develop',
  // Or multiple: mainBranch: ['main', 'master', 'trunk'],

  // Tag prefix (default: 'v')
  tagPrefix: 'version-',

  // Enable commit message bumping (default: true)
  enableCommitBumps: true,

  // Custom commit patterns
  commitBumpPatterns: {
    major: /BREAKING|!:/i,
    minor: /feature:|feat:/i,
    patch: /fix:|bugfix:/i
  },

  // Progress callback
  onProgress: (msg) => console.log(msg)
});

Versioning Behavior

Main Branch (Trunk)

On your main branch, Prospector uses commit messages to determine version bumps:

Commit Message Markers

Add these markers to your commit messages for explicit version control:

Marker Effect Example
[major] Bump major version (2.0.0 → 3.0.0) git commit -m "[major] Breaking API changes"
[minor] Bump minor version (1.2.0 → 1.3.0) git commit -m "[minor] Add new feature"
[patch] Bump patch version (1.2.3 → 1.2.4) git commit -m "[patch] Fix bug"
[version:x.y.z] Set exact version git commit -m "[version:2.5.0] Release 2.5.0"
[v:x.y.z] Set exact version (short) git commit -m "[v:1.0.0] Initial release"

How Bumps Work

  • Explicit versions ([version:x.y.z]) take absolute precedence
  • Multiple bump indicators accumulate (e.g., 3 [minor] = +3 to minor version)
  • When major bumps, minor and patch reset to 0
  • When minor bumps, patch resets to 0
  • If no explicit bumps found, falls back to commit count

Optimized Scanning (Default)

Prospector is extremely fast even with millions of commits. It uses git's internal indexes to find only commits with bump markers:

Why it's fast:

  • Only fetches commits containing [major], [minor], [patch], or [version:x.y.z]
  • Uses git log --grep (built-in git optimization)
  • Early stops when explicit version found
  • Typical repositories: <1% of commits have markers

Performance examples:

  • 100K commits, 50 with markers → ~0.2s, ~500KB memory
  • 1M commits, 100 with markers → ~0.3s, ~1MB memory
  • 10M commits, 200 with markers → ~0.5s, ~2MB memory

No configuration needed - it just works fast out of the box!

Legacy Scan Limit (Optional)

For edge cases or specific performance requirements, you can enable a scan limit:

// Only if you experience performance issues
calculateVersion({ maxCommitScan: 1000 })

When maxCommitScan is set, Prospector falls back to the legacy scanning mode that fetches all commits up to the limit. This is generally not needed with the optimized approach.

Tagging Best Practices

Use git tags to mark official releases:

# Work with commit markers
git commit -m "[minor] Add feature A"
git commit -m "[patch] Fix bug"
git commit -m "[minor] Add feature B"

# Mark official release
git tag v1.5.0
git push --tags

# Continue development
git commit -m "[patch] Quick fix"
prospector → 1.5.1

Why tag?

  • Tags mark stable release points
  • Makes version history clear
  • Allows easy rollback to specific versions
  • Prevents hundreds of bump markers from accumulating

Feature Branches

Feature branches automatically get prerelease versions:

Format: {base_version}-{branch_name}.{commit_count}

Example:

# On main: v1.2.0 exists
git checkout -b feature/awesome-thing

# Make 3 commits
prospector → 1.2.1-feature-awesome-thing.3

How it works:

  1. Find last version tag (v1.2.0)
  2. Increment patch by 1 (1.2.1)
  3. Add branch name (sanitized) and commit count as prerelease

This makes it clear the version is:

  • Not an official release
  • Based on version 1.2.0
  • From the "feature-awesome-thing" branch
  • 3 commits ahead

No Tags (Initial State)

When no git tags exist:

  • Main branch: 0.0.{total_commits}
    • Example: 15 commits → 0.0.15
  • Feature branch: 0.0.1-{branch_name}.{total_commits}
    • Example: 8 commits on feature/init0.0.1-feature-init.8

Create your first tag when ready to release:

git tag v1.0.0
git push --tags

Advanced Usage

Disable Commit Bumps (Faster)

For maximum speed, disable commit message scanning:

# CLI
prospector --no-commit-bumps

# API
await calculateVersion({ enableCommitBumps: false })

This uses only commit count, no message parsing. Version is simply {tag} + {commit_count}.

Custom Commit Patterns

Match your team's commit conventions:

await calculateVersion({
  commitBumpPatterns: {
    major: /BREAKING CHANGE|!:/i,
    minor: /^feat:/i,
    patch: /^fix:/i
  }
});

Now commits like feat: add dashboard will bump minor version.

Legacy Scan Limit

If you experience issues (very rare), you can limit commit scanning:

await calculateVersion({
  maxCommitScan: 1000  // Only scan last 1000 commits
})

Note: This is rarely needed. The default optimized mode handles millions of commits efficiently.

API Reference

calculateVersion(options?)

Calculates the current version based on git history.

Parameters:

  • options.cwd - Repository path (default: '.')
  • options.mainBranch - Main/trunk branch name(s). Can be a string or array of strings (default: ['main', 'master'] - both are recognized as trunk branches)
  • options.tagPrefix - Tag prefix (default: 'v')
  • options.enableCommitBumps - Enable commit message bumping (default: true)
  • options.maxCommitScan - Optional limit on commits to scan. Only set this if you experience performance issues. When undefined (default), uses optimized git grep scanning with no limit.
  • options.commitBumpPatterns - Custom regex patterns for detecting bumps

Returns: VersionInfo object with:

  • version - The calculated version string
  • branch - Current branch name
  • isMainBranch - Whether on main branch
  • commitsSinceTag - Number of commits since last tag
  • lastTag - Last version tag info (or null)
  • currentCommit - Current commit hash
  • commitBumps - Object with { major, minor, patch, explicitVersion } counts from commit messages

suggestVersionBump(bumpType, options?)

Suggests the next version for a given bump type.

Parameters:

  • bumpType - 'major', 'minor', or 'patch'
  • options - Same as calculateVersion

Returns: Version string (e.g., '2.0.0')

Performance

Prospector is extremely fast even with millions of commits:

Why It's Fast

  • Optimized git operations: Uses git log --grep to scan only commits with bump markers
  • Typical case: <1% of commits have version markers, so we only process <1%
  • Early termination: Stops when explicit version found
  • Smart counting: Uses git rev-list --count (O(1) operation)

Performance Data

Repository Size Commits with Bumps Time Memory
1K commits 20 < 0.1s < 1MB
10K commits 50 < 0.2s < 1MB
100K commits 100 < 0.3s < 2MB
1M commits 200 < 0.5s < 3MB
10M commits 500 < 1s < 5MB

With --no-commit-bumps: All sizes run in < 0.1s.

Quick Reference

Commit Marker Syntax

# Major version bump (1.0.0 → 2.0.0)
git commit -m "[major] Breaking API changes"

# Minor version bump (1.0.0 → 1.1.0)
git commit -m "[minor] New reporting feature"

# Patch version bump (1.0.0 → 1.0.1)
git commit -m "[patch] Fix bug in login"

# Set exact version
git commit -m "[version:2.5.0] Release version 2.5.0"
git commit -m "[v:1.3.7] Align with upstream"

# No explicit bump (uses commit count for patch)
git commit -m "Update README"

See docs/COMMIT_SYNTAX.md for more details on commit message patterns.

Common CLI Commands

# Get current version
prospector

# Show detailed information including commit bumps
prospector --detailed

# Disable commit message bumping
prospector --no-commit-bumps

# Suggest next minor release
prospector --suggest minor

# Use in different directory
prospector --dir /path/to/repo

License

MIT

About

Automatic semantic versioning from git history for trunk-based development

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors