Skip to main content

Stamping utility

Project description

vmn

Automatic semantic versioning powered by git tags. Zero lock-in.

Language-agnostic CLI for versioning, multi-repo state recovery, and experiment tracking -- all stored in git.

PyPI version PyPI downloads GitHub stars Semver Conventional Commits License

Python Rust Go C++ Java JavaScript


pip install vmn

vmn stamp -r patch my_app                 # => 0.0.1  (auto-initializes repo + app)
vmn stamp -r minor my_app                 # => 0.1.0
vmn stamp -r patch --pr rc my_app         # => 0.1.1-rc.1  (prerelease)
vmn release my_app                        # => 0.1.1
vmn goto -v 0.1.0 my_app                  # repo restored to exact 0.1.0 state

Versions live in git annotated tags. Uninstall vmn and the tags still make sense. No databases, no SaaS, no ecosystem buy-in.


Requirements · Quick Start · Why vmn? · Only in vmn · Experiments · Snapshots · Commands · Auto-Embedding · Configuration · CI · Troubleshooting · Migration


vmn is for you if:

Any language — Python, Rust, Go, C++, Java, JS, or anything with a git repo Microservices — independent versions per service, one root counter
Multi-repo — reproducible state recovery across repositories Zero config — no plugins, no pipelines, no ecosystem buy-in
Offline / air-gapped — works without network access Zero lock-in — versions live in plain git tags
ML experiments — reproducible snapshots with metrics, no tracking server

No separate vmn init required -- vmn stamp auto-initializes on first run. Works in CI (handles shallow clones automatically).

How it works

git commitvmn stampgit taggit push

vmn stores all version state in git annotated tag messages as plain YAML. When you vmn stamp, it computes the next version, writes it into a tag (e.g., my_app_1.2.0), and optionally pushes. When you vmn show, it reads that tag. There is no database, no config service, no proprietary format -- just git tags you can inspect with git tag -n1.

Try it locally (30 seconds):

pip install vmn

mkdir remote && cd remote && git init --bare && cd ..
git clone ./remote ./local && cd local
echo a >> ./a.txt && git add ./a.txt && git commit -m "first commit" && git push origin master

vmn stamp -r patch my_app   # => 0.0.1

echo b >> ./a.txt && git add ./a.txt && git commit -m "feat: add b" && git push origin master
vmn stamp -r patch my_app   # => 0.0.2

git tag -n1 my_app_0.0.2    # version metadata right there in the tag

📋 Requirements

  • Python 3.8+
  • Git 2.10+ (for push options support; 2.17+ recommended)
  • Platforms: Linux, macOS, Windows (including WSL). No platform-specific configuration needed -- vmn uses GitPython for cross-platform git operations.

🚀 Quick Start

Version a project

pip install vmn
cd your-project                           # any git repo

vmn stamp -r patch my_app                 # => 0.0.1 (auto-initializes)
vmn stamp -r minor my_app                 # => 0.1.0
vmn show my_app                           # => 0.1.0
vmn stamp -r patch --pr rc my_app         # => 0.1.1-rc.1 (prerelease)
vmn release my_app                        # => 0.1.1

Track an ML experiment (60 seconds)

# You're mid-experiment, code is dirty, results look promising
vmn exp create my_model --note "baseline CNN" --metrics loss=0.45 acc=0.85
# => 0.1.0-dev.a1b2c3d.e4f5g6h

# Try a different approach...
vmn exp create my_model --note "with dropout" --metrics loss=0.34 acc=0.91
# => 0.1.0-dev.f7a2b1c.d3e4f5g

# Compare results
vmn exp compare --latest 2 my_model
# metric        0.1.0-dev.a1b2c3  0.1.0-dev.f7a2b1
# --------------------------------------------------
# loss          0.45              0.34
# acc           0.85              0.91

# Winner! Restore that exact state (checkout + apply uncommitted changes)
vmn exp restore --latest my_model

Both workflows store everything in git -- no servers, no lock-in.


⚡ Why vmn?

vmn does everything semantic-release and release-please do -- plus 9 things nothing else does.

