CI workflow (hikari-py/hikari)
The CI workflow from hikari-py/hikari, explained and optimized by Latchkey.
CI health: A - excellent
Point runs-on at Latchkey and get job timeouts, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the CI workflow from the hikari-py/hikari 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
name: CI
on:
push:
branches:
- master
pull_request:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
strategy:
# Allows for matrix sub-jobs to fail without cancelling the rest
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", 3.11, 3.12, 3.13, 3.14]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
python-version: ${{ matrix.python-version }}
activate-environment: true
# TODO(https://github.com/astral-sh/setup-uv/issues/226): Remove this.
prune-cache: ${{ matrix.os != 'windows-latest' }}
- name: Test Installation
shell: bash
run: |
uv pip install .
uv pip uninstall hikari
uv pip install .[speedups]
uv pip uninstall hikari
- name: Run tests
shell: bash
run: |
uv sync --frozen --only-group nox
nox -s pytest -- --coverage
nox -s pytest-all-features -- --coverage --cov-append
python scripts/ci/normalize_coverage.py
- name: Upload coverage
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: .coverage.${{ matrix.os }}.${{ matrix.python-version }}
path: .coverage
retention-days: 1
if-no-files-found: error
include-hidden-files: true
upload-coverage:
needs: [test]
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
python-version: 3.14
activate-environment: true
- name: Download coverage
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: coverages/
# Not specifying any name will lead to the download of all available artifacts
# Since artifacts v2, we can no longer upload multiple artifacts
# with the same name and have them re-download as if it were a directory.
# For this reason, we need to download all available artifacts, filter
# out for only coverage files and then place them in the root directory
# with the name of the artifact
- name: Extract individual coverage files
run: |
cd coverages
for coverage_dir in ./.coverage.*; do
mv "$coverage_dir/.coverage" "../$coverage_dir"
rmdir "$coverage_dir"
done
cd ..
- name: Combine coverage
run: |
uv sync --frozen --only-group coverage
coverage combine
coverage xml
coverage report
- name: Upload coverage to codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
linting:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
python-version: "3.10"
activate-environment: true
- name: Install nox
run: |
uv sync --frozen --only-group nox
- name: Check stubs
if: always() && !cancelled()
run: |
nox -s generate-stubs
if [ "$(git status --short)" ]; then
echo "Stubs were not updated accordingly to the changes. Please run 'nox -s generate-stubs' and commit the changes to fix this."
exit 1
fi
- name: Audit
if: always() && !cancelled()
run: |
nox -s audit
- name: Mypy
if: always() && !cancelled()
run: |
nox -s mypy
- name: Verify types
if: always() && !cancelled()
run: |
nox -s verify-types
- name: Ruff
if: always() && !cancelled()
run: |
nox -s ruff -- --output-format=github
- name: Slotscheck
if: always() && !cancelled()
run: |
nox -s slotscheck
- name: Codespell
if: always() && !cancelled()
run: |
nox -s codespell
- name: Check trailing whitespaces
if: always() && !cancelled()
run: |
nox -s check-trailing-whitespaces
twemoji:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
python-version: 3.14
activate-environment: true
- name: Test twemoji mapping
run: |
uv sync --frozen --only-group nox
nox -s twemoji-test
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup uv
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
# NOTE: This should be kept up to date with .readthedocs.yaml
python-version: 3.13
activate-environment: true
- name: Build documentation
run: |
uv sync --frozen --only-group nox
nox -s mkdocs
- name: Upload artifacts
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: docs
path: public/docs
retention-days: 2
if-no-files-found: error
# Allows us to add this as a required check in Github branch rules, as all the other jobs are subject to change
ci-done:
needs: [upload-coverage, linting, twemoji, docs]
if: always() && !cancelled()
runs-on: ubuntu-latest
steps:
- name: Set status based on required jobs
env:
RESULTS: ${{ join(needs.*.result, ' ') }}
run: |
for result in $RESULTS; do
if [ "$result" != "success" ]; then
exit 1
fi
done
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: CI on: push: branches: - master pull_request: branches: - master concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: test: timeout-minutes: 30 strategy: # Allows for matrix sub-jobs to fail without cancelling the rest fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.10", 3.11, 3.12, 3.13, 3.14] runs-on: ${{ matrix.os }} steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: ${{ matrix.python-version }} activate-environment: true # TODO(https://github.com/astral-sh/setup-uv/issues/226): Remove this. prune-cache: ${{ matrix.os != 'windows-latest' }} - name: Test Installation shell: bash run: | uv pip install . uv pip uninstall hikari uv pip install .[speedups] uv pip uninstall hikari - name: Run tests shell: bash run: | uv sync --frozen --only-group nox nox -s pytest -- --coverage nox -s pytest-all-features -- --coverage --cov-append python scripts/ci/normalize_coverage.py - name: Upload coverage uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: .coverage.${{ matrix.os }}.${{ matrix.python-version }} path: .coverage retention-days: 1 if-no-files-found: error include-hidden-files: true upload-coverage: timeout-minutes: 30 needs: [test] runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: 3.14 activate-environment: true - name: Download coverage uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: path: coverages/ # Not specifying any name will lead to the download of all available artifacts # Since artifacts v2, we can no longer upload multiple artifacts # with the same name and have them re-download as if it were a directory. # For this reason, we need to download all available artifacts, filter # out for only coverage files and then place them in the root directory # with the name of the artifact - name: Extract individual coverage files run: | cd coverages for coverage_dir in ./.coverage.*; do mv "$coverage_dir/.coverage" "../$coverage_dir" rmdir "$coverage_dir" done cd .. - name: Combine coverage run: | uv sync --frozen --only-group coverage coverage combine coverage xml coverage report - name: Upload coverage to codecov uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 linting: timeout-minutes: 30 runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: "3.10" activate-environment: true - name: Install nox run: | uv sync --frozen --only-group nox - name: Check stubs if: always() && !cancelled() run: | nox -s generate-stubs if [ "$(git status --short)" ]; then echo "Stubs were not updated accordingly to the changes. Please run 'nox -s generate-stubs' and commit the changes to fix this." exit 1 fi - name: Audit if: always() && !cancelled() run: | nox -s audit - name: Mypy if: always() && !cancelled() run: | nox -s mypy - name: Verify types if: always() && !cancelled() run: | nox -s verify-types - name: Ruff if: always() && !cancelled() run: | nox -s ruff -- --output-format=github - name: Slotscheck if: always() && !cancelled() run: | nox -s slotscheck - name: Codespell if: always() && !cancelled() run: | nox -s codespell - name: Check trailing whitespaces if: always() && !cancelled() run: | nox -s check-trailing-whitespaces twemoji: timeout-minutes: 30 runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: python-version: 3.14 activate-environment: true - name: Test twemoji mapping run: | uv sync --frozen --only-group nox nox -s twemoji-test docs: timeout-minutes: 30 runs-on: latchkey-small steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: # NOTE: This should be kept up to date with .readthedocs.yaml python-version: 3.13 activate-environment: true - name: Build documentation run: | uv sync --frozen --only-group nox nox -s mkdocs - name: Upload artifacts if: github.event_name == 'pull_request' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: docs path: public/docs retention-days: 2 if-no-files-found: error # Allows us to add this as a required check in Github branch rules, as all the other jobs are subject to change ci-done: timeout-minutes: 30 needs: [upload-coverage, linting, twemoji, docs] if: always() && !cancelled() runs-on: latchkey-small steps: - name: Set status based on required jobs env: RESULTS: ${{ join(needs.*.result, ' ') }} run: | for result in $RESULTS; do if [ "$result" != "success" ]; then exit 1 fi done
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Add a job timeout so a hung step cannot burn hours of runner time.
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:
- Dependency installs
This workflow runs 6 jobs (20 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.