Ensure PyPI Publish Action Does Not Fail Because of Wrong Version

Posted 3 years, 2 months ago | Originally written on 1 Oct 2021

Here is the scenario: you run a Github action which automatically publishes to PyPI on new tags (I'm yet to figure out how to publish on push only). Since there are two places to label the new release (the tag and the setup.py file), it is very easy to find that the action fails for the simple reason that the tag actually builds an old version.

Solution? Do a check before completing the commit to ensure that both the tag and the package version in setup.py are the same.

Here's how.

Add a variable in setup.py to represent the package version

You need a variable that will hold the version, which can be imported for comparison against the tag.

VERSION = "1.3.3"

Modify setup.py to be importable without side-effects

By default, setup.py will do something you don't expect. The solution is to hide the setup(...) or setuptools.setup(...) inside a condition:

if __name__ == "__main__":
    setuptools.setup(...)

Compare the current version in setup.py and the latest tag using this Python script

We'll save the following as check-version.py:

import os
import subprocess
import sys
import re

# get the latest version in setup.py
from setup import VERSION

VERSION_CRE = re.compile(r"^v(?P<version>\d+\.\d+\.\d+)(-.*)*$")

def main():
   # check whether the latest commit has a new tag
   p = subprocess.Popen("git describe --tags HEAD", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
   o, _ = p.communicate()
   latest_tag = o.decode('utf-8').strip()
   latest_version = VERSION_CRE.match(latest_tag).group('version')
   # compare these values
   if VERSION == latest_version:
       print("ok: latest version and HEAD tag match", file=sys.stderr)
       return os.EX_OK
   print("fail: latest version and HEAD tag DO NOT match; please change accordingly", file=sys.stderr)
   return os.EX_DATAERR


if __name__ == "__main__":
   sys.exit(main())

Call the script from .git/hooks/pre-commit

Add this:

python check-version.py

Make sure .git/hooks/pre-commit is executable

chmod +x pre-commit

if you are not using the pre-commit hook yet.

Now any time you attempt to commit without updating setup.py with the latest tag name you will get:

fail: latest version and HEAD tag DO NOT match; please change accordingly

and the commit will be aborted; otherwise you will get:

ok: latest version and HEAD tag match

and the commit will succeed.