Capability vmn semantic-release release-please changesets
Language-agnostic :white_check_mark: JS-centric JS-centric JS-only
Git-tag source of truth :white_check_mark: :white_check_mark: :white_check_mark: :x:
Conventional commits + changelog :white_check_mark: :white_check_mark: :white_check_mark: :x: / :white_check_mark:
GitHub Release creation :white_check_mark: :white_check_mark: :white_check_mark: :x:
Auto-embed version (npm, Cargo, pyproject, any file) :white_check_mark: per-plugin :x: JS only
Multi-repo dependency tracking :white_check_mark: :x: :x: :x:
State recovery (vmn goto) :white_check_mark: :x: :x: :x:
Microservice / root app topology :white_check_mark: :x: :x: monorepo only
4-segment hotfix versioning :white_check_mark: :x: :x: :x:
Zero-config start (auto-init) :white_check_mark: :x: :x: :x:
Offline / air-gapped :white_check_mark: :x: :x: :x: *
Zero lock-in (pure git tags) :white_check_mark: :x: :x: :x:
Dev snapshots (uncommitted state capture) :white_check_mark: :x: :x: :x:
ML experiment tracking :white_check_mark: :x: :x: :x:

Bold rows = only vmn.

* changesets works offline for authoring, but requires GitHub/npm for publishing.

Detailed comparisons & migration guides

See Already using another tool? for step-by-step migration paths from semantic-release, release-please, setuptools-scm, standard-version, and bump2version.


🧪 Why vmn for ML experiments?

Most experiment trackers require a server, a cloud account, or both. vmn tracks experiments the same way it tracks versions -- in git and local files.

vmn exp create my_model --metrics accuracy=0.94 loss=0.12 --note "baseline ResNet run"
# => 0.2.0-dev.c3d4e5f.a1b2c3d

vmn exp list my_model --sort loss --top 3
# [1] 0.2.0-dev.c3d4e5f.a1b2c3d  (5m ago)  loss=0.12  accuracy=0.94 - baseline ResNet run
# [2] 0.1.0-dev.f7a2b1c.d3e4f5g  (2d ago)  loss=0.34  accuracy=0.91 - with dropout
# [3] 0.1.0-dev.a1b2c3d.e4f5g6h  (3d ago)  loss=0.45  accuracy=0.85 - baseline CNN

vmn exp restore --latest my_model         # checkout exact code state

How vmn compares to dedicated experiment trackers

Capability vmn MLflow W&B DVC Neptune
No server required :white_check_mark: :x: * :x: :white_check_mark: :x:
No cloud account :white_check_mark: :white_check_mark: (self-hosted) :x: :white_check_mark: :x:
Free & open source :white_check_mark: :white_check_mark: Free tier :white_check_mark: Free tier
Metrics tracking :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
Experiment comparison CLI table Web UI Web UI CLI Web UI
Full code state capture :white_check_mark: :x: :x: partial ** :x:
Uncommitted changes captured :white_check_mark: :x: :x: :x: :x:
One-command state restore :white_check_mark: :x: :x: :x: :x:
Built-in version management :white_check_mark: :x: :x: :x: :x:
Artifact tracking :white_check_mark: (SHA256) :white_check_mark: :white_check_mark: :white_check_mark: :white_check_mark:
Works offline / air-gapped :white_check_mark: self-hosted only :x: partially :x:
Install complexity pip install vmn server + DB account + pip pip + git config account + pip
Storage git + local/S3 database cloud git/S3 cloud
Lock-in zero (git tags + files) MLflow format W&B cloud DVC format Neptune cloud

Bold rows = only vmn. Capture your exact working state -- dirty files, local commits, everything -- and restore it with one command.

* MLflow Tracking can log to local files without a server, but the comparison UI requires mlflow server. ** DVC tracks data/model files via git, but does not capture uncommitted code changes or local-only commits.

vmn is not trying to replace MLflow's web dashboard or W&B's visualization suite. It's for researchers who want lightweight, git-native experiment tracking that lives alongside their version management -- without spinning up servers, creating cloud accounts, or leaving the terminal.

When to use what

Use vmn when:

  • You want a CLI-first workflow with no context switching
  • You work offline or in air-gapped environments
  • You need version management and experiment tracking in one tool
  • You want zero infrastructure -- no servers, no databases, no accounts
  • You prefer git-native storage with no vendor lock-in

Use MLflow / W&B when:

  • You need rich web visualizations and interactive charts
  • Your team relies on shared dashboards and collaboration features
  • You are already invested in their ecosystem and integrations

