Metadata-Version: 2.1
Name: vmn
Version: 0.9.1rc4
Summary: Stamping utility
Home-page: https://github.com/progovoy/vmn
Author: Pavel Rogovoy
Author-email: p.rogovoy@gmail.com
License: MIT
Platform: UNKNOWN
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: filelock>=3.2.0
Requires-Dist: GitPython==3.1.18
Requires-Dist: packaging>=21.0
Requires-Dist: PyYAML>=5.4.1
Requires-Dist: tomlkit>0.7.2
Requires-Dist: Jinja2>=3.0.3
Requires-Dist: questionary>=2.0.0
Requires-Dist: rich>=12.0.0
Requires-Dist: git-cliff==2.5.0; python_version >= "3.8"

<h1 align="center">vmn</h1>
<p align="center"><strong>One command. Any language. Versions that live in git — not in your way.</strong></p>
<p align="center"><em>The only versioning tool with built-in experiment snapshots for AI/ML reproducibility.</em></p>

<p align="center">
  <a href="https://pypi.org/project/vmn/"><img src="https://img.shields.io/pypi/v/vmn?logo=pypi&logoColor=white&label=PyPI" alt="PyPI version"></a>
  <a href="https://pypi.org/project/vmn/"><img src="https://img.shields.io/pypi/dw/vmn?logo=pypi&logoColor=white" alt="PyPI downloads"></a>
  <a href="https://github.com/progovoy/vmn"><img src="https://img.shields.io/github/stars/progovoy/vmn?style=flat&logo=github" alt="GitHub stars"></a>
  <a href="https://semver.org"><img src="https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&logoColor=white" alt="Semver"></a>
  <a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white" alt="Conventional Commits"></a>
  <a href="https://github.com/progovoy/vmn/blob/master/LICENSE"><img src="https://img.shields.io/github/license/progovoy/vmn" alt="License"></a>
</p>

<p align="center">
  <img src="https://img.shields.io/badge/python-3.8+-3776AB?logo=python&logoColor=white" alt="Python 3.8+">
  <img src="https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-brightgreen" alt="Platforms">
</p>

---

```sh
pip install vmn

vmn stamp -r minor my_app              # => 0.1.0
vmn stamp -r patch --pr rc my_app      # => 0.1.1-rc.1
vmn release my_app                     # => 0.1.1
vmn goto -v 0.1.0 my_app              # entire repo + deps restored to 0.1.0
vmn snapshot create my_model           # => 0.1.1-dev.abc1234.def5678 (capture uncommitted experiments)
```

That snapshot line? **No other versioning tool can do that.** Capture your exact working state — uncommitted code, local commits, everything — and restore it later. Built for AI/ML researchers who iterate faster than they commit.

---

