Skip to content
Latchkey

CI workflow (pyca/pyopenssl)

The CI workflow from pyca/pyopenssl, explained and optimized by Latchkey.

D

CI health: D - needs work

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

Grade your own workflow free or run it on Latchkey →
Source: pyca/pyopenssl.github/workflows/ci.ymlLicense Apache-2.0View source

What it does

This is the CI workflow from the pyca/pyopenssl repository, a real project running GitHub Actions. It is shown here with attribution under its Apache-2.0 license.

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

The workflow

workflow (.yml)
name: CI
on:
  pull_request: {}
  push: {}

permissions:
  contents: read

jobs:
  linux:
    runs-on: ${{ matrix.PYTHON.OS || 'ubuntu-latest' }}
    strategy:
      fail-fast: false
      matrix:
        PYTHON:
          # Base builds
          - {VERSION: "3.9", NOXSESSION: "tests"}
          - {VERSION: "3.10", NOXSESSION: "tests"}
          - {VERSION: "3.11", NOXSESSION: "tests"}
          - {VERSION: "3.12", NOXSESSION: "tests"}
          - {VERSION: "3.13", NOXSESSION: "tests"}
          - {VERSION: "3.14", NOXSESSION: "tests"}
          - {VERSION: "3.14t", NOXSESSION: "tests"}
          - {VERSION: "pypy-3.11", NOXSESSION: "tests"}
          - {VERSION: "3.11", NOXSESSION: "tests-wheel", OS: "windows-latest"}
          - {VERSION: "3.14t", NOXSESSION: "tests-wheel", OS: "windows-latest"}
          # -cryptography-main
          - {VERSION: "3.9", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.10", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.11", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.12", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.13", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.14", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.14t", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "pypy-3.11", NOXSESSION: "tests-cryptography-main"}
          # -cryptography-minimum
          - {VERSION: "3.9", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.10", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.11", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.12", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.13", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "pypy-3.11", NOXSESSION: "tests-cryptography-minimum"}
          # Cryptography wheels
          - {VERSION: "3.9", NOXSESSION: "tests-cryptography-minimum-wheel"}
          - {VERSION: "3.9", NOXSESSION: "tests-wheel"}
          # Random order
          - {VERSION: "3.9", NOXSESSION: "tests-random-order"}
          # aws-lc
          - {VERSION: "3.14", NOXSESSION: "tests-cryptography-main", OPENSSL: {TYPE: "aws-lc", VERSION: "v1.73.0"}}
          # Meta
          - {VERSION: "3.9", NOXSESSION: "check-manifest"}
          - {VERSION: "3.11", NOXSESSION: "lint"}
          - {VERSION: "3.13", NOXSESSION: "mypy"}
          - {VERSION: "3.9", NOXSESSION: "docs"}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Setup python
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          python-version: ${{ matrix.PYTHON.VERSION }}
      - name: Cache custom OpenSSL
        if: matrix.PYTHON.OPENSSL
        id: ossl-cache
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
        with:
          path: ~/ossl
          key: ossl-${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}
      - name: Build custom OpenSSL
        if: matrix.PYTHON.OPENSSL && steps.ossl-cache.outputs.cache-hit != 'true'
        run: |
          git clone --depth 1 --branch ${{ matrix.PYTHON.OPENSSL.VERSION }} https://github.com/aws/aws-lc.git
          cmake -GNinja -B aws-lc/build -S aws-lc -DCMAKE_INSTALL_PREFIX="${HOME}/ossl" -DBUILD_TESTING=OFF
          ninja -C aws-lc/build install
          rm -rf aws-lc/
      - name: Set up custom OpenSSL env
        if: matrix.PYTHON.OPENSSL
        run: |
          cargo install bindgen-cli
          echo "OPENSSL_DIR=${HOME}/ossl" >> $GITHUB_ENV
      - run: python -m pip install nox
      - run: nox
        env:
          NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }}
      - uses: ./.github/actions/upload-coverage

  linux-docker:
    runs-on: ubuntu-latest
    container: ghcr.io/pyca/cryptography-runner-${{ matrix.TEST.CONTAINER }}
    strategy:
      fail-fast: false
      matrix:
        TEST:
          # cryptography-main used since there's no wheel
          - {CONTAINER: "ubuntu-rolling", NOXSESSION: "tests-cryptography-main"}
    name: "${{ matrix.TEST.NOXSESSION }} on ${{ matrix.TEST.CONTAINER }}"
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - run: /venv/bin/pip install nox
      - run: /venv/bin/nox
        env:
          RUSTUP_HOME: /root/.rustup
          NOXSESSION: ${{ matrix.TEST.NOXSESSION }}
      - uses: ./.github/actions/upload-coverage

  linux-downstream:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        DOWNSTREAM:
          - twisted
          - certbot
          - certbot-josepy
        PYTHON:
          - 3.12
    name: "Downstream tests for ${{ matrix.DOWNSTREAM }}"
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Setup python
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          python-version: ${{ matrix.PYTHON }}
      - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh install
      - run: pip install .
      - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh run

  all-green:
    runs-on: ubuntu-latest
    needs: [linux, linux-docker, linux-downstream]
    if: ${{ always() }}
    timeout-minutes: 3
    steps:
      - name: Decide whether the needed jobs succeeded or failed
        uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
        with:
          jobs: ${{ toJSON(needs) }}

      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        timeout-minutes: 3
        with:
          persist-credentials: false
      - name: Setup python
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          python-version: '3.12'
        timeout-minutes: 3
      - run: pip install coverage[toml]
      - name: Download coverage data
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: coverage-data-*
          merge-multiple: true
      - name: Combine coverage and fail if it's too low
        id: combinecoverage
        run: |
          set +e
          python -m coverage combine
          echo "## Python Coverage" >> $GITHUB_STEP_SUMMARY
          python -m coverage report -m --fail-under=98 > COV_REPORT
          COV_EXIT_CODE=$?
          cat COV_REPORT
          if [ $COV_EXIT_CODE -ne 0 ]; then
            echo "🚨 Python Coverage failed. Coverage too low." | tee -a $GITHUB_STEP_SUMMARY
          fi
          echo '```' >> $GITHUB_STEP_SUMMARY
          cat COV_REPORT >> $GITHUB_STEP_SUMMARY
          echo '```' >> $GITHUB_STEP_SUMMARY
          exit $COV_EXIT_CODE
      - name: Create coverage HTML
        run: python -m coverage html
        if: ${{ failure() && steps.combinecoverage.outcome == 'failure' }}
      - name: Upload HTML report.
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: _html-report
          path: htmlcov
          if-no-files-found: ignore

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: CI
on:
  pull_request: {}
  push: {}
 
