Skip to content

GitHub Workflows

Darren edited this page Mar 12, 2026 · 1 revision

A matrix workflow lets your continuous integration (CI) pipeline run the same jobs across multiple Python versions, operating systems, or dependency sets—all in parallel.

This file is added to your local repository in <repository_name>/.github/workflows/CI.yml

Once committed and merged with MAIN, the workflow will be executed when commiting and pushing changes to your repository. If following the recommended respoitory actions, a Pull Request will need to be created in GitHub and the workflow will execute and a report be shown to the user and reviewer in the Pull Request.

1. Ensures compatibility across Python versions:

Different environments can behave slightly differently. By testing against multiple Python versions (e.g., 3.10–3.13), you quickly learn whether:

Your code works consistently everywhere New language releases introduce issues You’re safe to deprecate older versions

This dramatically reduces “works on my machine” problems.

  1. Catches bugs early

Matrix testing reveals problems that only show up in specific combinations, such as:

A package behaving differently in Python 3.12 A test passing on Linux but failing on Windows A dependency update breaking only one environment

Finding these early prevents expensive debugging later.

  1. Speeds up testing with parallel execution.

Although a matrix tests more environments, GitHub Actions runs them simultaneously, not sequentially. This means:

Broader test coverage without slowing down your CI Faster feedback to developers No bottlenecks when pushing PRs

  1. Helps maintain high-quality releases.

If you distribute a library or CLI, users may run it on different Python versions. A matrix workflow gives you:

Confidence that releases won’t break for downstream users Assurance that you support the versions you claim to support A professional, robust release pipeline

  1. Makes upgrading Python safer.
matrix:
  python-version: ["3.10", "3.11", "3.12", "3.13"]
  1. Easier maintenance over time

Your CI doesn’t have to be updated many times—it can be extended with a single line. Instead of writing multiple jobs, you define the logic once and let the matrix generate the variations automatically.

  1. Clear visibility into environment-specific failures

GitHub visually separates results for each Python version. This helps developers quickly understand:

  • Where a failure occurred
  • Which versions are affected
  • Whether it’s a universal issue or an edge case

This clarity speeds up debugging and reduces noise in PRs.

Below is an example of a simple CI configuration used in one of my repos.

name: CI (Python)

on:
  push:
    branches: [ "main", "master" ]
  pull_request:
    branches: [ "main", "master" ]

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    name: Python ${{ matrix.python-version }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: [ "3.10", "3.11", "3.12", "3.13" ]

    steps:
      - name: Checkout
        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.txt

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          # Core dev tooling
          pip install pytest pytest-cov ruff flake8
          # Project deps
          pip install -r requirements.txt
          # If your package is installable (optional)
          if [ -f pyproject.toml ] || [ -f setup.cfg ] || [ -f setup.py ]; then pip install -e .; fi

      - name: Lint (ruff)
        run: |
          # Lint package code and tests
          ruff check src/soa_builder tests

      # Keep flake8 if you depend on its rules (syntax/errors must fail, style warnings are non-blocking)
      - name: Lint (flake8)
        run: |
          flake8 src/soa_builder tests --count --select=E9,F63,F7,F82 --show-source --statistics
          flake8 src/soa_builder tests --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

      - name: Run tests (pytest + coverage)
        run: |
          mkdir -p reports
          pytest -q tests \
            --maxfail=1 \
            --disable-warnings \
            --junitxml=reports/junit.xml \
            --cov=soa_builder \
            --cov-report=xml:reports/coverage.xml \
            --cov-report=term-missing

      - name: Upload test reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-reports-py${{ matrix.python-version }}
          path: reports/
          if-no-files-found: ignore

Clone this wiki locally