Eight subcommands cover the full experiment lifecycle:

Command What it does
vmn exp create Capture a snapshot with metrics, parameters, and notes
vmn exp add Log additional metrics, notes, or artifacts to an experiment
vmn exp list List experiments with filtering and sorting by any metric
vmn exp show Display full experiment details including log history
vmn exp compare Side-by-side metric comparison across experiments
vmn exp restore Restore the exact code state -- checkout + apply patches
vmn exp export Export experiment as a directory or tarball
vmn exp prune Clean up old experiments (keep N or older than duration)

🔑 What only vmn does

State recovery -- a time machine for your repo

vmn goto -v 1.2.3 my_app

Your entire repository -- plus every tracked dependency -- is now at exactly the state when 1.2.3 shipped. No digging through CI logs, no guessing which commit broke prod. Reproduce bugs in seconds, not hours.

Multi-repo snapshots -- reproducible builds across repositories

If your product spans multiple git repos, vmn records the exact commit hash of every dependency at stamp time. vmn goto restores all of them in parallel:

vmn stamp -r minor my_app
# records: my_app @ abc123, lib_core @ def456, lib_utils @ 789fed

# six months later
vmn goto -v 0.1.0 my_app    # all 3 repos restored to recorded commits

Dependencies are declared in .vmn/my_app/conf.yml and auto-cloned if missing -- up to 10 in parallel. One command, full reproducibility.

Microservice topology -- one umbrella, independent versions

Version multiple services under one root app. Each service has its own semver; the root gets an auto-incrementing integer that ticks on every child stamp:

vmn stamp -r patch my_platform/auth      # auth => 0.0.1, root => 1
vmn stamp -r minor my_platform/billing   # billing => 0.1.0, root => 2
vmn stamp -r patch my_platform/auth      # auth => 0.0.2, root => 3

vmn show --root my_platform              # => 3

Deploy auth and billing independently while the root version gives you a single monotonic counter for "what changed last." Perfect for Kubernetes manifests and release notes that span services.

Zero lock-in -- it is just git tags

All version state lives in annotated git tag messages as plain YAML. There are no proprietary databases, no SaaS dashboards, no JSON files you have to keep in sync. Uninstall vmn tomorrow and your tags still make perfect sense:

git tag -l 'my_app_*'          # every version, right there
git tag -n1 my_app_1.2.3       # full stamp metadata in the tag message

Switch to a different tool, read the tags with a shell script, or parse them in CI -- the data is yours.

Version formats -- full semver plus hotfix

Standard Semver 2.0 plus an optional 4th hotfix segment and dev snapshots for every workflow:

1.6.0                              # stable release
1.6.0-rc.23                        # prerelease
1.6.7.4                            # hotfix (4th segment)
1.6.0-rc.23+build01.Info           # build metadata
1.6.0-dev.a1b2c3d.e4f5g6h          # dev snapshot

The hotfix segment lets you ship emergency fixes without bumping patch, keeping your release train on schedule while production stays safe.


🧬 Experiment Management

vmn experiment (alias: vmn exp) adds git-native experiment tracking to any versioned app. No servers, no databases -- experiments are stored alongside your tags and snapshots. Every experiment ties back to an exact version and commit, so reproducing results is a vmn exp restore away.

Quick workflow

# Create an experiment pinned to a version
vmn exp create my_model --note "baseline CNN" --metrics loss=0.45 acc=0.85

# Train more, append metrics and artifacts
vmn exp add my_model --latest --metrics loss=0.31 acc=0.92 --attach weights.pt

# Compare the last 3 experiments side by side
vmn exp compare my_model --latest 3

# Restore the best run (checkout code + retrieve artifacts)
vmn exp restore my_model -v 1.2.0

Subcommand reference

create

Start a new experiment. This is the default action when no subcommand is given.

vmn exp create my_model --note "dropout 0.3" --metrics loss=0.45 acc=0.85
vmn exp create my_model -f params.yml --attach initial_weights.pt -v 1.2.0

add

Append metrics, notes, or artifacts to an existing experiment.

vmn exp add my_model -v 1.2.0 --metrics val_loss=0.29 val_acc=0.93
vmn exp add my_model --latest --attach checkpoint_epoch10.pt --note "after LR warmup"

list

List experiments, optionally sorted by a metric.

