Hatch has an amazing versioning cli for python packages that just works. It takes very little config to get going and you can start bumping versions without worry.

Hatch version cover image

creating new versions created by myself with stable diffusion

project layout

For trying out the hatch version cli let's make a simple project with the terrible name pkg.


 tree .
.
├── pkg
│   ├── __about__.py
│   └── __init__.py
├── pyproject.toml
└── README.md

1 directory, 4 files

pyproject.toml

The main hero of this post is the pyproject.toml. This is what defines all of our PEP 517 style project setup.


[project]
name = "pkg"
description = "Show how to version packages with hatch"
readme = "README.md"
dynamic = [
 "version",
]

[build-system]
requires = [
 "hatchling>=1.4.1",
]
build-backend = "hatchling.build"

[tool.hatch.version]
path = "pkg/__about__.py"

statically versioning

project.version

It is possible to set the version number inside the pyproject.toml statically. This is fine if you just want to version your package manually, and not through the hatch cli.


[project]
name = "pkg"
version = "0.0.0"
# ...

Statically versioning in pyproject.toml will not work with hatch version

hatch-static-version-error


Cannot set version when it is statically defined by the `project.version` field

dynamically Versioning

project.dynamic

Setting the project verion dynamically can be done by changing up the following to your pyproject.toml. Hatch only accepts a path to store your version. If you need to reference it elsewhere in your project you can grab it from the package metadata for that file. I would not put anything else that could possibly clash with the version, as you might accidently change both things.

If you really need to set it in more places use a package like bump2version.


[project]
name = "pkg"
dynamic = [
  "version"
]
# ...
[tool.hatch.version]
path = "pkg/__about__.py"

Note: you can configure hatch to use a different pattern https://hatch.pypa.io/1.2/version/#configuration, but I have not found it to be something that I need.

about.py

The hatch project itself uses a about.py to store it's version. It's sole content is a single __version__ variable. I don't have any personal issues with this so I am going to be following this in my projects that use hatch.


__version__ = "0.0.0"

versioning

hatch version docs

Hatch has a pretty intuitive versioning api. hatch version gives you the version. If you pass in a version like hatch version "0.0.1" it will set it to that version as long as it is in the future, otherwise it will error.


# print the current version
hatch version

# set the version to 0.0.1
hatch version "0.0.1"

bumping

You can bump parts of the semver version.


# minor bump
hatch version minor

# beta pre-release bump
# If published to pypi this can be installed with the --pre flag to pip
hatch version b

# bump minor and beta
hatch version minor,b

# release all of the --pre-release flags such as alpha beta rc
hatch release

Example

Here is a screenshot of bumping a projet along.

hatch-version-cli

GitOps

In my github actions flow I will be utilizing this to automate my versions. In my side projects I use the develop branch to release --pre releases. I have all of my own dependent projets running on these --pre releases, this allows me to cut myself in my own projects before anyone else. Then on main I automatically release this beta version.

GitHub Actions

Here is what the ci/cd for markata looks like. There might be a better workflow strategy, but I use a single github actions workflow and cut branches to release --pre releases and full release. These steps will bump, tag, commit, and deploy for me.


      - name: automatically pre-release develop branch
        if: github.ref == 'refs/heads/develop'
        run: |
          git config --global user.name 'autobump'
          git config --global user.email '[email protected]'
          VERSION=`hatch version`
          # if current version is not already beta then bump minor and beta
          [ -z "${b##*`hatch version`*}" ] && hatch version b || hatch version minor,b
          NEW_VERSION=`hatch version`
          git add markta/__about__.py
          git commit -m "Bump version: $VERSION → $NEW_VERSION"
          git tag $VERSION
          git push
          git push --tags

      - name: automatically release main branch
        if: github.ref == 'refs/heads/main'
        run: |
          git config --global user.name 'autobump'
          git config --global user.email '[email protected]'
          VERSION=`hatch version`
          hatch version release
          NEW_VERSION=`hatch version`
          git add markta/__about__.py
          git commit -m "Bump version: $VERSION → $NEW_VERSION"
          git tag $VERSION
          git push
          git push --tags

      - name: build
        run: |
          python -m build

      - name: pypi-publish
        if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
        uses: pypa/[email protected]
        with:
          password: ${{ secrets.pypi_password }}

Hatch Version Action

I am setting up a github custom action waylonwalker/hatch-version-action that will lint, test, bump, and publish for me in one step. More on that in the future.