PV

progovoy/vmn

Developer tools
70 stars Quality 71 Trend 20MIT

git checkout for releases that span many repos. Version tags that restore your whole product. No server, no database, any language.

Overview

git checkout for releases that span many repos. Record a release once. Put the app and every repo it depends on back to exactly that state with one command. No server. No database. Any language. Production broke after last Tuesday's 2.1.0 deploy. Your product is four repos. Which commit of each one actually shipped? Without vmn, that is an afternoon of CI logs and container tags. With vmn: vmn stores release metadata as readable YAML in annotated Git tags. Each tag records the application revision, dependency revisions, previous version, and release context. There is no vmn server and no external metadata database. Developed continuously since 2019, vmn is used in daily production workflows by teams at large companies managing multi-repository products. vmn versions its own releases. The repository contains more than 400 tests, including Docker-backed multi-repository, recovery, and compatibility scenarios. If vmn saves you an afternoon, a ⭐ helps other teams find it.

README

vmn

git checkout for releases that span many repos.

Record a release once. Put the app and every repo it depends on back to exactly that state with one command. No server. No database. Any language.

Production broke after last Tuesday’s 2.1.0 deploy. Your product is four repos. Which commit of each one actually shipped? Without vmn, that is an afternoon of CI logs and container tags. With vmn:

vmn goto -v 2.1.0 my_platform   # every repo back at the commit that shipped

vmn stores release metadata as readable YAML in annotated Git tags: the application revision, every dependency’s revision and remote, the previous version, and release context. There is no vmn server and no external metadata database.

Developed continuously since 2019, vmn is used in daily production workflows by teams at large companies managing multi-repository products. vmn versions its own releases. The repository contains more than 400 tests, including Docker-backed multi-repository, recovery, and compatibility scenarios.

If vmn saves you an afternoon, a ⭐ helps other teams find it.

Quick start · Why vmn · Multi-repository recovery · Release models · Configuration · Operations · Commands · Documentation

Why vmn

Requirement What vmn provides
Recover a recorded multi-repository source state vmn goto restores the application and its configured dependencies to their recorded Git revisions.
Keep release data inspectable Annotated tags contain readable YAML and use the namespaced form _.
Version mixed technology stacks vmn operates on Git repositories, not a language-specific package manager or build system.
Release services independently Root apps group independently versioned services under a monotonic composition version.
Work without a hosted control plane A standard Git remote is enough; internal and air-gapped Git servers are supported.
Adopt without replacing build tooling Version backends update npm, Cargo, Poetry, PEP 621, Jinja2, or regex-selected files.

A version is a handle to recorded source state, not only a string:

State Command Captures
Release vmn stamp → vmn goto Committed application and dependency revisions
Working vmn snapshot Release state plus local commits, tracked changes, and untracked files

Scope: vmn restores recorded source revisions. It does not rebuild artifacts, capture toolchains or runtime infrastructure, sign tags, or deploy software. Keep those responsibilities in your build, signing, and deployment pipeline.

Quick start

Requirements: Python 3.8+, Git 2.10+ (2.17+ recommended), and a Git repository with at least one commit and a writable remote.

pipx install vmn            # or: uv tool install vmn
vmn --completion-install    # optional: bash/zsh/fish/tcsh, auto-detected

Inside any Git repository:

vmn stamp -r patch my_app          # 0.0.1; initializes the repo and app on first use
vmn show my_app                    # 0.0.1

# After committing the next change:
vmn stamp -r minor my_app          # 0.1.0

# After committing another change:
vmn stamp -r patch --pr rc my_app  # 0.1.1-rc.1
vmn release my_app                 # 0.1.1

A stamp creates a version commit and annotated tags, then pushes the branch and tags. --dry-run previews it. Stamping an already-versioned state is idempotent. No separate vmn init is needed; init and init-app -v remain for migrations and non-default starting versions.

Inspect the source of truth directly:

git tag --list 'my_app_*'
git cat-file -p my_app_0.1.0
vmn show --verbose my_app

Multi-repository recovery

Declare dependency repositories in .vmn/my_app/conf.yml, keyed by their directory relative to the app repository:

conf:
  deps:
    ../:
      lib_core:
        vcs_type: git
      service_api:
        vcs_type: git
        branch: main       # optional pin, checked before every stamp

Every stamp records each dependency’s exact revision and remote. Later, from any revision:

vmn goto -v 1.4.0 my_app