vmn exp list my_model                          # all experiments
vmn exp list my_model --sort loss --top 5      # best 5 by loss
vmn exp list my_model --latest 10              # most recent 10

show

Display full details for a single experiment.

vmn exp show my_model -v 1.2.0
vmn exp show my_model --latest

compare

Side-by-side metric comparison across experiments.

vmn exp compare my_model -v 1.1.0 -v 1.2.0 -v 1.3.0
vmn exp compare my_model --latest 3 --tool delta   # use external diff tool

Falls back to git config diff.tool if --tool is not given.

restore

Check out the exact code state and retrieve artifacts for an experiment.

vmn exp restore my_model -v 1.2.0
vmn exp restore my_model --latest

export

Package an experiment (metadata, metrics, artifacts) into a tarball.

vmn exp export my_model -v 1.2.0                     # writes 1.2.0.tar.gz
vmn exp export my_model --latest -o best_run.tar.gz

prune

Clean up old experiments by count or age.

vmn exp prune my_model --keep 10           # keep the 10 most recent
vmn exp prune my_model --older-than 30d    # remove experiments older than 30 days

Structured notes

Pass a YAML file with -f to attach structured metadata to any experiment:

# params.yml
hypothesis: "larger batch size improves convergence"
params:
  lr: 0.001
  batch_size: 64
  epochs: 50
tags: [baseline, transformer-v2]
vmn exp create my_model -f params.yml --metrics loss=0.38

Metrics schema

Define sort order and a primary metric in .vmn/{app_name}/conf.yml so that list --sort and compare know which direction is better:

experiment:
  metrics:
    loss: {sort: desc, primary: true}
    acc:  {sort: desc}
    val_loss: {sort: desc}

Storage

Experiments are stored locally by default under .vmn/. For team-wide sharing, point to an S3-compatible backend:

S3 / MinIO storage flags

All subcommands accept these flags:

vmn exp create my_model --backend s3 --bucket my-experiments \
    --endpoint-url http://minio:9000 --prefix team/ml
Flag Default Description
--backend local local or s3
--bucket -- S3 bucket name
--endpoint-url -- Custom endpoint (MinIO, LocalStack, etc.)
--prefix vmn-experiments Key prefix inside the bucket

📸 Snapshots

Snapshots capture your exact working state -- uncommitted changes, local commits, untracked files -- into a deterministic dev version you can restore later. No WIP commits, no stash juggling. The newer vmn experiment command builds on this primitive for structured experiment tracking.

Dev version format

Every snapshot produces a version string derived from the content itself:

{base_version}-dev.{commit_hash}.{diff_hash}

Identical code state always produces the identical version string. If nothing changed, you get the same snapshot version back.

Quick start

vmn snapshot create my_model --note "promising results"
# => 1.2.0-dev.a1b2c3d.e4f5g6h

vmn snapshot list my_model
# [1] 1.2.0-dev.a1b2c3d.e4f5g6h  (2h ago) - promising results
# [2] 1.2.0-dev.x9y8z7w.q1r2s3t  (1d ago)

vmn snapshot show --latest my_model
vmn snapshot note --latest --note "confirmed: best run" my_model
vmn snapshot diff -v 1.2.0-dev.a1b --to current my_model
vmn snapshot export --latest -o ./experiment_42 my_model

# Restore via goto
vmn goto -v 1.2.0-dev.a1b2c3d.e4f5g6h my_model
What's stored in a snapshot
.vmn/{app}/snapshots/{version}/
  metadata.yml           # version, branch, timestamp, note, dirty states
  working_tree.patch     # uncommitted changes (git diff HEAD)
  local_commits.patch    # local commits not yet pushed
  untracked_files.tar.gz # untracked files
  deps/{dep_name}/...    # dependency patches (same structure)
  artifacts/{filename}   # attached files

Everything needed to reconstruct the working tree is stored alongside the metadata. Dependencies get their own patch set so multi-repo states restore correctly with vmn goto.

All snapshot flags
Flag Description
-v, --version Target a specific snapshot version (supports prefix matching)
--latest Use the most recent snapshot
--note Attach or update a text note
--to Second version for diff (or current for working tree)
--tool External diff tool (meld, vimdiff, etc.). Falls back to git config diff.tool
-o, --output Export destination path
--meta Repeatable key=value metadata pairs
--meta-file YAML file with additional metadata
--filter Repeatable key=value pairs for filtering list output
--backend Storage backend: local (default) or s3
--bucket S3 bucket name
--endpoint-url Custom S3 endpoint (MinIO, DigitalOcean Spaces, etc.)
--prefix S3 key prefix (default: vmn-snapshots)
--verbose Show extended snapshot details
Storage backends

