Expand description
§⬆️ Bump2version
bump2versionis a multi-language version bumper written entirely in 100% safe Rust, withno_stdsupport and native Python and Node.js bindings 🗿.
| 🦀 Rust | 🐍 Python | 🟩 Node.js |
|---|---|---|
cargo add bump2version | pip install bump-rs | npm install bump2version |
| Documentation | Read PYTHON.md | Read NODE.md |
§🤔 What does this crate provide?
bump2version automates semantic version management for any project regardless of language. It:
- Parses version strings using a fully configurable regex (default: semver
major.minor.patch). - Bumps any named component (
major,minor,patch, or custom cyclic stages). - Rewrites version occurrences across multiple files, including multiline CHANGELOG patterns, using
(?ms)DOTALL + MULTILINE semantics identical to Python’sre.MULTILINE | re.DOTALL. - Commits and tags via 100% pure
gix(gitoxide); zero subprocess calls, zeroweb-flowghost-author bugs. - Reads author identity from the local git config.
§🦀 Rust
The Rust crate is available on crates.io. For a complete API reference, installation guide, and worked examples, visit the Rust Usage Guide.
The crate ships the following Cargo features:
| Feature | Default | Description |
|---|---|---|
std | ✅ | File I/O, git integration, regex stdlib cache |
cli | ❌ | Standalone bump2version binary via clap |
python | ❌ | Python extension module via PyO3/maturin |
node | ❌ | Node.js native add-on via napi-rs |
§Quick Start
[dependencies]
bump2version = "0.2.0"use bump2version::{config::BumpConfig, version::{parse_version, bump_version, serialize_version}};
fn main() {
let cfg = BumpConfig::default();
let v = parse_version("1.2.3", &cfg).unwrap();
let v2 = bump_version(&v, "patch", &cfg).unwrap();
println!("{}", serialize_version(&v2, &cfg)); // 1.2.4
}§no_std Support
Core modules (config, version, files, error) compile in no_std + alloc:
# no_std (alloc required by the target):
bump2version = { version = "0.2.0", default-features = false }| Module | no_std+alloc | std |
|---|---|---|
config | ✅ | ✅ |
version | ✅ | ✅ |
files | ✅ | ✅ |
error | ✅ | ✅ |
git | ❌ | ✅ |
| CLI | ❌ | ✅ |
| Python / Node.js | ❌ | ✅ |
§🐍 Python
The Python bindings are published to PyPI as bump-rs and can be installed with pip install bump-rs.
Built with maturin, pre-compiled wheels for CPython 3.12+.
For installation instructions, full method signatures, and examples, read the Python Usage Guide.
pip install bump-rsfrom bump_rs import bump_version, apply_file_change, BumpConfig
print(bump_version("1.2.3", "patch")) # "1.2.4"
print(bump_version("1.2.3", "minor")) # "1.3.0"
print(bump_version("1.2.3", "major")) # "2.0.0"
content = 'version = "1.0.0"\n'
print(apply_file_change(content, "1.0.0", "1.0.1"))
# 'version = "1.0.1"\n'
# Multiline CHANGELOG pattern
content = "## 1.0.0\nRelease notes\n\n## 0.9.0\nOld notes\n"
updated = apply_file_change(
content,
current_version="1.0.0",
new_version="1.0.1",
search="## {current_version}\nRelease notes",
replace="## {new_version}\nRelease notes",
)
print(updated) # "## 1.0.1\nRelease notes\n\n## 0.9.0\nOld notes\n"
# Custom parse/serialize config
cfg = BumpConfig(parse=r"(?P<major>\d+)\.(?P<minor>\d+)", serialize="{major}.{minor}")
print(bump_version("2.0", "minor", config=cfg)) # "2.1"§🟩 Node.js
The Node.js bindings are published to npm as bump2version and can be installed with npm install bump2version.
Built with napi-rs, pre-compiled .node add-on.
For installation instructions, TypeScript type definitions, and examples, read the Node.js Usage Guide.
npm install bump2versionconst { bumpVersion, applyFileChange } = require("bump2version");
console.log(bumpVersion("1.2.3", "patch")); // '1.2.4'
console.log(bumpVersion("1.2.3", "minor")); // '1.3.0'§💻 Command-line interface
cargo install bump2version --features rust-binary
bump2version --bump patch # 1.0.0 → 1.0.1
bump2version --bump minor # 1.0.0 → 1.1.0
bump2version --bump major # 1.0.0 → 2.0.0| Option | Description |
|---|---|
--config-file | Config file path (default: .bumpversion.toml) |
--current-version | Override current version |
--bump | Component: major, minor, patch |
--parse | Parse regex override |
--serialize | Serialize format override |
--dry-run / -n | Simulate without writing files |
--new-version | Explicit new version |
--commit / --tag | Git commit + tag |
§⚙️ Configuration File (.bumpversion.toml)
[bumpversion]
current_version = "1.0.0"
commit = true
tag = true
[bumpversion:file:Cargo.toml]
search = 'version = "{current_version}"'
replace = 'version = "{new_version}"'
[bumpversion:file:CHANGELOG.md]
search = """
## {current_version}
Release notes line 1
Release notes line 2"""
replace = """
## {new_version}
Release notes line 1
Release notes line 2"""§🔒 Safety
This crate enforces a zero-unsafe policy via #![forbid(unsafe_code)] at the crate root (except the Node.js FFI layer which requires unsafe for napi-rs interop). Every byte of the implementation, config parsing, regex matching, version bumping, git object creation, is written in safe Rust. The compiler will reject any future unsafe block introduced into the safe portions.
§📊 Benchmarks
§Rust (cargo bench)
Run with cargo bench. Results on x86-64 Linux (rustc stable):
cargo bench output
| Benchmark | Time |
|---|---|
config_parse/minimal | ~12 µs |
config_parse/full_with_parts | ~18 µs |
version_parse/1.0.0 | ~439 µs |
version_bump/patch | ~13.5 µs |
version_bump/minor | ~14.1 µs |
version_bump/major | ~6.0 µs |
file_replace/100 lines | ~247 µs |
file_replace/1 000 lines | ~1.35 ms |
file_replace/10 000 lines | ~14.4 ms |
multiline_replace (CHANGELOG) | ~87 µs |
§Python Nano-Benchmarks (benchmarks/benchmark.py)
Times measured via 3-sigma filtered timeit (CPython 3.12, x86-64 Linux). Run with:
pip install bump-rs bumpversion
python benchmarks/benchmark.py§Version Bumping: full round-trip (parse + bump + serialize)
| Library | patch | minor | major |
|---|---|---|---|
bump-rs (Rust, Arc<Regex> cache) | ~57 µs | ~54 µs | ~53 µs |
bump-my-version (Python library) | ~79 µs | ~95 µs | ~72 µs |
Pure Python (re.compile + int()) | ~3.6 µs | ~2.2 µs | ~2.2 µs |
bump-my-version CLI (subprocess) | ~585 ms | ~585 ms | ~585 ms |
bump-rs is 1.4-1.8× faster than bump-my-version’s Python library and ~10 000× faster than the CLI.
§File Search/Replace
| Library | Single-line | Multiline CHANGELOG |
|---|---|---|
| bump-rs (Rust, cached) | ~65 µs | ~104 µs |
Pure Python re.sub | ~1.7 µs | ~1.3 µs |
When bump-rs wins:
- vs bump-my-version library: 1.4-1.8× faster version bumping, correct multiline pattern semantics.
- vs bump-my-version CLI: ~10 000× faster, no subprocess startup.
- Thread safety:
#![forbid(unsafe_code)]+ no GIL constraint → scales across threads. - Full pipeline: config + bump + git commit entirely in safe Rust.
When pure Python wins:
- Single in-memory arithmetic on a tiny string where ~50 µs PyO3 FFI overhead dominates: use
bump_rsin batch or for full-pipeline work.
§📚 Further Reading
- Semantic Versioning 2.0.0: the canonical version scheme.
- bump-my-version: the Python tool this crate is feature-parity with.
- gitoxide (gix): the pure-Rust git implementation powering our git integration.
- PyO3: Rust ↔ Python FFI framework.
- napi-rs: Rust ↔ Node.js FFI framework.
§📄 License
Licensed under the MIT License.
§bump2version Rust Documentation 🦀
The bump2version Rust crate provides a fully thread-safe, library-quality
version bumper. All logic is available both as a CLI binary and as a library
importable in other Rust projects.
§📦 Installation (CLI)
cargo install bump2version --features rust-binary§📦 Library Usage
[dependencies]
bump2version = { version = "0.2.0", default-features = false }§🛠 Usage Overview
§Parse a version
use bump2version::config::BumpConfig;
use bump2version::version::parse_version;
let cfg = BumpConfig::default();
let v = parse_version("1.2.3", &cfg).unwrap();
assert_eq!(v["major"].value, "1");
assert_eq!(v["patch"].value, "3");§Bump a version
use bump2version::config::BumpConfig;
use bump2version::version::{parse_version, bump_version, serialize_version};
let cfg = BumpConfig::default();
let v = parse_version("1.2.3", &cfg).unwrap();
let bumped = bump_version(&v, "minor", &cfg).unwrap();
assert_eq!(serialize_version(&bumped, &cfg), "1.3.0");§Parse config file
use bump2version::config::parse_config_file;
let cfg = parse_config_file(".bumpversion.toml").unwrap();
println!("{:?}", cfg.current_version);§Apply file search/replace
use bump2version::config::{BumpConfig, FileConfig};
use bump2version::files::apply_file_change;
let cfg = BumpConfig::default();
let mut fc = FileConfig::new("Cargo.toml");
fc.search = Some(r#"version = "{current_version}""#.to_string());
fc.replace = Some(r#"version = "{new_version}""#.to_string());
let content = r#"version = "1.0.0""#.to_string();
let updated = apply_file_change(&content, &fc, &cfg, "1.0.0", "1.0.1").unwrap();
assert!(updated.contains("1.0.1"));§Read git author from local config
use bump2version::git::get_git_author;
use gix::open;
let repo = open(".").unwrap();
let (name, email) = get_git_author(&repo).unwrap();
println!("{name} <{email}>");§📖 Module Overview
| Module | Description |
|---|---|
config | Parse .bumpversion.toml into BumpConfig, FileConfig, PartConfig |
version | Parse version strings, bump components, serialize back to string |
files | Apply single/multiline search-replace with {current_version} tokens |
git | Thread-safe git commit + tag via gix; reads author from git config |
error | Typed BumpError enum |
utils | Higher-level helpers: load_config, compute_new_version, collect_file_configs |
cli | clap-based CLI argument struct (requires cli feature) |
python | PyO3 bindings (requires python feature) |
node | napi-rs bindings (requires node feature) |