goto checks out every recorded repository and clones any that are missing. --pull fetches first when the version is not available locally; --deps-only leaves the application repository unchanged; without -v it returns to the tip of the current branch.

Islands: app and deps side by side

vmn wt (alias of vmn worktrees) builds an island: git worktrees for the application and every dependency, laid out like the originals, plus an island.json manifest. Each checkout starts on a private island// branch that follows its source branch but cannot be pushed; stamping is refused on it.

vmn wt create my_app --island-name feat                   # from the current commits
vmn wt create my_app --island-name feat --carry-changes   # ...plus uncommitted work
vmn wt pull                                               # rebase onto the source branches
vmn wt freeze my_app                                      # pin deps to the branches they are on
vmn wt remove feat

To share the work, check out a real branch in each repo you changed (git checkout -b feature/x && git push -u origin feature/x) and run vmn wt freeze in the application. It pins those dependency branches in the current branch’s conf, so a colleague who checks out feature/x and runs vmn wt create gets the same dependency state. -fv 2.1.0 builds an island at a recorded version instead.

Release models

1.6.0                         release
1.6.0-rc.23                   prerelease
1.6.7.4                       optional fourth hotfix segment
1.6.0-rc.23+build01           build metadata (vmn add)
1.6.0-dev.a1b2c3d.e4f5g6h     recorded working state (a snapshot)

How the release mode is chosen, first match wins:

  1. -r (strict: always bumps) or --orm (optional: only advances if no prerelease exists at the target) on the command line.
  2. Conventional Commits since the last version — on by default: fix: → patch, feat: → minor, type!: or a BREAKING CHANGE footer → major.
  3. default_release_mode from conf.yml.

Modes from 2 and 3 are applied as --orm or -r per release_mode_policy (optional by default, or strict). During a prerelease sequence, vmn stamp --pr rc my_app needs no mode at all.

For independently deployed services, use a root app:

vmn stamp -r patch platform/auth       # auth 0.0.1
vmn stamp -r minor platform/billing    # billing 0.1.0
vmn show --root platform               # 3 on a fresh repo: every service stamp, including each service's first-use init, bumps the root
vmn goto --root -v 3 platform          # root version 3, with each service as it was at that stamp

Tags for platform/auth are named platform-auth_ (the / becomes -), and root versions are tagged platform_.

Try it in 60 seconds (local only)

demo.sh creates throwaway repos in a temp dir. Local bare repos act as the remotes, and vmn is installed into a temp venv. Nothing touches your real remotes.

curl -O https://raw.githubusercontent.com/progovoy/vmn/master/demo/demo.sh
bash demo.sh

What it shows (vmn 0.11.2):

vmn stamp -r patch platform/auth        # 0.0.1   (auto-initializes vmn and the app)
vmn stamp -r minor platform/billing     # 0.1.0
vmn show --root platform                # 3: every service stamp, including first-use init, bumps the root

# platform/auth declares ../lib_core as a dependency in .vmn/platform/auth/conf.yml
vmn stamp -r minor platform/auth        # 0.1.0, root 4
git cat-file -p platform-auth_0.1.0     # YAML: commit + remote of the app repo and ../lib_core

# ...more commits in both repos...
vmn goto -v 0.1.0 platform/auth         # app repo and lib_core back to the stamped commits
vmn goto platform/auth                  # back to the branch tip
vmn goto --root -v 4 platform           # root version 4, services as they were at that stamp

Configuration

Per-app configuration lives in .vmn//conf.yml under a top-level conf: key; root apps have .vmn//root_conf.yml. Edit it with the TUI (vmn config my_app, --vim for $EDITOR) or create it non-interactively with vmn config gen my_app.

conf:
  release_mode_policy: optional
  changelog:
    path: CHANGELOG.md
  github_release:
    draft: true
  policies:
    whitelist_release_branches: [main]
  version_backends:
    pep621:
      path: pyproject.toml

Branch-specific configuration

Integration branches can override configuration, typically dep pins, without touching the main conf:

vmn config gen my_app --branch                      # seeded from the effective conf
vmn config gen my_app --branch --sync-dep-branches  # pin deps to their checked-out branches
vmn config my_app --branch                          # edit interactively

The canonical layout is .vmn//branch_conf//conf.yml (slashes in the branch name become directories; root apps use root_conf.yml). Legacy _conf.yml files are still read and are migrated on the next stamp.

