Skip to main content

Automatic, configurable CalVer versioning for setuptools-scm — no manual bumping required

Project description

calver-scm

Automatic, date-based versioning for Python projects — powered by your Git history.

PyPI version PyPI downloads Python versions License: MIT CI Ruff uv mypy pre-commit

calver-scm is a setuptools-scm plugin that generates CalVer version strings directly from your Git tags and commit history. No __version__ files to maintain, no manual bumping — just tag and go.

Versions look like this:

2026.04.0          ← clean tag, April 2026, patch 0
2026.04.1.dev3     ← 3 commits after that tag, still April, patch incremented
2026.05.0.dev3     ← 3 commits after an old tag, month rolled over, patch reset
2026.04.15.0       ← day mode: clean tag on the 15th
2026.04.15.1.dev2  ← day mode: 2 commits after a tag on the same day
2026.04.0.dev12    ← no tag yet, 12 commits in

Installation

pip install calver-scm

Then wire it into your pyproject.toml. Pick the setup that matches your build backend:

With setuptools

[build-system]
requires = ["setuptools", "setuptools-scm", "calver-scm"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
version_scheme = "calver_scm"
local_scheme   = "dirty-tag"

With hatch-vcs (recommended)

hatch-vcs is a Hatchling plugin that delegates versioning to setuptools-scm under the hood. It's a great pairing with calver-scm — Hatchling handles your build, hatch-vcs reads the Git tags, and calver-scm turns them into CalVer strings.

[build-system]
requires = ["hatchling", "hatch-vcs", "calver-scm"]
build-backend = "hatchling.build"

[project]
dynamic = ["version"]

[tool.hatch.version]
source = "vcs"

[tool.hatch.version.raw-options]
version_scheme = "calver_scm"
local_scheme   = "dirty-tag"

The raw-options table passes arguments directly through hatch-vcs to setuptools-scm, so calver_scm is picked up exactly as if you had configured it directly.

Optionally, you can have hatch-vcs write the resolved version to a _version.py file at build time — handy if you want your_package.__version__ to be available at runtime:

[tool.hatch.build.hooks.vcs]
version-file = "src/your_package/_version.py"

Then in your __init__.py:

from ._version import __version__

That's it. Your project will now version itself automatically on every build.


How versions are generated

Every version follows the pattern:

YYYY.MM[.DD].PATCH[.devN][+dirty]
Situation Example
Clean tag, month mode 2026.04.0
Commits after tag, same month 2026.04.1.dev3
Commits after tag, previous month's tag 2026.05.0.dev3
Clean tag, day mode 2026.04.15.0
Dirty working tree 2026.04.0+dirty
No tag yet 2026.04.0.dev12

The patch segment increments automatically from your last tag within the current period, and resets to 0 whenever the month (or day, in day mode) rolls over.


Configuration

Add a [tool.calver_scm] section to pyproject.toml to customise behaviour. All fields are optional — the defaults shown below are sensible for most projects.

[tool.calver_scm]
mode       = "month"   # "month" | "day"
patch      = true      # auto-increment patch within a period
fallback   = "dev"     # "dev" | "date" — when no tag exists
tag_prefix = "v"       # prefix stripped when reading tags

mode

Controls the time granularity of the version base.

  • "month" — base is YYYY.MM (e.g. 2026.04)
  • "day" — base is YYYY.MM.DD (e.g. 2026.04.15)
# Month mode (default) — good for most projects
[tool.calver_scm]
mode = "month"
# → 2026.04.0, 2026.04.1.dev3, 2026.05.0.dev1 …

# Day mode — useful for projects that release frequently
[tool.calver_scm]
mode = "day"
# → 2026.04.15.0, 2026.04.15.1.dev2, 2026.04.16.0.dev1 …

patch

When true, the patch number increments from the last tag within the same period. When false, patch is always 0 and only the .devN distance distinguishes pre-release builds.

# patch = true (default) — each build within a period gets a unique patch number
[tool.calver_scm]
patch = true
# tag v2026.04.2, then 3 commits later → 2026.04.3.dev3

# patch = false — patch stays 0, only devN changes
[tool.calver_scm]
patch = false
# tag v2026.04.2, then 3 commits later → 2026.04.0.dev3

fallback

Controls what happens when no tag exists yet in the repository.

  • "dev" — emits YYYY.MM.0.devN where N is the total commit count (default)
  • "date" — emits YYYY.MM.0 with no dev segment, useful for initial releases
# fallback = "dev" (default) — makes it clear this is a pre-release build
[tool.calver_scm]
fallback = "dev"
# 12 commits, no tag yet → 2026.04.0.dev12

# fallback = "date" — emits a clean version even before the first tag
[tool.calver_scm]
fallback = "date"
# 12 commits, no tag yet → 2026.04.0

tag_prefix

The string that must prefix a tag for it to be recognised as a CalVer tag. Set to "" if your tags have no prefix.

# tag_prefix = "v" (default) — tags like v2026.04.0
[tool.calver_scm]
tag_prefix = "v"

# No prefix — tags like 2026.04.0
[tool.calver_scm]
tag_prefix = ""

# Custom prefix — tags like release-2026.04.0
[tool.calver_scm]
tag_prefix = "release-"

Environment variable overrides

Every option can be overridden at build time without touching pyproject.toml — handy for CI pipelines.

Variable Equivalent option
SCM_CALVER_MODE mode
SCM_CALVER_PATCH patch (true / false)
SCM_CALVER_FALLBACK fallback
SCM_CALVER_TAG_PREFIX tag_prefix

Environment variables take precedence over pyproject.toml.


Local scheme

calver_scm controls the public version segment only. The +dirty suffix is handled separately by setuptools-scm's built-in local schemes. Pick whichever suits you:

[tool.setuptools_scm]
version_scheme = "calver_scm"
local_scheme   = "dirty-tag"         # +dirty on uncommitted changes (recommended)
# local_scheme = "no-local-version"  # suppress the local segment entirely
# local_scheme = "node-and-date"     # +g1a2b3c4.d20260415 (setuptools-scm default)

Requirements

  • Python ≥ 3.10
  • setuptools-scm ≥ 9.2.2
  • tomli ≥ 2.0.0 on Python 3.10 (the stdlib tomllib is used on 3.11+)

Contributing

git clone https://github.com/samcorky/calver-scm
cd calver-scm
uv sync --group dev
pre-commit install
pytest

Pull requests and issues are welcome!


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

calver_scm-1.0.1.tar.gz (44.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

calver_scm-1.0.1-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file calver_scm-1.0.1.tar.gz.

File metadata

  • Download URL: calver_scm-1.0.1.tar.gz
  • Upload date:
  • Size: 44.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for calver_scm-1.0.1.tar.gz
Algorithm Hash digest
SHA256 410283e6edf8d9bc89c63aff92279fb69d6eb95939be725b36a3eb510c1f4df7
MD5 f7f0db3900852c44e0563f7f383c1c0f
BLAKE2b-256 d77d69cae03a05c914f14fb1d1bc2f69f4f27c5503a4e495a1cf53a1ee7819d3

See more details on using hashes here.

File details

Details for the file calver_scm-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: calver_scm-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for calver_scm-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 28e7445d215ccaf637991b5b1575494f0d0b52cd1df633f690ff19135695f1ba
MD5 143a855ca43214d49ce89578d3b9af70
BLAKE2b-256 160ab3d6666566f964b7be67a9d0c3a2ebe1c1f2a726967d5afa125473ae9e17

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page