Config file formats#

bumpversion looks for its configuration in the directory it runs in — the current directory, or the one given by --dir.

Discovery order#

Four filenames are tried, in this order:

#FileSection
1.bumpversion.toml[tool.bumpversion]
2.bumpversion.cfg[bumpversion]
3pyproject.toml[tool.bumpversion]
4setup.cfg[bumpversion]

The first file that contains a usable section wins, and only that file is used — configuration is never merged across files. A pyproject.toml with no [tool.bumpversion] table (or with an empty one) is skipped as though it were not there, so the search continues to setup.cfg.

If no file yields a configuration, the run fails with missing config file.

Cargo.toml is checked last but reading configuration from it is not implemented — a [package.metadata.bumpversion] table has no effect today.

TOML#

The native format. Everything lives under [tool.bumpversion], per-file entries are an array of tables, and per-component settings are nested tables:

pyproject.toml
[project]
name = "widget"
version = "0.3.1"

[tool.bumpversion]
current_version = "0.3.1"
commit = true
tag = true
tag_name = "v{new_version}"
message = "chore(release): {current_version} → {new_version}"

# pyproject.toml holds the config, so bumpversion already rewrites
# `current_version` here. This entry additionally rewrites `[project].version`.
[[tool.bumpversion.files]]
filename = "pyproject.toml"

[[tool.bumpversion.files]]
filename = "src/widget/__init__.py"

Running against it produces:

$ bumpversion --dry-run -v bump minor
 
 [DRY-RUN] [current version]
 [DRY-RUN] 	0.3.1
 [DRY-RUN] [setup]
 [DRY-RUN] 	no setup hooks defined
 [DRY-RUN] [new version]
 [DRY-RUN] 	0.4.0
 [DRY-RUN] 
 [DRY-RUN] [./pyproject.toml]
 [DRY-RUN] 	replacing `{current_version}` (0.3.1) with `{new_version}` (0.4.0)
 [DRY-RUN] 
 [DRY-RUN] 	Differences (-before|+after):
 [DRY-RUN] 	 [project]
 [DRY-RUN] 	 name = "widget"
 [DRY-RUN] 	-version = "0.3.1"
 [DRY-RUN] 	+version = "0.4.0"
 [DRY-RUN] 
 [DRY-RUN] 	 [tool.bumpversion]
 [DRY-RUN] 	-current_version = "0.3.1"
 [DRY-RUN] 	+current_version = "0.4.0"
 [DRY-RUN] 	 commit = true
 [DRY-RUN] 	 tag = true
 [DRY-RUN] 	 tag_name = "v{new_version}"
 [DRY-RUN] 	 message = "chore(release): {current_version} → {new_version}"
 [DRY-RUN] 
 [DRY-RUN] [./src/widget/__init__.py]
 [DRY-RUN] 	replacing `{current_version}` (0.3.1) with `{new_version}` (0.4.0)
 [DRY-RUN] 
 [DRY-RUN] 	Differences (-before|+after):
 [DRY-RUN] 	-__version__ = "0.3.1"
 [DRY-RUN] 	+__version__ = "0.4.0"
 [DRY-RUN] 
 [DRY-RUN] [./pyproject.toml]
 [DRY-RUN] 	replacing `{current_version}` (0.3.1) with `{new_version}` (0.4.0)
 [DRY-RUN] 
 [DRY-RUN] 	Differences (-before|+after):
 [DRY-RUN] 	 name = "widget"
 [DRY-RUN] 	 version = "0.3.1"
 [DRY-RUN] 
 [DRY-RUN] 	 [tool.bumpversion]
 [DRY-RUN] 	-current_version = "0.3.1"
 [DRY-RUN] 	+current_version = "0.4.0"
 [DRY-RUN] 	 commit = true
 [DRY-RUN] 	 tag = true
 [DRY-RUN] 	 tag_name = "v{new_version}"
 [DRY-RUN] 	 message = "chore(release): {current_version} → {new_version}"
 [DRY-RUN] [pre-commit]
 [DRY-RUN] 	no pre-commit hooks defined
 [DRY-RUN] [commit]
 [DRY-RUN] 	   add ./pyproject.toml
 [DRY-RUN] 	   add ./src/widget/__init__.py
 [DRY-RUN] 	commit chore(release): 0.3.1 → 0.4.0
 [DRY-RUN] [tag]
 [DRY-RUN] 	tag = v0.4.0
 [DRY-RUN] 	message = Bump version: 0.3.1 → 0.4.0
 [DRY-RUN] 	sign = false
 [DRY-RUN] [post-commit]
 [DRY-RUN] 	no post-commit hooks defined

Two details worth noting in that output:

  • [project].version and [tool.bumpversion].current_version are both rewritten, because pyproject.toml is listed as a file and is the config file.
  • The tag name and commit message come from the tag_name and message templates in the config.

Regexes in TOML#

The parse key is a regex, and regexes are full of backslashes. Use a TOML literal string (single quotes) so they are taken as written:

parse = '(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)'

In a basic string (double quotes) every backslash has to be doubled, which is easy to get wrong:

parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"

INI#

.bumpversion.cfg and setup.cfg use the legacy INI layout from bump2version, supported so an existing project works unchanged:

setup.cfg
[metadata]
name = widget
version = 4.2.0

[bumpversion]
current_version = 4.2.0
commit = True
tag = True
tag_name = release-{new_version}
serialize =
    {major}.{minor}.{patch}

[bumpversion:file:setup.cfg]

[bumpversion:file:widget/__init__.py]

[bumpversion:glob:docs/*.rst]

The differences from TOML:

  • Global keys go in [bumpversion].

  • Per-file entries are their own sections, named [bumpversion:file:<path>]. A disambiguating suffix is allowed, so two entries can target the same file: [bumpversion:file(version heading):CHANGELOG.md].

  • Glob entries are [bumpversion:glob:<pattern>]. glob_exclude is not supported in INI.

  • Component entries are [bumpversion:part:<name>].

  • Values are unquoted, and booleans accept True / False as well as true / false.

  • Lists are written one item per line (indented), or comma-separated on one line:

    serialize =
        {major}.{minor}.{patch}-{release}
        {major}.{minor}.{patch}
  • The literal value None means “unset” for most keys.

  • In [bumpversion:part:<name>], values must be multi-line or comma-separated — a bare single value is not accepted.

In setup.cfg, sections unrelated to bumpversion are ignored silently. In .bumpversion.cfg — a file that exists only for this tool — an unrecognized section is reported as a diagnostic.

The INI parser currently prints a few => section: … debug lines to stdout while reading the file. They come from the underlying serde-ini-spanned parser and are harmless, but they do clutter the output — TOML is the quieter choice for a new project.

Choosing a file#

  • New project.bumpversion.toml. It is found first and keeps release configuration out of your package manifest.
  • Python project[tool.bumpversion] in pyproject.toml, next to the rest of your tooling.
  • Existing bump2version project — leave setup.cfg or .bumpversion.cfg where it is; it is read as-is.