Migrating from bump2version to vmn

August 1, 2026 ยท View on GitHub

vmn is a language-agnostic, git-tag-based versioning CLI. This guide helps you migrate from bump2version (and its predecessor bumpversion), which are no longer actively maintained.

Why Migrate

bump2version (and the original bumpversion) have seen minimal maintenance for years. Open issues and pull requests go unaddressed, and the tools do not support modern Python packaging conventions like pyproject.toml natively.

vmn is an actively maintained alternative that offers:

  • Git-tag-based source of truth: no current_version field to keep in sync
  • Language-agnostic: works with Python, Node.js, Rust, Go, and any other language
  • Multi-repo dependency tracking for products spanning several repositories
  • State recovery via vmn goto to check out the exact repo state at any version
  • Root app / microservice topology for versioning parent apps and child services
  • 4-segment hotfix versions (major.minor.patch.hotfix) for hotfix workflows
  • Conventional commits for automatic release mode detection
  • Offline / air-gapped support via a local file backend
  • CI-agnostic: works in GitHub Actions, GitLab CI, Jenkins, Bitbucket Pipelines, or locally

Concept Mapping

bump2versionvmnNotes
.bumpversion.cfg / setup.cfg [bumpversion].vmn/{app}/conf.ymlPer-app configuration
current_version = 1.2.3Git tag (source of truth)No version in config files
[bumpversion:file:setup.py]version_backends: {pep621: {path: pyproject.toml}}Auto-embed version in files
[bumpversion:file:package.json]version_backends: {npm: {path: package.json}}Auto-embed version in files
part = major/minor/patch-r major/minor/patchRelease mode argument
--allow-dirtyNot applicable (vmn manages git state)vmn commits changes automatically
--dry-run--dry-runPreview without changes
--tag / --no-tagvmn always creates tagsTags are the core mechanism
--commit / --no-commitvmn always commitsPart of the stamp workflow
tag_name = v{new_version}Tag format: {app_name}_{version}vmn uses its own convention
commit_message configNot configurablevmn generates structured commits
search / replace patternsVersion backends + Jinja2 templatesDifferent approach to file updates
serialize / parse formattemplate in conf.ymlVersion display format

Step-by-Step Migration

1. Install vmn

pip install vmn
# or: pipx install vmn
# or: uvx vmn

2. Initialize vmn

The first vmn stamp auto-initializes both the repository and the app, so you can skip straight to stamping. Run the explicit commands only if you want the .vmn/ scaffolding in place before your first version -- for example to seed a starting version:

vmn init                       # once per repository
vmn init-app -v 1.4.2 my_app   # once per app; -v sets the starting version

This registers my_app for versioning and creates .vmn/my_app/conf.yml and .vmn/my_app/last_known_app_version.yml.

3. Map Your bump2version File Patterns

bump2version uses [bumpversion:file:...] sections with search and replace patterns to update version strings in source files. vmn replaces this with version backends and Jinja2 templates.

Python Projects (pyproject.toml)

Before (bump2version):

[bumpversion]
current_version = 1.2.3

[bumpversion:file:setup.py]
search = version="{current_version}"
replace = version="{new_version}"

[bumpversion:file:mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

After (vmn):

# .vmn/my_app/conf.yml
conf:
  version_backends:
    pep621:
      path: pyproject.toml

For files like __init__.py that are not covered by a built-in backend, use a Jinja2 template with vmn gen:

vmn gen -t version.j2 -o mypackage/__init__.py my_app

Where version.j2 contains:

__version__ = "{{ version }}"

Node.js Projects (package.json)

Before (bump2version):

[bumpversion:file:package.json]
search = "version": "{current_version}"
replace = "version": "{new_version}"

After (vmn):

# .vmn/my_app/conf.yml
conf:
  version_backends:
    npm:
      path: package.json

Rust Projects (Cargo.toml)

Before (bump2version):

[bumpversion:file:Cargo.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

After (vmn):

# .vmn/my_app/conf.yml
conf:
  version_backends:
    cargo:
      path: Cargo.toml

4. Enable Conventional Commits (Optional)

If you want vmn to automatically determine the release mode from commit messages:

# .vmn/my_app/conf.yml
conf:
  conventional_commits: true

5. Stamp Your First vmn Version

# Explicit release mode
vmn stamp -r patch my_app

# Or, with conventional commits enabled
vmn stamp my_app

vmn creates an annotated git tag, updates configured version backends, commits the changes, and pushes.

6. Remove bump2version Configuration

# Remove config file
rm -f .bumpversion.cfg

# If using setup.cfg, remove the [bumpversion] and [bumpversion:file:...] sections

# Uninstall
pip uninstall bump2version

7. Update CI Pipelines

Replace bump2version commands in your CI configuration:

Before (bump2version):

# GitHub Actions example
- run: pip install bump2version
- run: bump2version patch
- run: git push --follow-tags

After (vmn):

# GitHub Actions example
- run: pip install vmn
- run: vmn stamp -r patch my_app

vmn handles the git commit, tag, and push internally.

Command Mapping Quick Reference

bump2version commandvmn equivalent
bump2version patchvmn stamp -r patch my_app
bump2version minorvmn stamp -r minor my_app
bump2version majorvmn stamp -r major my_app
bump2version --dry-run patchvmn stamp -r patch --dry-run my_app
bump2version --allow-dirty patchvmn stamp -r patch my_app (vmn manages state)
bump2version --list patchvmn show my_app
bump2version --tag --commit patchvmn stamp -r patch my_app (always tags and commits)

Key Differences to Be Aware Of

No current_version in Config

bump2version stores current_version in .bumpversion.cfg and updates it on every bump. vmn does not store the version in any config file. The version lives exclusively in git tags, eliminating a common source of merge conflicts and desynchronization.

No search / replace Patterns

bump2version uses regex-like search and replace directives to find and update version strings in arbitrary files. vmn uses structured version backends for well-known file formats (package.json, Cargo.toml, pyproject.toml) and Jinja2 templates for everything else. This is less flexible for unusual patterns but more reliable for standard formats.

Tags Are Required

bump2version can be configured to skip tagging (--no-tag). vmn always creates annotated git tags because tags are the source of truth. If you need to test without creating tags, use --dry-run.

Commit Messages Are Not Configurable

bump2version allows custom commit_message templates. vmn generates its own structured commit messages. If you need specific commit message formats, wrap vmn in a script that amends the commit.

FAQ

Can I keep my existing git tags?

Yes. vmn uses its own tag format ({app_name}_{version}), so it will not conflict with tags created by bump2version. Both sets of tags can coexist.

Can I use vmn for non-Python projects?

Absolutely. Unlike bump2version, vmn is language-agnostic. It works with any project that lives in a git repository.

What about bumpversion (the original)?

The migration process is identical. bumpversion and bump2version use the same configuration format, so the steps above apply to both.

Can I use vmn in a monorepo?

Yes. Initialize a separate app for each component:

vmn init-app service_a
vmn init-app service_b
vmn stamp -r patch service_a
vmn stamp -r minor service_b

For microservice topologies, use the root-app feature:

vmn init-app my_platform/service_a
vmn init-app my_platform/service_b

Does vmn support custom version parts?

bump2version allows defining custom version parts (e.g., release_num). vmn uses the standard semver segments (major, minor, patch) plus an optional hotfix segment and prerelease labels. Custom parts are not supported, but the four-segment format covers most real-world workflows.

Further Reading