Production operation

  • --dry-run previews a stamp without committing or tagging.
  • Dirty, detached, outgoing, and dependency states are checked before release.
  • Detached CI checkouts work as they are: vmn never creates or switches branches as a side effect. stamp and release --stamp check out the real branch whose remote tip is HEAD (git checkout -b --track origin/); release and add push only their tag and stay detached. When several branches qualify, the CI’s branch variable decides (BRANCH_NAME, GIT_BRANCH, GITHUB_HEAD_REF/GITHUB_REF_NAME, CI_COMMIT_REF_NAME, BUILDKITE_BRANCH, CIRCLE_BRANCH, BITBUCKET_BRANCH); otherwise vmn warns and picks the first alphabetically. Leftover vmn_tracking_remote__* branches from older versions are treated as their upstream and deleted once unused.
  • A per-repository lock prevents concurrent local vmn operations.
  • Release-branch allowlists restrict stable stamps to configured branches.
  • --pull fetches remote state and retries version conflicts.
  • vmn rolls back newly created local release state when publication fails.
  • --git-push-user/--git-push-token (or VMN_GIT_PUSH_USER/_TOKEN) authenticate the push through an ephemeral HTTPS URL; the remote config is never modified.
  • No internet access is required when an internal or local Git remote is used.

For GitHub Actions, use vmn-action:

steps:
  - uses: actions/checkout@v4
    with:
      fetch-depth: 0

  - id: vmn
    uses: progovoy/vmn-action@latest
    with:
      app-name: my_app
      do-stamp: true
      stamp-mode: patch
    env:
      GITHUB_TOKEN: ${{ github.token }}

  - run: echo "Stamped ${{ steps.vmn.outputs.verstr }}"

Elsewhere, fetch complete history and tags, serialize stamps for the same app, and give the job write access to the remote:

pip install vmn
vmn stamp --pull -r patch my_app

Start an established migration with --dry-run, then add branch policy before enabling automatic stamps.

Working-state snapshots

Between releases, save and restore your exact working state — uncommitted changes, local commits, and untracked files, across every dependency — as a named version, without committing:

vmn snapshot create my_app --note "parser refactor"   # prints 1.2.0-dev.a1b2c3d.e4f5g6h
vmn snapshot list my_app --last 5
vmn snapshot diff my_app -v @2                        # vs your working tree; --to 
vmn snapshot restore my_app --latest                  # your current work is auto-saved first

Actions are create (default), list, show, note, delete, restore, export and diff. The app must be stamped once first. With vmn-exp installed, --store keeps snapshots in a shared experiment store and vmn goto -v restores them. See docs/snapshots.md.

Experiment tracking (vmn-exp)

vmn-exp is a separate, optional product built on vmn: it records training and evaluation runs as working-state versions with their metrics, params and artifacts, plus a model registry, sweeps and a web dashboard.

pip install vmn-exp         # "vmn-exp[ui]" adds the dashboard

See the vmn-exp documentation. Coming from vmn 0.10 or earlier: vmn exp/model/ui are now vmn-exp … (packaging).

AI agent integration

vmn skill gives AI coding agents the context they need to use vmn correctly:

vmn skill                               # print the skill block
vmn skill --install                     # .claude/skills/vmn/SKILL.md (--force overwrites)
vmn skill --install --target cursor     # .cursorrules
vmn skill --install --target agents     # AGENTS.md

Re-running --install for cursor/agents updates only vmn’s section. Optional, opinionated development rules for agents (TDD, worktrees, minimal diffs, …) live in docs/agent-methodology.md.

Command reference

Command Purpose
vmn stamp Compute, create, and publish a version
vmn release Promote a prerelease to a final release
vmn show Read version, status, or effective configuration
vmn goto Restore recorded application and dependency revisions
vmn snapshot Capture, inspect, compare, export, or restore working state
vmn worktrees (wt) Islands: worktrees of the app and its deps (create, list, pull, freeze, remove)
vmn add Attach build metadata to an existing version
vmn gen Render a file from a Jinja2 template
vmn config List apps, or edit global, app, root-app, and branch configuration
vmn skill Output or install the AI agent skill block
vmn init / vmn init-app Explicit initialization (optional; stamp auto-inits)

Documentation

Project

vmn is open source under the MIT License. Issues, questions, and pull requests are welcome; see the contributing guide and the issue tracker.

View the full README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add progovoy/vmn
New to MCP? Start with the mcpskill.org guide →