{"id":13460138,"url":"https://github.com/mtkennerly/dunamai","last_synced_at":"2025-05-14T12:10:11.333Z","repository":{"id":34359075,"uuid":"177762671","full_name":"mtkennerly/dunamai","owner":"mtkennerly","description":"Dynamic versioning library and CLI","archived":false,"fork":false,"pushed_at":"2025-05-06T16:07:33.000Z","size":498,"stargazers_count":361,"open_issues_count":1,"forks_count":25,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-06T16:47:59.503Z","etag":null,"topics":["bazaar","cli","darcs","dynamic-version","fossil","fossil-scm","git","mercurial","pijul","python","semantic-versioning","subversion","versioning"],"latest_commit_sha":null,"homepage":"https://dunamai.readthedocs.io/en/latest","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mtkennerly.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-03-26T10:09:59.000Z","updated_at":"2025-05-06T16:07:37.000Z","dependencies_parsed_at":"2023-02-14T14:45:49.873Z","dependency_job_id":"7f14eac5-7eb5-468b-9d9e-215b5c5345c4","html_url":"https://github.com/mtkennerly/dunamai","commit_stats":{"total_commits":213,"total_committers":14,"mean_commits":"15.214285714285714","dds":"0.19718309859154926","last_synced_commit":"bf8cb851355d49837d60c007a7bd45d490650439"},"previous_names":[],"tags_count":56,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtkennerly%2Fdunamai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtkennerly%2Fdunamai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtkennerly%2Fdunamai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mtkennerly%2Fdunamai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mtkennerly","download_url":"https://codeload.github.com/mtkennerly/dunamai/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254140756,"owners_count":22021219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bazaar","cli","darcs","dynamic-version","fossil","fossil-scm","git","mercurial","pijul","python","semantic-versioning","subversion","versioning"],"created_at":"2024-07-31T10:00:36.187Z","updated_at":"2025-05-14T12:10:06.317Z","avatar_url":"https://github.com/mtkennerly.png","language":"Python","funding_links":[],"categories":["Python","语言资源库"],"sub_categories":["python"],"readme":"\n# Dunamai\nDunamai is a Python 3.5+ library and command line tool for producing dynamic,\nstandards-compliant version strings, derived from tags in your version control system.\nThis facilitates uniquely identifying nightly or per-commit builds in continuous integration\nand releasing new versions of your software simply by creating a tag.\n\nDunamai is also available as a [GitHub Action](https://github.com/marketplace/actions/run-dunamai).\n\n## Features\n* Version control system support:\n  * [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)\n  * [Mercurial](https://www.mercurial-scm.org)\n  * [Darcs](http://darcs.net)\n  * [Subversion](https://subversion.apache.org)\n  * [Bazaar](https://bazaar.canonical.com/en)\n  * [Fossil](https://www.fossil-scm.org/home/doc/trunk/www/index.wiki)\n  * [Pijul](https://pijul.org)\n* Version styles:\n  * [PEP 440](https://www.python.org/dev/peps/pep-0440)\n  * [Semantic Versioning](https://semver.org)\n  * [Haskell Package Versioning Policy](https://pvp.haskell.org)\n  * Custom output formats\n* Can be used for projects written in any programming language.\n  For Python, this means you do not need a setup.py.\n\n## Usage\n### Installation\n```\npip install dunamai\n```\n\n### CLI\n```console\n# Suppose you are on commit g29045e8, 7 commits after the v0.2.0 tag.\n\n# Auto-detect the version control system and generate a version:\n$ dunamai from any\n0.2.0.post7.dev0+g29045e8\n\n# Or use an explicit VCS and style:\n$ dunamai from git --no-metadata --style semver\n0.2.0-post.7\n\n# Custom formats:\n$ dunamai from any --format \"v{base}+{distance}.{commit}\"\nv0.2.0+7.g29045e8\n\n# If you'd prefer to frame the version in terms of progress toward the next\n# release rather than distance from the latest one, you can bump it:\n$ dunamai from any --bump\n0.2.1.dev7+g29045e8\n\n# Validation of custom formats:\n$ dunamai from any --format \"v{base}\" --style pep440\nVersion 'v0.2.0' does not conform to the PEP 440 style\n\n# Validate your own freeform versions:\n$ dunamai check 0.01.0 --style semver\nVersion '0.01.0' does not conform to the Semantic Versioning style\n\n# More info:\n$ dunamai --help\n$ dunamai from --help\n$ dunamai from git --help\n```\n\n### Library\n\n```python\nfrom dunamai import Version, Style\n\n# Let's say you're on commit g644252b, which is tagged as v0.1.0.\nversion = Version.from_git()\nassert version.serialize() == \"0.1.0\"\n\n# Let's say there was a v0.1.0rc5 tag 44 commits ago\n# and you have some uncommitted changes.\nversion = Version.from_any_vcs()\nassert version.serialize() == \"0.1.0rc5.post44.dev0+g644252b\"\nassert version.serialize(metadata=False) == \"0.1.0rc5.post44.dev0\"\nassert version.serialize(dirty=True) == \"0.1.0rc5.post44.dev0+g644252b.dirty\"\nassert version.serialize(style=Style.SemVer) == \"0.1.0-rc.5.post.44+g644252b\"\n```\n\nThe `serialize()` method gives you an opinionated, PEP 440-compliant default\nthat ensures that versions for untagged commits are compatible with Pip's `--pre` flag.\nThe individual parts of the version are also available for you to use and inspect as you please:\n\n```python\nassert version.base == \"0.1.0\"\nassert version.stage == \"rc\"\nassert version.revision == 5\nassert version.distance == 44\nassert version.commit == \"g644252b\"\nassert version.dirty is True\n\n# Available if the latest tag includes metadata, like v0.1.0+linux:\nassert version.tagged_metadata == \"linux\"\n```\n\n### Tips\nBy default, the \"v\" prefix on the tag is required,\nunless you specify a custom tag pattern.\nYou can either write a regular expression:\n\n* Console:\n  ```console\n  $ dunamai from any --pattern \"(?P\u003cbase\u003e\\d+\\.\\d+\\.\\d+)\"\n  ```\n* Python:\n  ```python\n  from dunamai import Version\n  version = Version.from_any_vcs(pattern=r\"(?P\u003cbase\u003e\\d+\\.\\d+\\.\\d+)\")\n  ```\n\n...or use a named preset:\n\n* Console:\n  ```console\n  $ dunamai from any --pattern default-unprefixed\n  ```\n* Python:\n  ```python\n  from dunamai import Version, Pattern\n  version = Version.from_any_vcs(pattern=Pattern.DefaultUnprefixed)\n  ```\n\nYou can also keep the default pattern and just specify a prefix.\nFor example, this would match tags like `some-package-v1.2.3`:\n\n* Console:\n  ```console\n  $ dunamai from any --pattern-prefix some-package-\n  ```\n* Python:\n  ```python\n  from dunamai import Version\n  version = Version.from_any_vcs(pattern_prefix=\"some-package-\")\n  ```\n\n### VCS archives\nSometimes, you may only have access to an archive of a repository (e.g., a zip file) without the full history.\nDunamai can still detect a version in some of these cases:\n\n* For Git, you can configure `git archive` to produce a file with some metadata for Dunamai.\n\n  Add a `.git_archival.json` file to the root of your repository with this content:\n  ```\n  {\n    \"hash-full\": \"$Format:%H$\",\n    \"hash-short\": \"$Format:%h$\",\n    \"timestamp\": \"$Format:%cI$\",\n    \"refs\": \"$Format:%D$\",\n    \"describe\": \"$Format:%(describe:tags=true,match=v[0-9]*)$\"\n  }\n  ```\n\n  Add this line to your `.gitattributes` file.\n  If you don't already have this file, add it to the root of your repository:\n  ```\n  .git_archival.json  export-subst\n  ```\n\n* For Mercurial, Dunamai will detect and use an `.hg_archival.txt` file created by `hg archive`.\n  It will also recognize `.hgtags` if present.\n\n### Custom formats\nHere are the available substitutions for custom formats.\nIf you have a tag like `v9!0.1.2-beta.3+other`, then:\n\n* `{base}` = `0.1.2`\n* `{stage}` = `beta`\n* `{revision}` = `3`\n* `{distance}` is the number of commits since the last\n* `{commit}` is the commit hash (defaults to short form, unless you use `--full-commit`)\n* `{dirty}` expands to either \"dirty\" or \"clean\" if you have uncommitted modified files\n* `{tagged_metadata}` = `other`\n* `{epoch}` = `9`\n* `{branch}` = `feature/foo`\n* `{branch_escaped}` = `featurefoo`\n* `{timestamp}` is in the format `YYYYmmddHHMMSS` as UTC\n* `{major}` = `0`\n* `{minor}` = `1`\n* `{patch}` = `2`\n\nIf you specify a substitution, its value will always be included in the output.\nFor conditional formatting, you can do something like this (Bash):\n\n```bash\ndistance=$(dunamai from any --format \"{distance}\")\nif [ \"$distance\" = \"0\" ]; then\n    dunamai from any --format \"v{base}\"\nelse\n    dunamai from any --format \"v{base}+{distance}.{dirty}\"\nfi\n```\n\n## Comparison to Versioneer\n[Versioneer](https://github.com/warner/python-versioneer)\nis another great library for dynamic versions,\nbut there are some design decisions that prompted the creation of Dunamai as an alternative:\n\n* Versioneer requires a setup.py file to exist, or else `versioneer install` will fail,\n  rendering it incompatible with non-setuptools-based projects such as those using Poetry or Flit.\n  Dunamai can be used regardless of the project's build system.\n* Versioneer has a CLI that generates Python code which needs to be committed into your repository,\n  whereas Dunamai is just a normal importable library\n  with an optional CLI to help statically include your version string.\n* Versioneer produces the version as an opaque string,\n  whereas Dunamai provides a Version class with discrete parts\n  that can then be inspected and serialized separately.\n* Versioneer provides customizability through a config file,\n  whereas Dunamai aims to offer customizability through its library API and CLI\n  for both scripting support and use in other libraries.\n\n## Integration\n* Setting a `__version__` statically:\n\n  ```console\n  $ echo \"__version__ = '$(dunamai from any)'\" \u003e your_library/_version.py\n  ```\n  ```python\n  # your_library/__init__.py\n  from your_library._version import __version__\n  ```\n\n  Or dynamically (but Dunamai becomes a runtime dependency):\n\n  ```python\n  # your_library/__init__.py\n  import dunamai as _dunamai\n  __version__ = _dunamai.get_version(\"your-library\", third_choice=_dunamai.Version.from_any_vcs).serialize()\n  ```\n\n* setup.py (no install-time dependency on Dunamai as long as you use wheels):\n\n  ```python\n  from setuptools import setup\n  from dunamai import Version\n\n  setup(\n      name=\"your-library\",\n      version=Version.from_any_vcs().serialize(),\n  )\n  ```\n\n  Or you could use a static inclusion approach as in the prior example.\n\n* [Poetry](https://poetry.eustace.io):\n\n  ```console\n  $ poetry version $(dunamai from any)\n  ```\n\n  Or you can use the [poetry-dynamic-versioning](https://github.com/mtkennerly/poetry-dynamic-versioning) plugin.\n\n## Other notes\n* Dunamai needs access to the full version history to find tags and compute distance.\n  Be careful if your CI system does a shallow clone by default.\n\n  * For GitHub workflows, invoke `actions/checkout@v3` with `fetch-depth: 0`.\n  * For GitLab pipelines, set the `GIT_DEPTH` variable to 0.\n  * For Docker builds, copy the VCS history (e.g., `.git` folder) into the container.\n\n  For Git, you can also avoid doing a full clone by specifying a remote branch for tags\n  (e.g., `--tag-branch remotes/origin/master`).\n* When using Git, remember that lightweight tags do not store their creation time.\n  Therefore, if a commit has multiple lightweight tags,\n  we cannot reliably determine which one should be considered the newest.\n  The solution is to use annotated tags instead.\n* When using Git, the initial commit must **not** be both tagged and empty\n  (i.e., created with `--allow-empty`).\n  This is related to a reporting issue in Git.\n  For more info, [click here](https://github.com/mtkennerly/dunamai/issues/14).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtkennerly%2Fdunamai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmtkennerly%2Fdunamai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmtkennerly%2Fdunamai/lists"}