# dunamai

> Dynamic version generation

- **URL**: https://www.freshcrate.ai/projects/dunamai
- **Author**: Matthew T. Kennerly
- **Category**: Developer Tools
- **Latest version**: `1.26.1` (2026-04-21)
- **License**: MIT
- **Source**: https://github.com/mtkennerly/dunamai
- **Language**: Python
- **GitHub**: 382 stars, 26 forks
- **Registry**: pypi (`dunamai`)
- **Tags**: `dynamic`, `pypi`, `version`, `versioning`

## Description

# Dunamai
Dunamai is a Python 3.5+ library and command line tool for producing dynamic,
standards-compliant version strings, derived from tags in your version control system.
This facilitates uniquely identifying nightly or per-commit builds in continuous integration
and releasing new versions of your software simply by creating a tag.

Dunamai is also available as a [GitHub Action](https://github.com/marketplace/actions/run-dunamai).

## Features
* Version control system support:
  * [Git](https://git-scm.com) (2.7.0+ is recommended, but versions as old as 1.8.2.3 will work with some reduced functionality)
  * [Mercurial](https://www.mercurial-scm.org)
  * [Darcs](http://darcs.net)
  * [Subversion](https://subversion.apache.org)
  * [Bazaar](https://bazaar.canonical.com/en)
  * [Fossil](https://www.fossil-scm.org/home/doc/trunk/www/index.wiki)
  * [Pijul](https://pijul.org)
* Version styles:
  * [PEP 440](https://www.python.org/dev/peps/pep-0440)
  * [Semantic Versioning](https://semver.org)
  * [Haskell Package Versioning Policy](https://pvp.haskell.org)
  * Custom output formats
* Can be used for projects written in any programming language.
  For Python, this means you do not need a setup.py.

## Usage
### Installation
```
pip install dunamai
```

### CLI
```console
# Suppose you are on commit g29045e8, 7 commits after the v0.2.0 tag.

# Auto-detect the version control system and generate a version:
$ dunamai from any
0.2.0.post7.dev0+g29045e8

# Or use an explicit VCS and style:
$ dunamai from git --no-metadata --style semver
0.2.0-post.7

# Custom formats:
$ dunamai from any --format "v{base}+{distance}.{commit}"
v0.2.0+7.g29045e8

# If you'd prefer to frame the version in terms of progress toward the next
# release rather than distance from the latest one, you can bump it:
$ dunamai from any --bump
0.2.1.dev7+g29045e8

# Validation of custom formats:
$ dunamai from any --format "v{base}" --style pep440
Version 'v0.2.0' does not conform to the PEP 440 style

# Validate your own freeform versions:
$ dunamai check 0.01.0 --style semver
Version '0.01.0' does not conform to the Semantic Versioning style

# More info:
$ dunamai --help
$ dunamai from --help
$ dunamai from git --help
```

### Library

```python
from dunamai import Version, Style

# Let's say you're on commit g644252b, which is tagged as v0.1.0.
version = Version.from_git()
assert version.serialize() == "0.1.0"

# Let's say there was a v0.1.0rc5 tag 44 commits ago
# and you have some uncommitted changes.
version = Version.from_any_vcs()
assert version.serialize() == "0.1.0rc5.post44.dev0+g644252b"
assert version.serialize(metadata=False) == "0.1.0rc5.post44.dev0"
assert version.serialize(dirty=True) == "0.1.0rc5.post44.dev0+g644252b.dirty"
assert version.serialize(style=Style.SemVer) == "0.1.0-rc.5.post.44+g644252b"
```

The `serialize()` method gives you an opinionated, PEP 440-compliant default
that ensures that versions for untagged commits are compatible with Pip's `--pre` flag.
The individual parts of the version are also available for you to use and inspect as you please:

```python
assert version.base == "0.1.0"
assert version.stage == "rc"
assert version.revision == 5
assert version.distance == 44
assert version.commit == "g644252b"
assert version.dirty is True

# Available if the latest tag includes metadata, like v0.1.0+linux:
assert version.tagged_metadata == "linux"
```

### Tips
By default, the "v" prefix on the tag is required,
unless you specify a custom tag pattern.
You can either write a regular expression:

* Console:
  ```console
  $ dunamai from any --pattern "(?P<base>\d+\.\d+\.\d+)"
  ```
* Python:
  ```python
  from dunamai import Version
  version = Version.from_any_vcs(pattern=r"(?P<base>\d+\.\d+\.\d+)")
  ```

...or use a named preset:

* Console:
  ```console
  $ dunamai from any --pattern default-unprefixed
  ```
* Python:
  ```python
  from dunamai import Version, Pattern
  version = Version.from_any_vcs(pattern=Pattern.DefaultUnprefixed)
  ```

You can also keep the default pattern and just specify a prefix.
For example, this would match tags like `some-package-v1.2.3`:

* Console:
  ```console
  $ dunamai from any --pattern-prefix some-package-
  ```
* Python:
  ```python
  from dunamai import Version
  version = Version.from_any_vcs(pattern_prefix="some-package-")
  ```

### VCS archives
Sometimes, you may only have access to an archive of a repository (e.g., a zip file) without the full history.
Dunamai can still detect a version in some of these cases:

* For Git, you can configure `git archive` to produce a file with some metadata for Dunamai.

  Add a `.git_archival.json` file to the root of your repository with this content:
  ```
  {
    "hash-full": "$Format:%H$",
    "hash-short": "$Format:%h$",
    "timestamp": "$Format:%cI$",
    "refs": "$Format:%D$",
    "describe": "$Format:%(describe:tags=true,match=v[0-9]*)$"
  }
  ```

  Add this line to your `.gitattributes` file.
  If you don't already have t

## Recent releases

| Version | Date | Urgency | Changes |
| --- | --- | --- | --- |
| `1.26.1` | 2026-04-21 | Low | Imported from PyPI (1.26.1) |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |
| `v1.26.1` | 2026-04-04 | Medium | * Fixed: The `highest_tag` argument was ignored in `Version.from_any_vcs()`. ([Contributed by ninoseki](https://github.com/mtkennerly/dunamai/pull/110)) * Fixed: Compatibility with Pijul `1.0.0-beta.11`. |

## Citation

- HTML: https://www.freshcrate.ai/projects/dunamai
- Markdown: https://www.freshcrate.ai/projects/dunamai.md
- Dependencies JSON: https://www.freshcrate.ai/api/projects/dunamai/deps

_Generated by freshcrate.ai. Indexes pypi releases for AI-agent ecosystem packages._