Local (default) -- snapshots live under .vmn/{app}/snapshots/ in the repository. Nothing to configure.

S3 -- push snapshots to any S3-compatible store:

vmn snapshot create my_model --backend s3 --bucket my-snapshots
vmn snapshot list my_model --backend s3 --bucket my-snapshots

Custom endpoints work for MinIO, DigitalOcean Spaces, and similar services:

vmn snapshot create my_model \
  --backend s3 \
  --bucket dev-snapshots \
  --endpoint-url http://localhost:9000 \
  --prefix team/ml

🔧 Commands

init-app and stamp both support --dry-run for safe testing.

Command What it does Example
vmn stamp Create a new version vmn stamp -r patch my_app
vmn release Promote prerelease to final vmn release my_app
vmn show Display version info vmn show my_app
vmn goto Checkout repo at a version vmn goto -v 1.2.3 my_app
vmn snapshot Capture dev state (uncommitted work) vmn snapshot create my_app
vmn experiment Track ML experiments (alias: exp) vmn exp create my_model --metrics loss=0.34
vmn gen Generate file from template vmn gen -t ver.j2 -o ver.txt my_app
vmn add Attach build metadata vmn add -v 1.0.0 --bm build42 my_app
vmn config Edit app config (TUI) vmn config my_app
vmn init Initialize repo/app vmn stamp auto-inits -- rarely needed

vmn stamp

vmn stamp -r patch my_app                 # => 0.0.1
vmn stamp -r minor my_app                 # => 0.1.0
vmn stamp -r patch --pr rc my_app         # => 0.1.1-rc.1 (prerelease)
vmn stamp my_app                          # => 0.1.2 (with conventional_commits -- no -r needed)
vmn stamp --dry-run -r patch my_app       # preview without committing
vmn stamp --pull -r patch my_app          # pull before stamping (retries on conflict)

Idempotent -- won't re-stamp if the current commit already matches. Auto-initializes repo and app on first run.

Stamping without -r, -r vs --orm, and all flags

Without -r: only works during a prerelease sequence (continues the existing prerelease). Errors on a release commit. Exception: with conventional_commits enabled, -r is always optional -- the release mode is inferred from commit messages.

-r vs --orm:

Flag Behavior
-r patch Strict -- always advances. 0.0.1 -> 0.0.2. 0.0.2-rc.3 -> 0.0.3.
--orm patch Optional -- advances only if no prerelease exists at the target version.

All stamp flags:

Flag Description
-r, --release-mode Release mode: major, minor, patch, hotfix, micro
--orm, --optional-release-mode Optional release mode: major, minor, patch, hotfix
--pr, --prerelease Create a prerelease (e.g., --pr rc produces X.Y.Z-rc.N)
--pull Pull remote before stamping; retries on conflict
--dry-run Preview the version without committing or tagging
-e, --extra-commit-message Append extra text to the stamp commit message
--ov, --override-version Force a specific version string
--orv, --override-root-version Force a specific root-app version
--dont-check-vmn-version Skip the vmn version compatibility check

vmn init-app flags:

Flag Description
-v, --version Initial version (default 0.0.0)
--dry-run Preview without writing
--orm, --default-release-mode Set default release mode: optional or strict

Stamp automation (conventional commits, changelog, GitHub releases)

Enable conventional_commits and never type -r again. Commit prefixes map directly to release modes: fix: -> patch, feat: -> minor, BREAKING CHANGE or ! after type -> major.

git commit -m "feat: add search endpoint"
vmn stamp my_app    # => 0.2.0 (minor, auto-detected)
conf:
  conventional_commits: true
  default_release_mode: optional
  changelog:
    path: "CHANGELOG.md"
  github_release:
    draft: false
vmn release
vmn release -v 0.0.1-rc.1 my_app   # explicit version -- tag-only
vmn release my_app                  # auto-detect from current commit
vmn release --stamp my_app          # full stamp flow -- new commit + tag + push