[Why vmn?](#why-vmn) · [Only in vmn](#what-only-vmn-can-do) · [AI/ML Snapshots](#reproducible-aiml-experiments---dev-snapshots) · [Commands](#commands) · [Auto-Embedding](#version-auto-embedding) · [Configuration](#configuration) · [CI](#ci-integration) · [Migration](#already-using-another-tool) · [Contributing](CONTRIBUTING.md)

---

### vmn is for you if:

- You're an **AI/ML researcher** who needs reproducible experiment snapshots — without committing half-finished code
- You version projects in **Python, Rust, Go, C++, Java** — or any language (not just JavaScript)
- You manage **microservices** and need coordinated versions across services
- You work across **multiple repos** and need reproducible cross-repo snapshots
- You're tired of **configuring plugins** for semantic-release or release-please
- You want versions stored in **pure git tags** — zero lock-in, zero databases, zero SaaS
- You need to work **offline**, in air-gapped environments, or without CI

No separate `vmn init` required — `vmn stamp` auto-initializes on first run. Works with shallow clones (`fetch-depth: 1`).

<details>
<summary><strong>Try it locally (playground)</strong></summary>

```sh
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
```

</details>

---

## Why vmn?

vmn does everything semantic-release and release-please do — plus **8 things nothing else can**.

| 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 / local file backend** | :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: |

> **Bold rows = only vmn.** That's the moat.

<details>
<summary>Detailed comparisons & migration guides</summary>

- [vmn vs semantic-release](docs/vmn-vs-semantic-release.md)
- [vmn vs release-please](docs/vmn-vs-release-please.md)
- [vmn vs setuptools-scm](docs/vmn-vs-setuptools-scm.md)
- [Migrating from standard-version](docs/migrating-from-standard-version.md)
- [Migrating from bump2version](docs/migrating-from-bump2version.md)

</details>

---

## What only vmn can do

### State recovery — a time machine for your repo

Your QA team reports a bug in v1.2.3. Instead of digging through `git log`, you run one command:

```sh
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. Reproduce the bug in seconds, not hours. No other versioning tool offers this.

### 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:

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

# six months later — restore everything to that exact state
vmn goto -v 0.1.0 my_app    # all 3 repos checked out to their recorded commits
```

### Microservice topology — one umbrella, independent versions

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

```sh
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 (latest root version)
```

### Zero lock-in — it's just git tags

All version state lives in annotated git tag messages. Uninstall vmn tomorrow and your tags still make perfect sense. No config files, databases, or SaaS dependencies hold your versions hostage.

### Version formats — full semver plus hotfix

Standard [Semver 2.0](https://semver.org) plus an optional 4th hotfix segment and dev snapshots:

`1.6.0` · `1.6.0-rc.23` · `1.6.7.4` · `1.6.0-rc.23+build01.Info` · `1.6.0-dev.a1b2c3d.e4f5g6h`

---

## Reproducible AI/ML experiments - dev snapshots

`vmn snapshot` captures your exact working state — uncommitted changes, local commits that haven't been pushed, everything — into a deterministic dev version you can restore later. No WIP commits, no git noise.

```sh
# You're mid-experiment, code is dirty, results look promising
vmn snapshot create my_model --note "batch_size=32, lr=3e-4, best val_loss so far"
# => 1.2.0-dev.a1b2c3d.e4f5g6h

# Days later — reproduce that exact state
vmn goto -v 1.2.0-dev.a1b2c3d.e4f5g6h my_model
# Code restored: base commit + local commits + working tree changes
```

### Dev version format

Dev versions follow the pattern: `{base_version}-dev.{commit_hash}.{diff_hash}`

- **base_version**: Last stamped version (e.g., `1.2.0`)
- **commit_hash**: Current HEAD (7 chars)
- **diff_hash**: SHA1 of uncommitted changes (7 chars) — same changes always produce the same hash

This means: **identical code state = identical version string**. Deterministic, reproducible, auditable.

Snapshots are stored locally by default in `.vmn/{app}/snapshots/`.

<details>
<summary><strong>Planned: cloud snapshot backends (S3, W&B, MLflow)</strong></summary>

| Backend | Status | Use case |
|:--------|:-------|:---------|
| `local` | Available | Default. Stored in `.vmn/{app}/snapshots/` |
| `s3` | Planned | Team-shared snapshots via S3 bucket |
| `wandb` | Planned | Link snapshots to W&B experiment runs |
| `mlflow` | Planned | Link snapshots to MLflow tracking |

Cloud backends will allow team-wide snapshot sharing and integration with experiment tracking platforms.

</details>

---

## 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 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

```sh
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.0.2-rc.1 (prerelease)
vmn stamp my_app                          # => 0.0.3 (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 current commit already matches). Auto-initializes repo/app on first run.

<details>
<summary><strong>Stamping without <code>-r</code>, <code>-r</code> vs <code>--orm</code>, and all flags</strong></summary>

**Without `-r`:** works **only during a prerelease sequence** — continues the existing prerelease. If the current version is a release, omitting `-r` errors out. **Exception:** with [`conventional_commits`](#stamp-automation-conventional-commits-changelog-github-releases) enabled, `-r` can always be omitted.

```sh
vmn stamp -r patch --pr rc my_app   # 0.0.2-rc.1
vmn stamp --pr rc my_app            # 0.0.2-rc.2  (continues)
vmn stamp my_app                    # 0.0.2-rc.3  (still continues)
```

**`-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 target. `0.0.2-rc.1` exists -> `0.0.2-rc.2`. |

`--orm` is useful in CI to bump on new work but continue an existing prerelease series.

**All flags:**

| Flag | Description |
|:-----|:------------|
| `-r`, `--release-mode` | `major` / `minor` / `patch` / `hotfix` (also `micro` for hotfix) |
| `--orm`, `--optional-release-mode` | Like `-r` but only advances if no prerelease at target |
| `--pr`, `--prerelease` | Prerelease identifier (e.g. `alpha`, `rc`, `beta.1`) |
| `--dry-run` | Preview without committing or pushing |
| `--pull` | Pull remote first; retries on push conflict (3 retries, 1-5s delay) |
| `-e`, `--extra-commit-message` | Append text to commit message (e.g. `[skip ci]`) |
| `--ov`, `--override-version` | Force a specific version string |
| `--orv`, `--override-root-version` | Force root app version to a specific integer |
| `--dont-check-vmn-version` | Skip vmn binary version check |

**`vmn init-app` flags:**

| Flag | Description |
|:-----|:------------|
| `-v`, `--version` | Version to start from (default: `0.0.0`) |
| `--dry-run` | Preview without committing |
| `--orm`, `--default-release-mode` | Set `default_release_mode` in app config: `optional` (default) or `strict` |


</details>

#### Stamp automation (conventional commits, changelog, GitHub releases)

Enable `conventional_commits` and **never type `-r` again** — vmn reads your commit messages (`fix:` → patch, `feat:` → minor, `BREAKING CHANGE` / `!` → major) and picks the release mode automatically:

```sh
# with conventional_commits enabled:
git commit -m "feat: add search endpoint"
vmn stamp my_app    # => 0.2.0 (minor, auto-detected from "feat:")
```

Configure per-app in `.vmn/<app-name>/conf.yml`:

```yaml
conf:
  conventional_commits: true             # auto-detect release mode — no -r flag needed
  default_release_mode: optional         # "optional" (--orm behavior) or "strict" (-r behavior)

  changelog:                             # generate CHANGELOG.md on each stamp (requires conventional_commits)
    path: "CHANGELOG.md"

  github_release:                        # create GitHub Release after push (requires gh CLI + GITHUB_TOKEN)
    draft: false
```

> `default_release_mode` is a top-level config key (not nested under `conventional_commits`). If you have it nested from an older version, vmn will ask you to move it.

### vmn release

Promote a prerelease to its final version. Three modes:

```sh
vmn release -v 0.0.1-rc.1 my_app   # explicit version — tag-only, works from anywhere
vmn release my_app                  # auto-detect from current commit (must be on version commit)
vmn release --stamp my_app          # full stamp flow — new commit + tag + push
```

Idempotent. `-v` and `--stamp` are mutually exclusive.

### vmn show

```sh
vmn show my_app                    # current version from HEAD
vmn show --dev my_app              # dev version with commit+diff hash (for dirty working trees)
vmn show -v 0.0.1 my_app          # specific version info
vmn show --verbose my_app          # full YAML metadata dump
vmn show --raw my_app              # without template formatting
vmn show --root my_root_app        # root app version (integer)
vmn show --type my_app             # release / prerelease / metadata
vmn show -u my_app                 # unique ID (version+commit_hash)
vmn show -t '[{major}]' my_app    # override display template
vmn show --conf my_app             # show app configuration
vmn show --ignore-dirty my_app     # ignore dirty working tree
vmn show --from-file my_app        # read from local snapshot file (requires create_snapshots)
```

### vmn goto

Checkout the repo (and all tracked dependencies) to the exact state at a version:

```sh
vmn goto -v 1.2.3 my_app              # repo + deps restored to version 1.2.3
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 --pull -v 1.2.3 my_app       # pull remote before checking out

# Restore a dev snapshot (uncommitted experiment state)
vmn goto -v 1.2.0-dev.a1b2c3d.e4f5g6h my_model  # checkout + apply patches
```

Dependencies are auto-cloned if missing (up to 10 in parallel). Dev version restore checks out the base commit, applies local commits, then applies the working tree patch — recreating your exact experimental state.

### vmn gen

Generate a version file from a Jinja2 template:

```sh
vmn gen -t version.j2 -o version.txt my_app           # current HEAD version
vmn gen -t version.j2 -o version.txt -v 1.0.0 my_app  # specific version
vmn gen -t version.j2 -o version.txt -c custom.yml my_app  # with custom template values
vmn gen -t version.j2 -o version.txt --verify-version my_app  # verify version exists
```

See [Template Variables](#template-variables-for-vmn-gen) for available Jinja2 variables.

### vmn add

Attach build metadata to an existing version:

```sh
vmn add -v 0.0.1 --bm build42 my_app                    # => 0.0.1+build42
vmn add -v 0.0.1 --bm build42 --vmp meta.yml my_app     # attach metadata file
vmn add -v 0.0.1 --bm build42 --vmu https://ci/42 my_app  # attach metadata URL
```

### vmn config

```sh
vmn config                  # list all managed apps in the repo
vmn config my_app           # interactive TUI editor
vmn config my_app --vim     # open in $EDITOR
vmn config --branch my_app  # edit/create branch-specific config for current branch
vmn config --root my_app    # edit root app config (root_conf.yml)
vmn config --global         # repo-level .vmn/conf.yml
```

### vmn snapshot

Capture, list, inspect, and annotate dev snapshots of your working state — without committing.

```sh
# Create a snapshot of your current dirty state
vmn snapshot create my_model                           # => 1.2.0-dev.a1b2c3d.e4f5g6h
vmn snapshot create my_model --note "best run so far"  # with annotation

# List all snapshots for an app
vmn snapshot list my_model
# 1.2.0-dev.a1b2c3d.e4f5g6h  [2025-01-15T10:30:00Z] - best run so far
# 1.2.0-dev.x9y8z7w.q1r2s3t  [2025-01-14T16:45:00Z]

# Inspect a snapshot (metadata + patches)
vmn snapshot show -v 1.2.0-dev.a1b2c3d.e4f5g6h my_model

# Add/update a note on an existing snapshot
vmn snapshot note -v 1.2.0-dev.a1b2c3d.e4f5g6h --note "confirmed: overfitting after epoch 12" my_model
```

<details>
<summary><strong>What's stored in a snapshot</strong></summary>

```
.vmn/my_model/snapshots/1.2.0-dev.a1b2c3d.e4f5g6h/
  metadata.yml          # version, branch, timestamp, note, dirty states
  working_tree.patch    # uncommitted changes (git diff HEAD)
  local_commits.patch   # local commits not yet pushed (git format-patch)
```

The metadata captures everything needed for reproducibility: base commit, branch, remote URL, timestamp, and which types of changes were captured. Patches are standard git format — you can apply them manually if needed.

</details>

<details>
<summary><strong>All snapshot flags</strong></summary>

| Flag | Description |
|:-----|:------------|
| `action` | `create` (default), `list`, `show`, `note` |
| `-v`, `--version` | Dev version string (for `show` and `note` actions) |
| `--note` | Description note for the snapshot |
| `--backend` | Storage backend: `local` (default), `s3`, `wandb`, `mlflow` |
| `--bucket` | S3 bucket name (required for `s3` backend) |
| `--project` | Project name (for `wandb`/`mlflow` backends) |

</details>

### vmn init / init-app

Usually not needed — `vmn stamp` auto-initializes. Use for explicit control:

```sh
vmn init-app my_app             # starts from 0.0.0
vmn init-app -v 1.6.8 my_app   # start from a specific version
```

### Python library

```python
from contextlib import redirect_stdout, redirect_stderr
import io, version_stamp.vmn as vmn

out, err = io.StringIO(), io.StringIO()
with redirect_stdout(out), redirect_stderr(err):
    ret, vmn_ctx = vmn.vmn_run(["show", "vmn"])
```

### 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](#stamp-automation-conventional-commits-changelog-github-releases) |

---

## Release candidates

Iterate on prereleases, then promote:

```sh
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
```

<details>
<summary><strong>Template variables for <code>vmn gen</code></strong></summary>

```json
{
  "version": "0.0.1",
  "base_version": "0.0.1",
  "name": "test_app2/s1",
  "release_mode": "patch",
  "prerelease": "release",
  "previous_version": "0.0.0",
  "stamped_on_branch": "main",
  "release_notes": "",
  "changesets": {".": {"hash": "d637...", "remote": "...", "vcs_type": "git"}},
  "root_name": "test_app2",
  "root_version": 1,
  "root_services": {"test_app2/s1": "0.0.1"}
}
```

**Example template** (`version.j2`):
```text
VERSION: {{version}}
NAME: {{name}}
BRANCH: {{stamped_on_branch}}
{% for k,v in changesets.items() %}
REPO: {{k}} | HASH: {{v.hash}} | REMOTE: {{v.remote}}
{% endfor %}
```

</details>

---

## Version auto-embedding

`vmn stamp` can automatically write the version into your project files:

| 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` |

<details>
<summary><strong>Hatchling / uv — dynamic versioning from git tags</strong></summary>

Use [hatch-vcs](https://github.com/ofek/hatch-vcs) to read vmn's tags directly (zero file injection). Works with multiple apps per repo.

```sh
uv add --dev hatch-vcs   # or: pip install hatch-vcs
```

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

[project]
name = "my-package"
dynamic = ["version"]

[tool.hatch.version]
source = "vcs"
tag-pattern = "my_app_(?P<version>.*)"    # replace my_app with your app name
# For root apps: "my_root_app-service1_(?P<version>.*)"  (/ becomes -)
```

Or use the `pep621` backend to write a static version into `[project].version` on each stamp instead.

</details>

<details>
<summary><strong>Generic backends — regex or Jinja2 for any file format</strong></summary>

**generic_selectors** — regex find-and-replace:

```yaml
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](https://regex101.com/r/JoEvaN/1)).

**generic_jinja** — Jinja2 template rendering:

```yaml
version_backends:
  generic_jinja:
    - input_file_path: f1.jinja2
      output_file_path: jinja_out.txt
      custom_keys_path: custom.yml    # optional
```

Same variables as `vmn gen`.

</details>

---

## Configuration

vmn auto-generates `.vmn/<app-name>/conf.yml` per app. Full example with inline docs:

```yaml
conf:
  template: '[{major}][.{minor}]'       # display format ({major}, {minor}, {patch}, {hotfix}, {prerelease}, {rcn}, {buildmetadata})
  hide_zero_hotfix: true                 # hide 4th segment when 0 (default: true)
  conventional_commits: true             # auto-detect release mode from commits
  default_release_mode: optional         # "optional" (--orm) or "strict" (-r) for auto-detected mode (top-level key)
  changelog:
    path: "CHANGELOG.md"                 # generate changelog on stamp (presence enables it)
  github_release:
    draft: false                         # create GitHub Release on stamp (presence enables it)
  deps:                                  # multi-repo dependencies — tracked on stamp, restored on goto
    ../:
      <repo dir name>:
        vcs_type: git
  version_backends:                      # auto-embed version into project files
    npm:
      path: "relative_path/to/package.json"
  policies:
    whitelist_release_branches: ["main"] # restrict which branches can stamp/release
  extra_info: false                      # include host/environment metadata in stamp
  create_snapshots: false                # write local snapshot files on stamp (enables vmn show --from-file)
```

> **Migration note:** `create_verinfo_files` has been renamed to `create_snapshots`. The old key still works but will show a deprecation warning. Snapshot files now live in `.vmn/{app}/snapshots/{version}/metadata.yml` instead of `.vmn/{app}/verinfo/{version}.yml`.

Use `vmn config <app-name>` for a TUI editor, or edit YAML directly.

### Per-branch configuration

vmn supports branch-specific config overrides. Place a file named `<branch>_conf.yml` next to the default `conf.yml` in `.vmn/<app-name>/`:

```
.vmn/my_app/
  conf.yml              # fallback when no branch-specific file exists
  feature-x_conf.yml    # used when on branch "feature-x"
```

When vmn loads configuration it checks for `<active_branch>_conf.yml` first; if that file exists it is used instead of `conf.yml`. The same applies to root app configs (`<branch>_root_conf.yml` vs `root_conf.yml`).

During `vmn stamp`, branch-specific config files from other branches are automatically cleaned up so they don't accumulate in the repository.


---

## CI integration

Use the official GitHub Action — [progovoy/vmn-action](https://github.com/progovoy/vmn-action):

```yaml
steps:
  - uses: actions/checkout@v4
    with:
      fetch-depth: 0   # recommended; vmn handles shallow clones but full is faster
  - uses: progovoy/vmn-action@latest
    with:
      app-name: my_app
      do-stamp: true
      stamp-mode: patch
    env:
      GITHUB_TOKEN: ${{ github.token }}
```

---

## Already using another tool?

Migration takes 5 minutes:

- [Migrating from semantic-release](docs/vmn-vs-semantic-release.md)
- [Migrating from release-please](docs/vmn-vs-release-please.md)
- [Migrating from setuptools-scm](docs/vmn-vs-setuptools-scm.md)
- [Migrating from standard-version](docs/migrating-from-standard-version.md)
- [Migrating from bump2version](docs/migrating-from-bump2version.md)

---

<h3 align="center">Ready to stop fighting your versioning tools?</h3>

```sh
pip install vmn
```

<p align="center">
  Star the repo if vmn saved you time. <a href="https://github.com/progovoy/vmn/issues">File an issue</a> if it didn't — we'll fix it.
</p>

<p align="center">
  <a href="CONTRIBUTING.md"><img src="https://img.shields.io/badge/Contributing-guide-blue?style=for-the-badge" alt="Contributing"></a>
  &nbsp;
  <a href="https://github.com/progovoy/vmn/issues"><img src="https://img.shields.io/badge/Report-Issue-red?style=for-the-badge&logo=github" alt="Report an issue"></a>
  &nbsp;
  <a href="https://pypi.org/project/vmn/"><img src="https://img.shields.io/badge/Install-PyPI-3776AB?style=for-the-badge&logo=pypi&logoColor=white" alt="PyPI"></a>
</p>

<p align="center">
  <a href="https://github.com/progovoy/vmn/graphs/contributors"><img src="https://contrib.rocks/image?repo=progovoy/vmn" /></a>
</p>

<p align="center">
  <sub>Add the badge to your project:</sub><br>
  <code>[![vmn: automatic versioning](https://img.shields.io/badge/vmn-automatic%20versioning-blue)](https://github.com/progovoy/vmn)</code>
</p>


