Skip to content

GitHub Actions

CI Workflow

The CI workflow contains the jobs that run in pull requests and pushes to the main branch:

  • tests: run tox on various Python versions and operating systems
  • coverage: generates code coverage reports for the docs site and README status badge
  • docs-build: builds the docs site
  • markdownlint: lints Markdown files
  • markdown-link-check: checks links in Markdown files work
  • docs-deploy and docs-delete: automate docs versioning

Notes:

  • tox is used to ensure that results can be replicated locally
  • Not all jobs run every workflow call: see the if keyword in each job
  • Some jobs depend on other jobs: see the needs keyword in each job
  • Artifacts are used to access the coverage reports generated by tests in coverage: storing workflow data as artifacts
CI workflow source code

.github/workflows/ci.yml

name: CI

on:
  pull_request:
    # default types + closed
    types: [opened, synchronize, reopened, closed]
  push:
    branches:
      - main

defaults:
  run:
    shell: bash

env:
  PIP_DISABLE_PIP_VERSION_CHECK: 1

permissions:
  contents: write

jobs:
  tests:
    name: "Run tests using python-${{ matrix.python-version }} on ${{ matrix.os }}"
    runs-on: "${{ matrix.os }}"
    if: github.event_name != 'pull_request' || github.event.action != 'closed'

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11']

    steps:
      - name: "Check out the repo"
        uses: "actions/checkout@v4"

      - name: "Set up Python"
        uses: "actions/setup-python@v5"
        with:
          python-version: "${{ matrix.python-version }}"
          cache: pip
          cache-dependency-path: '**/requirements/test.txt'

      - name: "Install dependencies"
        run: python -m pip install tox tox-gh-actions

      - name: "Run tox for python-${{ matrix.python-version }} on ${{ matrix.os }}"
        run: python -m tox

      - name: "Combine coverage data from tests"
        if: (matrix.python-version == '3.10') && (matrix.os == 'ubuntu-latest')
        run: |
          python -m tox -e coverage
          export TOTAL=$(python -c "import json;print(json.load(open('coverage.json'))['totals']['percent_covered_display'])")
          echo "total=$TOTAL" >> $GITHUB_ENV
          echo "### Total coverage: ${TOTAL}%" >> $GITHUB_STEP_SUMMARY

      - name: "Make coverage badge"
        if: (matrix.python-version == '3.10') && (matrix.os == 'ubuntu-latest') && (github.repository == 'patrick-5546/xlbudget') && (github.ref == 'refs/heads/main')
        # https://gist.github.com/patrick-5546/845b19d91f3d03c94677f6fae6eb414c
        uses: schneegans/dynamic-badges-action@v1.7.0
        with:
          # GIST_TOKEN is a GitHub personal access token with scope "gist".
          auth: ${{ secrets.GIST_TOKEN }}
          gistID: 845b19d91f3d03c94677f6fae6eb414c   # replace with your real Gist id.
          filename: covbadge-xlbudget.json
          label: Coverage
          message: ${{ env.total }}%
          minColorRange: 50
          maxColorRange: 90
          valColorRange: ${{ env.total }}

      - name: "Upload HTML coverage report"
        if: (matrix.python-version == '3.10') && (matrix.os == 'ubuntu-latest')
        uses: actions/upload-artifact@v4
        with:
          name: htmlcov
          path: htmlcov/

  docs-build:
    name: Build Docs
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request' || github.event.action != 'closed'
    steps:
      - name: "Check out the repo"
        uses: "actions/checkout@v4"

      - name: "Set up Python"
        uses: "actions/setup-python@v5"
        with:
          python-version: "3.10"
          cache: pip
          cache-dependency-path: '**/requirements/docs.txt'

      - name: "Install dependencies"
        run: python -m pip install tox tox-gh-actions

      - name: "Build docs"
        run: python -m tox -e docs-build

  # https://github.com/nosborn/github-action-markdown-cli
  markdownlint:
    name: Lint Markdown
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request' || github.event.action != 'closed'
    needs: docs-build
    steps:
    - name: Check out code
      uses: actions/checkout@v4

    - name: Lint markdown pages
      uses: nosborn/github-action-markdown-cli@v3
      with:
        files: .
        config_file: '.markdownlint.json'
        dot: true

  # https://github.com/gaurav-nelson/github-action-markdown-link-check
  markdown-link-check:
    name: Check links in Markdown files
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request' || github.event.action != 'closed'
    needs: docs-build
    steps:
    - name: Check out code
      uses: actions/checkout@v4

    - name: Check markdown pages for broken links
      uses: gaurav-nelson/github-action-markdown-link-check@v1
      with:
        config-file: '.mlc_config.json'
        folder-path: '.'

  # https://squidfunk.github.io/mkdocs-material/publishing-your-site/#with-github-actions
  docs-deploy:
    name: Deploy Docs version
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request' || github.event.action != 'closed'
    needs: [tests, markdownlint, markdown-link-check]
    steps:
      - name: Check out code
        uses: actions/checkout@v4
        with:
          # checkout all commits to get accurate page revision times
          # for the git-revision-date-localized plugin
          fetch-depth: '0'

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"
          cache: pip
          cache-dependency-path: '**/requirements/docs.txt'

      - name: Download HTML coverage report
        uses: actions/download-artifact@v4
        with:
          name: htmlcov
          path: htmlcov

      - name: Install dependencies
        run: python -m pip install -r requirements/docs.txt

      - name: "Deploy pr-${{ github.event.number }} version of the Docs"
        if: github.event_name == 'pull_request'
        run: |
          git config user.name ci-bot
          git config user.email ci-bot@example.com
          mike deploy --push pr-${{ github.event.number }}

      - name: Deploy main version of the Docs
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        run: |
          git config user.name ci-bot
          git config user.email ci-bot@example.com
          mike deploy --push --update-aliases main latest

  docs-delete:
    name: Delete Docs version
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    steps:
      - name: Check out code
        uses: actions/checkout@v4
        with:
          # checkout all commits and branches to get gh-pages
          fetch-depth: '0'

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.10"
          cache: pip
          cache-dependency-path: '**/requirements/docs.txt'

      - name: Install dependencies
        run: python -m pip install -r requirements/docs.txt

      - name: "Delete pr-${{ github.event.number }} version of the Docs"
        run: |
          git config user.name ci-bot
          git config user.email ci-bot@example.com
          mike delete --push pr-${{ github.event.number }}

Release Workflow

TBD.

Release workflow source code

.github/workflows/release.yml

# this file is *not* meant to cover or endorse the use of GitHub Actions, but rather to
# help make automated releases for this project

name: Release

on:
  release:
    types: [published]

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.x'
    - name: Install build dependencies
      run: python -m pip install -U setuptools wheel build
    - name: Build
      run: python -m build .
    - name: Publish
      uses: pypa/gh-action-pypi-publish@master
      with:
        password: ${{ secrets.PYPI_API_TOKEN }}

Last update: March 21, 2023