Automated pushes to PyPI

Bookmark and Share

For the last year and a half, I’ve been (co-)maintaining a handy little Python project called pytest-localserver.

Naturally, I completely neglected to mention this on the blog. (Surprise!)

Anyway… I just released a new version which has a nifty feature that I’ve been looking forward to for a while: the ability to build packages and upload them to PyPI as part of our automated build and test pipeline. That means we can release a new version by clicking buttons in GitHub’s web UI rather than manually running twine and other commands.

Hooray, automatic deployment works!
Hooray, automatic deployment works!

Normally I’m all for running things on the command line rather than clicking buttons, but when you’re maintaining a piece of software, repeatability and consistency are key. You don’t want some quirk of your local environment corrupting a build or sneaking some sensitive information into the package that’s going to be distributed to thousands (or let’s be honest, dozens) of people. During my time at SoundHound I developed a bunch of internal Python packages, some of which were shared outside the company, and leaking confidential info was always something to be very careful about.

Plus, with months between releases, a deployment procedure isn’t something you’re necessarily going to remember off the top of your head. And while Python infrastructure has come a long way in making deployments simpler and more standard (mostly just run pyproject-build and twine), it’s still not trivial to be sure you’re getting it exactly right. Here’s the procedure we used to use:

  1. Ensure that the CHANGES file is up to date with the latest changes.
  2. Create a tag whose name is the PEP 440-compliant version number prefixed by v, making sure to include at least three version number components (e.g. v0.6.0).
  3. Make sure that all tests pass on the tagged version.
  4. Push the tag to Github.
  5. Make a fresh clone of the repository, and in the root directory of the new clone, run pyproject-build (from the build package). This will create source and wheel packages under dist/.
  6. Upload the source and wheel to PyPI using twine upload (see twine).
  7. Using the new release form on Github, prepare notes for the new release following the pattern of previous releases. The “Auto-generate release notes” button will be useful in summarizing the changes since the last release.

Each package has its own quirks like this — how to handle tagging, what tests to run and when, and so on. And if one person is maintaining multiple packages (as I did at SoundHound) or multiple people are co-maintaining a single package, it gets that much more complicated to keep things straight. At this point, hiding as much of that procedure as you can behind one UI button starts to look really nice.

If you’re curious, you can check out the changes I made to enable automatic deployment. This is actually just the first step; I’m hoping to integrate testing as well, so GitHub will build the package, test it, and upload it all in one. But that’s a pull request for another day!