Enforce All GitHub Actions Status Checks to Pass in PRs

I have been using GitHub actions for a while and they are great! With them you can define your CI/CD along your code and review any changes to CI/CD through your normal PR processes. I’m intending to write more about great workflows and best practices, especially for Python packages, although that will be in a future post. This post addresses a pain point I have had and you may have had as well. Specifically, in PRs, it is possible on GitHub to enforce things like approvals from reviewers before being able to merge. GitHub also has a setting to require status checks to pass before being able to merge, although you have to define exactly which status checks you want to pass. I usually find the names of my status checks change a lot, especially if I’m using matrix strategies for multiple Python versions or package versions. Below we discuss a simple technique to reduce how often you have to update the required status checks!

The solution is relatively straight forward. My workflows usually consist of something like:

name: CI-CD

on:...

jobs:
  lint:
    name: Lint
    runs-on: ubuntu-latest
    steps:
      - ...
  test:
    name: Test
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version:
          - "3.8"
          - "3.9"
          - "3.10"
          - "3.11"
    steps:
      - ...
  release:
    runs-on: ubuntu-latest
    if: ...
    needs:
      - test
      - lint
    steps:
      - ...

Usually, PRs only run the lint and test jobs. The name of the test jobs end up being combined with the matrix strategy to something like Test (3.11), Test (3.10), …, which means that whenever I add something new to the matrix, such as a new Python version or a package, I also have to go and update the required status checks before merging with those new combinations. If your matrix becomes complicated, this can get quite extensive and difficult to maintain, which has meant that I usually don’t bother with it. What I now do instead is I just add a new job called required_status_checks that depends on all the jobs that I want to pass before being able to merge a PR and making that job the required status check:

name: CI-CD

on:...

jobs:
  lint: ...
  test: ...
  required_status_checks:
    name: Required Status Checks
    runs-on: ubuntu-latest
    if: always()
    needs:
      - test
      - lint
    steps:
      - run: |
          [ '${{ needs.test.result }}' = 'success' ] || (echo test failed && false)
          [ '${{ needs.lint.result }}' = 'success' ] || (echo lint failed && false)
  release: ...

This way, if a new required job is added, it just needs to be added to the needs list. Since PRs usually also require at least 1 approval from a reviewer, they can spot any changes to the required_status_checks job that would interfere with the required status checks, such as test being removed. Now you can kiss goodbye to endless updates of the required status checks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s