Promotes prerelease to final. Idempotent. -v and --stamp are mutually exclusive.

vmn show
vmn show my_app              # current version
vmn show --dev my_app        # dev version with commit+diff hash
vmn show --verbose my_app    # full YAML metadata dump
vmn show --raw my_app        # without template formatting
vmn show --type my_app       # release / prerelease / metadata
vmn show -u my_app           # unique ID (version+commit_hash)
vmn show --conf my_app       # show app configuration
vmn show --root my_root_app  # root app version (integer)
vmn goto
vmn goto -v 1.2.3 my_app              # repo + deps restored to exact state
vmn goto my_app                        # latest version on current branch
vmn goto -v 1.2.3 --deps-only my_app  # only checkout dependencies
vmn goto -v 5 --root my_root_app      # checkout to root app version
vmn goto -v 1.2.0-dev.a1b2c3d.e4f5g6h my_model  # restore dev snapshot

Deps auto-cloned if missing. Dev restore: checkout base, replay commits, apply working tree patch.

vmn gen
vmn gen -t version.j2 -o version.txt my_app
vmn gen -t version.j2 -o version.txt -c custom.yml my_app

Template variables: version, base_version, name, release_mode, prerelease, previous_version, stamped_on_branch, release_notes, changesets, root_name, root_version, root_services.

vmn add
vmn add -v 0.0.1 --bm build42 my_app

Attaches build metadata to an existing version tag (e.g. 0.0.1+build42). Optional --vmp for metadata file path.

vmn config
vmn config                  # list all managed apps
vmn config my_app           # interactive TUI editor
vmn config my_app --vim     # open in $EDITOR
vmn config --branch my_app  # branch-specific override
vmn config --global         # repo-level .vmn/conf.yml
Release candidates

Iterate on prereleases, then promote:

vmn stamp -r major --pr alpha my_app    # 2.0.0-alpha.1
vmn stamp --pr alpha my_app             # 2.0.0-alpha.2
vmn stamp --pr mybeta my_app            # 2.0.0-mybeta.1
vmn release my_app                      # 2.0.0
Python library

vmn can be called programmatically. vmn_run accepts a command-line argument list and returns (exit_code, context):

from version_stamp.cli.entry import vmn_run

ret, ctx = vmn_run(["show", "my_app"])

Note: vmn_run prints to stdout/stderr. To capture output programmatically, wrap calls with contextlib.redirect_stdout/redirect_stderr.

Environment variables
Variable Description
VMN_WORKING_DIR Override working directory
VMN_LOCK_FILE_PATH Custom lock file path (default: per-repo)
GITHUB_TOKEN / GH_TOKEN Required for GitHub Releases

📦 Version Auto-Embedding

vmn stamp can automatically write the stamped version into your project files. Add a version_backends section to .vmn/<app>/conf.yml:

Backend File What it updates
npm package.json version field
cargo Cargo.toml version field
poetry pyproject.toml [tool.poetry].version
pep_621 pyproject.toml [project].version
version_backends:
  npm:
    path: "relative/path/to/package.json"
Hatchling / uv dynamic versioning (hatch-vcs)

Instead of file injection, read the version directly from git tags at build time:

[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[tool.hatch.version]
source = "vcs"
tag-pattern = "my_app_(?P<version>.*)"

No version_backends entry needed -- the build tool pulls the version from the tag vmn created.

Generic backends (regex and Jinja2)

generic_selectors -- regex find-and-replace in any file:

version_backends:
  generic_selectors:
    - paths_section:
        - input_file_path: in.txt
          output_file_path: in.txt
      selectors_section:
        - regex_selector: '(version: )(\d+\.\d+\.\d+)'
          regex_sub: \1{{version}}

Use {{VMN_VERSION_REGEX}} to match any vmn version string (playground).

generic_jinja -- render a Jinja2 template:

version_backends:
  generic_jinja:
    - input_file_path: f1.jinja2
      output_file_path: jinja_out.txt
      custom_keys_path: custom.yml

Same variables as vmn gen.


⚙️ Configuration

vmn auto-generates .vmn/<app-name>/conf.yml when an app is first stamped. Edit it directly or use vmn config (see Commands). Full conf.yml structure:

conf:
  template: '[{major}][.{minor}][.{patch}][.{hotfix}][-{prerelease}][.{rcn}][-dev.{dev_commit}.{dev_diff_hash}][+{buildmetadata}]'
  hide_zero_hotfix: true
  extra_info: false
  create_snapshots: false
  conventional_commits: true
  default_release_mode: optional   # "optional" (--orm) or "strict" (-r required). Top-level key, not nested under conventional_commits.
  changelog:
    path: "CHANGELOG.md"
  github_release:
    draft: false
  deps:
    ../:
      other_repo:
        vcs_type: git
  version_backends:
    npm:
      path: "package.json"
  policies:
    whitelist_release_branches: ["main"]
  snapshot_storage:
    backend: local
    bucket: my-bucket
    prefix: vmn-snapshots
    endpoint_url: https://...
  experiment:
    metrics:
      loss: { sort: desc, primary: true }
      acc:  { sort: desc }

Migration note: create_verinfo_files has been renamed to create_snapshots. The old key still works but prints a deprecation warning.

Per-branch configuration. Place {branch}_conf.yml next to conf.yml in .vmn/<app>/. vmn checks for a branch-specific config first and falls back to conf.yml. Stale branch configs from other branches are auto-cleaned on stamp.

🔄 CI Integration

Use the official GitHub Action for stamping in CI pipelines:

steps:
  - uses: actions/checkout@v4
    with:
      fetch-depth: 0          # vmn needs full history
  - uses: progovoy/vmn-action@latest
    with:
      app-name: my_app
      do-stamp: true
      stamp-mode: patch
    env:
      GITHUB_TOKEN: ${{ github.token }}

fetch-depth: 0 is required -- vmn reads git tags and history to compute the next version.


🗺️ Roadmap

vmn is actively developed. File an issue to vote or suggest.

  • vmn exp plot -- ASCII metric charts in the terminal (vmn exp plot --metric loss my_model)
  • Monorepo auto-discovery -- detect apps from Cargo workspaces, pnpm-workspace.yaml, Python namespace packages
  • PR version annotation -- GitHub Action that auto-comments the next version using vmn stamp --dry-run
  • Post-stamp hooks -- run custom commands after a successful stamp (deploy, notify, update docs)
  • Homebrew tap -- brew install vmn

See the full roadmap and backlog for more.


🔍 Troubleshooting

vmn can't find tags / shows wrong version

Most CI systems default to shallow clones. vmn needs full history and tags:

# GitHub Actions
- uses: actions/checkout@v4
  with:
    fetch-depth: 0    # full history

Or manually: git fetch --tags --unshallow

"Another vmn process is running" / lock file error

vmn uses a per-repo lock file to prevent concurrent stamps. If a previous run crashed:

rm .vmn/.vmn.lock           # default location
# or if VMN_LOCK_FILE_PATH is set:
rm "$VMN_LOCK_FILE_PATH"
Tag name collision with existing tags

vmn tags follow the format {app_name}_{version}. If your repo already has tags matching this pattern, either rename the app or clean up conflicting tags before the first stamp.

"Dirty" state warnings on stamp

vmn checks for uncommitted changes and unpushed commits. To stamp despite dirty state, commit or stash your changes first. vmn show --verbose shows the exact dirty flags (pending, outgoing, detached).


🔀 Already using another tool?

Step-by-step guides for common migration paths:


Ready to stop fighting your versioning tools?

pip install vmn

Star the repo if vmn saved you time. File an issue if it didn't -- we'll fix it.

Contributing   Report an issue   PyPI

Add the badge to your project:
[![vmn: automatic versioning](https://img.shields.io/badge/vmn-automatic%20versioning-blue)](https://github.com/progovoy/vmn)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vmn-0.9.1rc7-py3-none-any.whl (114.0 kB view details)

Uploaded Python 3

File details

Details for the file vmn-0.9.1rc7-py3-none-any.whl.

File metadata

  • Download URL: vmn-0.9.1rc7-py3-none-any.whl
  • Upload date:
  • Size: 114.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for vmn-0.9.1rc7-py3-none-any.whl
Algorithm Hash digest
SHA256 ff9b670318ce586b6ece314bb451e5c9f7712ba75c432d8b31387212284e97fe
MD5 385ae182f8c51dfdffc74faa90c57f15
BLAKE2b-256 8856ae5a5be1553be29071c80181124be63ce1124481c510f3349a5a65c9f5ea

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page