permissions:
  contents: read
 
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
 
jobs:
  linux:
    timeout-minutes: 30
    runs-on: ${{ matrix.PYTHON.OS || 'ubuntu-latest' }}
    strategy:
      fail-fast: false
      matrix:
        PYTHON:
          # Base builds
          - {VERSION: "3.9", NOXSESSION: "tests"}
          - {VERSION: "3.10", NOXSESSION: "tests"}
          - {VERSION: "3.11", NOXSESSION: "tests"}
          - {VERSION: "3.12", NOXSESSION: "tests"}
          - {VERSION: "3.13", NOXSESSION: "tests"}
          - {VERSION: "3.14", NOXSESSION: "tests"}
          - {VERSION: "3.14t", NOXSESSION: "tests"}
          - {VERSION: "pypy-3.11", NOXSESSION: "tests"}
          - {VERSION: "3.11", NOXSESSION: "tests-wheel", OS: "windows-latest"}
          - {VERSION: "3.14t", NOXSESSION: "tests-wheel", OS: "windows-latest"}
          # -cryptography-main
          - {VERSION: "3.9", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.10", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.11", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.12", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.13", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.14", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "3.14t", NOXSESSION: "tests-cryptography-main"}
          - {VERSION: "pypy-3.11", NOXSESSION: "tests-cryptography-main"}
          # -cryptography-minimum
          - {VERSION: "3.9", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.10", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.11", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.12", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "3.13", NOXSESSION: "tests-cryptography-minimum"}
          - {VERSION: "pypy-3.11", NOXSESSION: "tests-cryptography-minimum"}
          # Cryptography wheels
          - {VERSION: "3.9", NOXSESSION: "tests-cryptography-minimum-wheel"}
          - {VERSION: "3.9", NOXSESSION: "tests-wheel"}
          # Random order
          - {VERSION: "3.9", NOXSESSION: "tests-random-order"}
          # aws-lc
          - {VERSION: "3.14", NOXSESSION: "tests-cryptography-main", OPENSSL: {TYPE: "aws-lc", VERSION: "v1.73.0"}}
          # Meta
          - {VERSION: "3.9", NOXSESSION: "check-manifest"}
          - {VERSION: "3.11", NOXSESSION: "lint"}
          - {VERSION: "3.13", NOXSESSION: "mypy"}
          - {VERSION: "3.9", NOXSESSION: "docs"}
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Setup python
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          cache: 'pip'
          python-version: ${{ matrix.PYTHON.VERSION }}
      - name: Cache custom OpenSSL
        if: matrix.PYTHON.OPENSSL
        id: ossl-cache
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
        with:
          path: ~/ossl
          key: ossl-${{ matrix.PYTHON.OPENSSL.TYPE }}-${{ matrix.PYTHON.OPENSSL.VERSION }}
      - name: Build custom OpenSSL
        if: matrix.PYTHON.OPENSSL && steps.ossl-cache.outputs.cache-hit != 'true'
        run: |
          git clone --depth 1 --branch ${{ matrix.PYTHON.OPENSSL.VERSION }} https://github.com/aws/aws-lc.git
          cmake -GNinja -B aws-lc/build -S aws-lc -DCMAKE_INSTALL_PREFIX="${HOME}/ossl" -DBUILD_TESTING=OFF
          ninja -C aws-lc/build install
          rm -rf aws-lc/
      - name: Set up custom OpenSSL env
        if: matrix.PYTHON.OPENSSL
        run: |
          cargo install bindgen-cli
          echo "OPENSSL_DIR=${HOME}/ossl" >> $GITHUB_ENV
      - run: python -m pip install nox
      - run: nox
        env:
          NOXSESSION: ${{ matrix.PYTHON.NOXSESSION }}
      - uses: ./.github/actions/upload-coverage
 
  linux-docker:
    timeout-minutes: 30
    runs-on: latchkey-small
    container: ghcr.io/pyca/cryptography-runner-${{ matrix.TEST.CONTAINER }}
    strategy:
      fail-fast: false
      matrix:
        TEST:
          # cryptography-main used since there's no wheel
          - {CONTAINER: "ubuntu-rolling", NOXSESSION: "tests-cryptography-main"}
    name: "${{ matrix.TEST.NOXSESSION }} on ${{ matrix.TEST.CONTAINER }}"
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - run: /venv/bin/pip install nox
      - run: /venv/bin/nox
        env:
          RUSTUP_HOME: /root/.rustup
          NOXSESSION: ${{ matrix.TEST.NOXSESSION }}
      - uses: ./.github/actions/upload-coverage
 
  linux-downstream:
    timeout-minutes: 30
    runs-on: latchkey-small
    strategy:
      fail-fast: false
      matrix:
        DOWNSTREAM:
          - twisted
          - certbot
          - certbot-josepy
        PYTHON:
          - 3.12
    name: "Downstream tests for ${{ matrix.DOWNSTREAM }}"
    steps:
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          persist-credentials: false
      - name: Setup python
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          cache: 'pip'
          python-version: ${{ matrix.PYTHON }}
      - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh install
      - run: pip install .
      - run: ./.github/downstream.d/${{ matrix.DOWNSTREAM }}.sh run
 
  all-green:
    runs-on: latchkey-small
    needs: [linux, linux-docker, linux-downstream]
    if: ${{ always() }}
    timeout-minutes: 3
    steps:
      - name: Decide whether the needed jobs succeeded or failed
        uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
        with:
          jobs: ${{ toJSON(needs) }}
 
      - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        timeout-minutes: 3
        with:
          persist-credentials: false
      - name: Setup python
        uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
        with:
          cache: 'pip'
          python-version: '3.12'
        timeout-minutes: 3
      - run: pip install coverage[toml]
      - name: Download coverage data
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          pattern: coverage-data-*
          merge-multiple: true
      - name: Combine coverage and fail if it's too low
        id: combinecoverage
        run: |
          set +e
          python -m coverage combine
          echo "## Python Coverage" >> $GITHUB_STEP_SUMMARY
          python -m coverage report -m --fail-under=98 > COV_REPORT
          COV_EXIT_CODE=$?
          cat COV_REPORT
          if [ $COV_EXIT_CODE -ne 0 ]; then
            echo "🚨 Python Coverage failed. Coverage too low." | tee -a $GITHUB_STEP_SUMMARY
          fi
          echo '```' >> $GITHUB_STEP_SUMMARY
          cat COV_REPORT >> $GITHUB_STEP_SUMMARY
          echo '```' >> $GITHUB_STEP_SUMMARY
          exit $COV_EXIT_CODE
      - name: Create coverage HTML
        run: python -m coverage html
        if: ${{ failure() && steps.combinecoverage.outcome == 'failure' }}
      - name: Upload HTML report.
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: _html-report
          path: htmlcov
          if-no-files-found: ignore
 

What changed

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 4 jobs (37 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