How to release tags using Github Actions to PyPI

This tutorial will describe how to release Python modules using GitHub Actions to PyPI.

TL;DR for setting up Github Actions and Twine you can jump here.

The Problem

In a Python Project (python-n26) I am currently co-maintaining, we wanted to move from Travis CI completely to GitHub Actions for our CI/CD pipeline. We used Travis before for testing the project and also releasing it when new Git Tags are created by using it’s PyPI deployment feature.

However, since the acquisition of it, it felt like its service is getting worse… build times were increasing and also the history of Idera didn’t seem so great.

GitHub Action seemed to offer something better. More build flexibility and better integration.
There seems to be a lot of Python build guides around, but not so many about Python module releases. That’s why below I’ll describe how to release Python modules to PyPI using twine and GitHub Actions.

The Solution

In order to only create a new PyPI release, I recommend to use two different files for your Python project on GitHub Actions. One for your builds and one for releases.
This makes it more readable and it separates things logically.

As Python builds are properly covered a lot, I’ll only go into detail of tag builds for Python modules. You can still find an example for a test build here.

Publish Python modules using Twine and GitHub Actions

First, go to your GitHub Project Actions Secrets: https://github.com/$user/$project/settings/secrets/actions and create a new secret, called PYPI_PASSWORD. This needs to contain a PyPI API token, you can create here for your Python project.

Then, in your GitHub Project, you need to create a .github/workflows directory.

# Go to your git (GitHub) repo
cd ~/your/github/repo
# Create the workflows directory
mkdir -p .github/workflows
touch .github/workflows/python-publish.yml
# Add the content in the other next clode block to:
# .github/workflows/python-publish.yml

As described in the code block above, add it to the python-publish.yaml file and save it.
It’s checking if any new tags were pushed. If there is a new tag, it will will run python setup.py ... and will then use twine to upload the project build.

name: Upload Python Package

on:
  push:
    tags:
      - '*'

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-python@v2
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install setuptools wheel twine
    - name: Build and publish
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: $
      run: |
        python setup.py sdist bdist_wheel
        twine upload dist/*

In order to make it work a regular Python project build must also work. But this way, it makes the entire release process more automated for co-collaborators and me for the python-n26 project.

A full working example can also be found here: https://github.com/femueller/python-n26/blob/master/.github/workflows/python-publish.yml.