Python PR Builds

This action defines how PRs that affect the Python code should be evaluated. At a high level it performs the following checks:

  • Builds and tests the code against each supported platform / version of Python

  • Produces a wheel and an sdist and makes them available as a build artifact.

  • Builds the documentation and makes it available as a build artifact.

  • Reports code coverage.

Triggers

Currently this action is triggered by any PR that is opened against the develop or master branches, unless the changes only affect the blog.

on:
  push:
    branches:
    - develop
    paths:
    - 'arlunio/**'
    - 'docs/using/tutorial/**'
    - 'tests/**'
    - 'setup.py'
    - 'pyproject.toml'
    - 'MANIFEST.in'

  pull_request:
    branches:
    - develop
    - master
    paths:
    - 'arlunio/**'
    - 'docs/using/tutorial/**'
    - 'tests/**'
    - 'setup.py'
    - 'pyproject.toml'
    - 'MANIFEST.in'

Jobs

To ensure that a PR doesn’t introduce any breaking changes we need to run the test suite against each supported platform and python version. To do this we make use of the matrix strategy which spawns a job for each combination of os and python-version

  test:
    name: Test ${{ matrix.python-version }} on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        python-version: [3.7, 3.8]
        os: [ubuntu-latest]  # TODO: Enable windows-latest, macOS-latest

Steps

With the preliminaries out of the way, time to focus on the actual work this action performs.

Setup

    - uses: actions/checkout@v1

    - name: Setup Python ${{ matrix.python-version }}
      uses: actions/setup-python@v1
      with:
        python-version: ${{ matrix.python-version }}

    - name: Setup Environment
      run: |
        python --version
        python -m pip install --upgrade pip
        python -m pip install --upgrade tox

The first few steps handle checking out the code, setting up Python and ensuring the tools we need to run the tests are available.

Dev Version Number

Note

Since we only publish one distribution these steps only run on the latest version of python.

    - name: Get Version Number
      run: |
        build=$(echo $GITHUB_REF | sed -E 's/.*\/([0-9]+)\/.*/\1/')

        echo "ref: $GITHUB_REF"
        echo "Build number is $build"

        echo "::set-env name=BUILD_NUMBER::$build"
        echo "::set-output name=BUILD_NUMBER::$build"
      if: matrix.python-version == '3.7' && github.event_name == 'pull_request'

    - name: Set Version Number
      shell: bash
      run : |
        sed -i 's/"\(.*\)"/"\1dev'"${BUILD_NUMBER}"'"/' arlunio/_version.py
        cat arlunio/_version.py
      if: matrix.python-version == '3.7' && github.event_name == 'pull_request'

So that we have the option of trying out the changes introduced by a PR locally before accepting it we package up the python code into a wheel and make it available as a build artifact. To ensure that these development builds are not confused with a beta or real release we adjust the version number to include a dev<BUILD_NUMBER> suffix.

However unlike beta builds we cannot make use of the einaregilsson/build-number action for PR builds to generate a build number for us. This is due to it requiring access to the GitHub API token which is unavailable for any build originating from a fork of the main repository.

Instead we choose the PR number as the build number which we can obtain using a carefully crafted sed command and the GITHUB_REF environment variable. Then we make an environment variable BUILD_NUMBER available which can be referenced by later steps in the job.

Finally using another carefully crafted sed command in the next step we rewrite the version number in the arlunio/_version.py file so that it contains the dev<BUILD_NUMBER> suffix.

Tox

    - name: 'Tox: Run Tests'
      shell: bash
      run: |
        tox -e py`echo ${{ matrix.python-version }} | tr -d .`

    - name: 'Tox: Build Pkg'
      run: |
        tox -e pkg
        ls dist
      if: matrix.python-version == '3.7' && github.event_name == 'pull_request'

Time for the main event, we use tox to run all our tests including the doctests defined in the documentation and the codebase as well as a code coverage report. Additionally for the latest version of Python we package the code and build the docs in preparation for them to be uploaded as build artifacts.

Publish Results

    - name: 'Publish Pkg'
      uses: actions/upload-artifact@v1.0.0
      with:
        name: 'pkg'
        path: dist
      if: matrix.python-version == '3.7' && github.event_name == 'pull_request'

    - name: 'Report Code Coverage'
      uses: codecov/codecov-action@v1
      with:
        name: "${{ matrix.os }}-${{ matrix.python-version }}"
        flags: unittests

Finally we publish all the artifacts generated during the course of the build along with uploading our code coverage report to codecov