Skip to content
Latchkey

Main workflow (Neoteroi/BlackSheep)

The Main workflow from Neoteroi/BlackSheep, explained and optimized by Latchkey.

F

CI health: F - at risk

Point runs-on at Latchkey and get caching, run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.

Grade your own workflow free or run it on Latchkey →
Source: Neoteroi/BlackSheep.github/workflows/main.ymlLicense MITView source

What it does

This is the Main workflow from the Neoteroi/BlackSheep repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Main

on:
  workflow_dispatch:
    inputs:
      publish_artifacts:
        description: 'Publish artifacts (Y|N)'
        required: true
        default: 'N'
  release:
    types: [published]
  push:
    branches:
      - main
      - ci
    paths-ignore:
      - README.md
      - CHANGELOG.md
  pull_request:
    branches:
      - "*"
    paths-ignore:
      - README.md
      - CHANGELOG.md

env:
  PROJECT_NAME: blacksheep

jobs:
  windows-validation:
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
    runs-on: windows-latest
    if: github.event_name == 'pull_request' || github.event_name == 'push'

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 9
          submodules: false

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

      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install flake8

      - name: Compile Cython extensions
        run: |
          cython blacksheep/url.pyx
          cython blacksheep/exceptions.pyx
          cython blacksheep/headers.pyx
          cython blacksheep/cookies.pyx
          cython blacksheep/contents.pyx
          cython blacksheep/messages.pyx
          cython blacksheep/scribe.pyx
          cython blacksheep/baseapp.pyx
          python setup.py build_ext --inplace

      - name: Run tests
        run: |
          pytest

  build:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "pypy-3.11"]
    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 9
          submodules: false

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

      - name: Install dependencies
        run: |
          VER='${{ matrix.python-version }}'

          if [ $VER == "pypy-3.11" ]; then
            echo "Install requirements for PyPy"
            pip install -r requirements.pypy.txt
          else
            echo "Install requirements for CPython"
            pip install -r requirements.txt
          fi
          pip install black==25.1.0 isort==6.0.1 flake8==7.2.0

      - name: Run tests for Pure-Python Code
        run: |
          echo "Running unit tests..."
          pytest tests

          echo "Running integration tests..."
          export APP_DEFAULT_ROUTER=false
          pytest itests

      - name: Compile Cython extensions
        if: matrix.python-version != 'pypy-3.11'
        run: |
          make compile

      - name: Run tests with Cython Code
        if: matrix.python-version != 'pypy-3.11'
        run: |
          echo "Running unit tests..."
          pytest tests

          echo "Running integration tests..."
          export APP_DEFAULT_ROUTER=false
          pytest itests

      - name: Run linters
        continue-on-error: true
        run: |
          echo "Running linters - if build fails here, please be patient! Feel free to ask for assistance."

          flake8 blacksheep
          flake8 tests
          flake8 itests
          isort --check-only blacksheep 2>&1
          isort --check-only tests 2>&1
          isort --check-only itests 2>&1
          black --check blacksheep 2>&1
          black --check tests 2>&1
          black --check itests 2>&1

      - name: Test Hypercorn
        run: |
          PYVER=`python -V 2>&1`

          if [ "${PYVER:0:-2}" == "Python 3.10" ]; then
            echo "Skipping Hypercorn tests, since it fails with Python 3.10.0 on Ubuntu (2021/10/24)"
          else
            APP_DEFAULT_ROUTER=false ASGI_SERVER=hypercorn pytest itests/test_server.py
          fi

      - name: Test Piccolo Admin / Starlette mount compatibility
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.14
        run: |
          echo "Running Starlette mount compatibility tests (ref: piccolo-orm/piccolo_admin#472)..."
          pip install piccolo-admin  # include the dedicated test

          pytest tests/test_piccolo_admin_compat.py -v

      - name: Install distribution dependencies
        run: pip install --upgrade twine setuptools wheel
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.12

      - name: Create distribution package
        run: python setup.py sdist
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.12

      - name: Upload distribution package
        uses: actions/upload-artifact@v4
        with:
          name: dist-ubuntu-no-wheels
          path: dist
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.12

      - name: Test imports with no optional dependencies
        run: |
          pip uninstall -y -r <(pip freeze)
          BLACKSHEEP_NO_EXTENSIONS="1" pip install -e .
          cd itests
          echo "Expect no import error…"
          python issue606.py

  # Build wheels with cibuildwheel
  build-wheels:
    needs: build
    name: Build ${{ matrix.os }} wheels
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os:
          [
            ubuntu-latest,
            ubuntu-24.04-arm,
            windows-latest,
            windows-11-arm,
            # macos-15-intel,  # Failing with "File Not Found" for wheels it builds
            # macos-latest,  # Failing with "File Not Found" for wheels it builds
          ]

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Build wheels
        uses: pypa/cibuildwheel@v3.2.1
        env:
          CIBW_BUILD: cp310-* cp311-* cp312-* cp313-* cp314-*
          CIBW_ARCHS: "auto64"

          # Before build commands
          CIBW_BEFORE_BUILD_WINDOWS: >
            python3 -m pip install setuptools wheel Cython==3.1.4 &&
            python -c "import os, glob; [os.remove(f) for f in glob.glob('blacksheep/*.c') + glob.glob('blacksheep/*.so')]" &&
            make compile

          # Unix-based systems (Linux)
          CIBW_BEFORE_BUILD: >
            python3 -m pip install setuptools wheel Cython==3.1.4 &&
            make clean &&
            make compile
        with:
          output-dir: dist

      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}-${{ strategy.job-index }}
          path: dist/*.whl

  # Separate macOS wheel building
  build-macos-wheels:
    needs: build
    name: Build macOS wheels
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [macos-latest]  # macos-15-intel NOT supported
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "pypy-3.11"]
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip build wheel setuptools Cython==3.1.4

      - name: Clean and compile
        run: |
          make clean
          make compile

      - name: Build wheel
        run: |
          python -m build --wheel

      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}-${{ matrix.python-version }}
          path: dist/*.whl

  # Build PyPy wheel (pure Python)
  build-pypy-wheels:
    needs: build
    name: Build PyPy wheel
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["pypy-3.11"]
        os: [ubuntu-latest]

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 9

      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6
        with:
          python-version: ${{ matrix.python-version }}

      - name: Install dependencies
        run: |
          pip install -r requirements.pypy.txt

      - name: Run tests with PyPy
        run: |
          echo "Running unit tests..."
          pytest tests

      - name: Install build dependencies
        run: |
          pip install --upgrade build wheel setuptools

      - name: Build wheels
        run: |
          BLACKSHEEP_NO_EXTENSIONS=1 python -m build --wheel

      - uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}-${{ matrix.python-version }}
          path: dist/*.whl

  # Build source distribution
  build-sdist:
    needs: build
    name: Build source distribution
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0

      - name: Setup Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.13"

      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build Cython==3.1.4

      - name: Build sdist
        run: |
          python -m build --sdist

      - name: Upload sdist
        uses: actions/upload-artifact@v4
        with:
          name: wheels-sdist
          path: dist/*.tar.gz

  publish:
    runs-on: ubuntu-latest
    needs: [build-wheels, build-macos-wheels, build-pypy-wheels, build-sdist]
    if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_artifacts == 'Y')
    steps:
      - name: Download a distribution artifact
        uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          merge-multiple: true
          path: dist

      - name: Use Python 3.13
        uses: actions/setup-python@v5
        with:
          python-version: '3.13'

      - name: Install dependencies
        run: |
          pip install twine
      - name: Publish distribution 📦 to Test PyPI
        run: |
          twine upload -r testpypi dist/*
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.test_pypi_password }}

      - name: Publish distribution 📦 to PyPI
        run: |
          twine upload -r pypi dist/*
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.pypi_password }}

The same workflow, on Latchkey

Estimated ~20% faster on cache hits, plus fewer wasted runs and a safer supply chain. Added and changed lines are highlighted.

name: Main
 
on:
  workflow_dispatch:
    inputs:
      publish_artifacts:
        description: 'Publish artifacts (Y|N)'
        required: true
        default: 'N'
  release:
    types: [published]
  push:
    branches:
      - main
      - ci
    paths-ignore:
      - README.md
      - CHANGELOG.md
  pull_request:
    branches:
      - "*"
    paths-ignore:
      - README.md
      - CHANGELOG.md
 
env:
  PROJECT_NAME: blacksheep
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  windows-validation:
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
    runs-on: windows-latest
    if: github.event_name == 'pull_request' || github.event_name == 'push'
 
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 9
          submodules: false
 
      - name: Use Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          cache: 'pip'
          python-version: ${{ matrix.python-version }}
 
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install flake8
 
      - name: Compile Cython extensions
        run: |
          cython blacksheep/url.pyx
          cython blacksheep/exceptions.pyx
          cython blacksheep/headers.pyx
          cython blacksheep/cookies.pyx
          cython blacksheep/contents.pyx
          cython blacksheep/messages.pyx
          cython blacksheep/scribe.pyx
          cython blacksheep/baseapp.pyx
          python setup.py build_ext --inplace
 
      - name: Run tests
        run: |
          pytest
 
  build:
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest]
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "pypy-3.11"]
    runs-on: ${{ matrix.os }}
 
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 9
          submodules: false
 
      - name: Use Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          cache: 'pip'
          python-version: ${{ matrix.python-version }}
 
      - name: Install dependencies
        run: |
          VER='${{ matrix.python-version }}'
 
          if [ $VER == "pypy-3.11" ]; then
            echo "Install requirements for PyPy"
            pip install -r requirements.pypy.txt
          else
            echo "Install requirements for CPython"
            pip install -r requirements.txt
          fi
          pip install black==25.1.0 isort==6.0.1 flake8==7.2.0
 
      - name: Run tests for Pure-Python Code
        run: |
          echo "Running unit tests..."
          pytest tests
 
          echo "Running integration tests..."
          export APP_DEFAULT_ROUTER=false
          pytest itests
 
      - name: Compile Cython extensions
        if: matrix.python-version != 'pypy-3.11'
        run: |
          make compile
 
      - name: Run tests with Cython Code
        if: matrix.python-version != 'pypy-3.11'
        run: |
          echo "Running unit tests..."
          pytest tests
 
          echo "Running integration tests..."
          export APP_DEFAULT_ROUTER=false
          pytest itests
 
      - name: Run linters
        continue-on-error: true
        run: |
          echo "Running linters - if build fails here, please be patient! Feel free to ask for assistance."
 
          flake8 blacksheep
          flake8 tests
          flake8 itests
          isort --check-only blacksheep 2>&1
          isort --check-only tests 2>&1
          isort --check-only itests 2>&1
          black --check blacksheep 2>&1
          black --check tests 2>&1
          black --check itests 2>&1
 
      - name: Test Hypercorn
        run: |
          PYVER=`python -V 2>&1`
 
          if [ "${PYVER:0:-2}" == "Python 3.10" ]; then
            echo "Skipping Hypercorn tests, since it fails with Python 3.10.0 on Ubuntu (2021/10/24)"
          else
            APP_DEFAULT_ROUTER=false ASGI_SERVER=hypercorn pytest itests/test_server.py
          fi
 
      - name: Test Piccolo Admin / Starlette mount compatibility
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.14
        run: |
          echo "Running Starlette mount compatibility tests (ref: piccolo-orm/piccolo_admin#472)..."
          pip install piccolo-admin  # include the dedicated test
 
          pytest tests/test_piccolo_admin_compat.py -v
 
      - name: Install distribution dependencies
        run: pip install --upgrade twine setuptools wheel
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.12
 
      - name: Create distribution package
        run: python setup.py sdist
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.12
 
      - name: Upload distribution package
        uses: actions/upload-artifact@v4
        with:
          name: dist-ubuntu-no-wheels
          path: dist
        if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.12
 
      - name: Test imports with no optional dependencies
        run: |
          pip uninstall -y -r <(pip freeze)
          BLACKSHEEP_NO_EXTENSIONS="1" pip install -e .
          cd itests
          echo "Expect no import error…"
          python issue606.py
 
  # Build wheels with cibuildwheel
  build-wheels:
    timeout-minutes: 30
    needs: build
    name: Build ${{ matrix.os }} wheels
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os:
          [
            ubuntu-latest,
            ubuntu-24.04-arm,
            windows-latest,
            windows-11-arm,
            # macos-15-intel,  # Failing with "File Not Found" for wheels it builds
            # macos-latest,  # Failing with "File Not Found" for wheels it builds
          ]
 
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
 
      - name: Build wheels
        uses: pypa/cibuildwheel@v3.2.1
        env:
          CIBW_BUILD: cp310-* cp311-* cp312-* cp313-* cp314-*
          CIBW_ARCHS: "auto64"
 
          # Before build commands
          CIBW_BEFORE_BUILD_WINDOWS: >
            python3 -m pip install setuptools wheel Cython==3.1.4 &&
            python -c "import os, glob; [os.remove(f) for f in glob.glob('blacksheep/*.c') + glob.glob('blacksheep/*.so')]" &&
            make compile
 
          # Unix-based systems (Linux)
          CIBW_BEFORE_BUILD: >
            python3 -m pip install setuptools wheel Cython==3.1.4 &&
            make clean &&
            make compile
        with:
          output-dir: dist
 
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}-${{ strategy.job-index }}
          path: dist/*.whl
 
  # Separate macOS wheel building
  build-macos-wheels:
    timeout-minutes: 30
    needs: build
    name: Build macOS wheels
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [macos-latest]  # macos-15-intel NOT supported
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "pypy-3.11"]
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
 
      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          cache: 'pip'
          python-version: ${{ matrix.python-version }}
 
      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip build wheel setuptools Cython==3.1.4
 
      - name: Clean and compile
        run: |
          make clean
          make compile
 
      - name: Build wheel
        run: |
          python -m build --wheel
 
      - name: Upload wheels
        uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}-${{ matrix.python-version }}
          path: dist/*.whl
 
  # Build PyPy wheel (pure Python)
  build-pypy-wheels:
    timeout-minutes: 30
    needs: build
    name: Build PyPy wheel
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        python-version: ["pypy-3.11"]
        os: [ubuntu-latest]
 
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 9
 
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6
        with:
          cache: 'pip'
          python-version: ${{ matrix.python-version }}
 
      - name: Install dependencies
        run: |
          pip install -r requirements.pypy.txt
 
      - name: Run tests with PyPy
        run: |
          echo "Running unit tests..."
          pytest tests
 
      - name: Install build dependencies
        run: |
          pip install --upgrade build wheel setuptools
 
      - name: Build wheels
        run: |
          BLACKSHEEP_NO_EXTENSIONS=1 python -m build --wheel
 
      - uses: actions/upload-artifact@v4
        with:
          name: wheels-${{ matrix.os }}-${{ matrix.python-version }}
          path: dist/*.whl
 
  # Build source distribution
  build-sdist:
    timeout-minutes: 30
    needs: build
    name: Build source distribution
    if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
    runs-on: latchkey-small
 
    steps:
      - uses: actions/checkout@v5
        with:
          fetch-depth: 0
 
      - name: Setup Python
        uses: actions/setup-python@v6
        with:
          cache: 'pip'
          python-version: "3.13"
 
      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build Cython==3.1.4
 
      - name: Build sdist
        run: |
          python -m build --sdist
 
      - name: Upload sdist
        uses: actions/upload-artifact@v4
        with:
          name: wheels-sdist
          path: dist/*.tar.gz
 
  publish:
    timeout-minutes: 30
    runs-on: latchkey-small
    needs: [build-wheels, build-macos-wheels, build-pypy-wheels, build-sdist]
    if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_artifacts == 'Y')
    steps:
      - name: Download a distribution artifact
        uses: actions/download-artifact@v4
        with:
          pattern: wheels-*
          merge-multiple: true
          path: dist
 
      - name: Use Python 3.13
        uses: actions/setup-python@v5
        with:
          cache: 'pip'
          python-version: '3.13'
 
      - name: Install dependencies
        run: |
          pip install twine
      - name: Publish distribution 📦 to Test PyPI
        run: |
          twine upload -r testpypi dist/*
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.test_pypi_password }}
 
      - name: Publish distribution 📦 to PyPI
        run: |
          twine upload -r pypi dist/*
        env:
          TWINE_USERNAME: __token__
          TWINE_PASSWORD: ${{ secrets.pypi_password }}
 

What changed

1 third-party action is referenced by a movable tag. Pin it to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

This workflow runs 7 jobs (24 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow