Skip to content

refactor: Asynchronous cog/extension loading. #8014

refactor: Asynchronous cog/extension loading.

refactor: Asynchronous cog/extension loading. #8014

Workflow file for this run

# SPDX-License-Identifier: MIT
name: Lint & Test
on:
push:
branches:
- "master"
- "v[0-9]+.[0-9]+.x" # matches to backport branches, e.g. v3.6.x
- "run-ci/**"
tags:
- "*"
pull_request:
merge_group:
types: [checks_requested]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
defaults:
run:
shell: bash
env:
# https://docs.astral.sh/uv/reference/environment/
UV_LOCKED: 1
UV_NO_SYNC: 1
UV_PYTHON_DOWNLOADS: never
GITHUB_STEP_SUMMARY_HEADER: "<details><summary>#name#</summary>\n<pre>"
GITHUB_STEP_SUMMARY_FOOTER: "</pre></details>\n"
jobs:
lock-dependencies:
name: Lock dependencies
# The only purpose of this is to create a lockfile, which will be cached
# to be used with subsequent jobs.
# This provides somewhat of a middle ground and avoids having each job lock dependencies on its own,
# while still not needing to commit a lockfile to the repo, which is discouraged for libraries as per
# https://pdm-project.org/en/latest/usage/lockfile/
runs-on: ubuntu-latest
env:
UV_RESOLUTION: highest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Set up environment
id: setup-env
uses: ./.github/actions/setup-env
with:
use-cached-uv-lock: 'false' # we don't need the lock here, we will automatically download it below
- name: Restore uv lock
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
id: cache-uv-lock
with:
key: uv-lock-resolved-${{ env.UV_RESOLUTION }}
restore-keys: |
uv-lock-resolved-${{ env.UV_RESOLUTION }}
path: uv.lock
enableCrossOsArchive: true
- name: Check if uv.lock changed
id: uv-lock-changed
continue-on-error: true
if: steps.cache-uv-lock.outputs.cache-hit == 'true' || steps.cache-uv-lock.outputs.cache-hit == 'false'
run: |
uv lock --check
- name: Lock dependencies
if: steps.uv-lock-changed.outcome == 'failure' || steps.cache-uv-lock.outputs.cache-hit != 'true' && steps.cache-uv-lock.outputs.cache-hit != 'false'
id: lock-uv-deps
env:
UV_LOCKED: 0
run: uv lock --refresh
- uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
if: steps.lock-uv-deps.outcome == 'success'
name: upload-uv-lock
with:
key: uv-lock-resolved-${{ env.UV_RESOLUTION }}-${{ hashFiles('uv.lock') }}
path: uv.lock
enableCrossOsArchive: true
# Used to determine which python versions to test against.
# noxfile.py is the source of truth, which in turn draws from
# pyproject.toml's `project.requires-python` and `project.classifiers`.
python-versions:
name: Export nox sessions
runs-on: ubuntu-latest
outputs:
min-python: ${{ steps.set-matrix.outputs.min-python }}
docs: ${{ steps.set-matrix.outputs.docs }}
libcst: ${{ steps.set-matrix.outputs.libcst }}
pyright-sessions: ${{ steps.set-matrix.outputs.pyright-sessions }}
test-sessions: ${{ steps.set-matrix.outputs.test-sessions }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Set up environment
uses: ./.github/actions/setup-env
with:
use-cached-uv-lock: 'false' # we don't need the lock here
- name: Determine Python versions to test and lint against
id: set-matrix
# raw values (without a list) need the raw flag in order to not print quotes
# lists do not need the raw flag, as we parse them with fromJSON() later on
run: |
echo min-python=$(nox -s test --list --json | jq -rc '[.[].python][0]') | tee --append $GITHUB_OUTPUT
echo docs=$(nox -s docs --list --json | jq -rc '.[].python') | tee --append $GITHUB_OUTPUT
echo libcst=$(nox -s codemod --list --json | jq -rc '[.[].python][0]') | tee --append $GITHUB_OUTPUT
echo test-sessions=$(nox -s test --list --json | jq -c '[.[] | { python: .python, name: .session, extras: .call_spec.execution_group.extras, experimental: (.call_spec.execution_group.experimental == true) }]') | tee --append $GITHUB_OUTPUT
echo pyright-sessions=$(nox -s pyright --list --json | jq -c '[.[] | {python: .python, name: .session, paths: .call_spec.execution_group.pyright_paths, experimental: (.call_spec.execution_group.experimental == true) }]') | tee --append $GITHUB_OUTPUT
lint:
name: Run pre-commit hooks
runs-on: ubuntu-latest
env:
RUFF_OUTPUT_FORMAT: github
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Cache pip cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
key: ${{ runner.os }}-prek-pip-${{ hashFiles('**/pyproject.toml') }}
path: ~/.cache/pip
lookup-only: true
- name: Run prek
id: prek
uses: j178/prek-action@91fd7d7cf70ae1dee9f4f44e7dfa5d1073fe6623 # v1.0.11
# given the interoperability and the nature of prek, we stay unpinned for now
# note that this action by default uses the latest version of prek
# this is exactly how the pre-commit action worked, and we never had any issues
docs:
name: Build documentation
# unlike the other workflows, we explicitly use the same version as
# readthedocs (see .readthedocs.yml) here for consistency
runs-on: ubuntu-24.04
needs:
- python-versions
- lock-dependencies
- lint
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: '0'
persist-credentials: false
- name: Set up environment
id: setup-env
uses: ./.github/actions/setup-env
with:
python-version: ${{ needs.python-versions.outputs.docs }}
- name: Run sphinx-build
run: nox -s docs -- --keep-going -W -w $GITHUB_STEP_SUMMARY
typing:
name: ${{matrix.session.name}}
runs-on: ubuntu-latest
needs:
- python-versions
- lock-dependencies
- lint
strategy:
matrix:
session: ${{ fromJson(needs.python-versions.outputs.pyright-sessions) }}
fail-fast: false
continue-on-error: ${{ matrix.session.experimental }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Set up environment
id: setup-env
uses: ./.github/actions/setup-env
with:
python-version: ${{ matrix.session.python }}
- name: Add .venv/bin to PATH
id: venv-bin
run: |
uv venv .venv
dirname "$(uv python find)" >> $GITHUB_PATH
echo python="$(uv python find)" >> $GITHUB_OUTPUT
- name: Install dependencies
# `--no-venv` to install in the main pdm venv instead of nox's pyright-specific one
# because nox uses pdm internally to install, this means that the venv that is used is
# the one that pdm would create in project: `.venv`
# python is NOT forced here because the session name is explicit
env:
NOXSESSION: ${{ matrix.session.name }}
NOX_DEFAULT_VENV_BACKEND: "none"
## needed to install specific dependencies
## used internally when nox calls pip to install a dependency in the venv
PIP_PYTHON: ${{ steps.venv-bin.outputs.python }}
run: |
nox --install-only
- name: Run pyright (Linux)
uses: jakebailey/pyright-action@6cabc0f01c4994be48fd45cd9dbacdd6e1ee6e5e # v2.3.3
id: pyright-linux
with:
version: PATH
python-version: ${{ steps.setup-env.outputs.python-version }}
python-platform: "Linux"
annotate: ${{ matrix.session.python == needs.python-versions.outputs.min-python }} # only add comments for one version
warnings: true
extra-args: ${{join(matrix.session.paths, ' ')}}
- name: Run pyright (Windows)
uses: jakebailey/pyright-action@6cabc0f01c4994be48fd45cd9dbacdd6e1ee6e5e # v2.3.3
if: always() && (steps.pyright-linux.outcome == 'success' || steps.pyright-linux.outcome == 'failure')
with:
version: PATH
python-version: ${{ steps.setup-env.outputs.python-version }}
python-platform: "Windows"
annotate: false # only add comments for one platform (see above)
warnings: true
extra-args: ${{join(matrix.session.paths, ' ')}}
misc:
name: Check slots and build
runs-on: ubuntu-latest
needs:
- python-versions
- lock-dependencies
- lint
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: '0'
persist-credentials: false
- name: Set up environment
id: setup
uses: ./.github/actions/setup-env
with:
python-version: ${{ needs.python-versions.outputs.min-python }}
- name: Run misc checks
run: |
nox --tags misc
- name: List built files
run: |
ls -la dist/
- name: Check README.md renders properly on PyPI
run: |
uvx twine~=6.1 check --strict dist/*
codemod:
name: LibCST codemods
runs-on: ubuntu-latest
needs:
- python-versions
- lock-dependencies
- lint
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Set up environment
id: setup
uses: ./.github/actions/setup-env
with:
python-version: ${{ needs.python-versions.outputs.libcst }}
# run the libcst parsers and check for changes
- name: libcst codemod
run: |
nox -s codemod -- run-all
- name: Check for changes by LibCST
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "::error::Please run 'nox -s codemod -- run-all' locally and commit the changes." >&2;
echo "$GITHUB_STEP_SUMMARY_HEADER" | sed "s/#name#/LibCST Codemod/" >> $GITHUB_STEP_SUMMARY
echo "The libcst codemod made changes to the codebase. Please run 'uv run nox -s codemod -- run-all' locally and commit the changes." >> $GITHUB_STEP_SUMMARY
echo "::group::git diff"
git diff |& tee -a $GITHUB_STEP_SUMMARY
echo "::endgroup::"
echo "$GITHUB_STEP_SUMMARY_FOOTER" >> $GITHUB_STEP_SUMMARY
exit 1;
else
exit 0;
fi
test:
name: ${{ matrix.session.name }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
needs:
- python-versions
- lock-dependencies
- lint
strategy:
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
session: ${{ fromJson(needs.python-versions.outputs.test-sessions) }}
fail-fast: true
continue-on-error: ${{ matrix.session.experimental }}
env:
NOXSESSION: ${{ matrix.session.name }}
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Set up environment
id: setup-env
uses: ./.github/actions/setup-env
with:
python-version: ${{ matrix.session.python }}
- name: Install nox environment
run: |
nox --install-only
- name: Run pytest
id: run_tests
# use non-utc timezone, to test time/date-dependent features properly
env:
TZ: "America/New_York"
run: |
echo "$GITHUB_STEP_SUMMARY_HEADER" | sed "s/#name#/Test Summary/" >> $GITHUB_STEP_SUMMARY
nox --no-install -- --color=no --cov-report= | tee -a $GITHUB_STEP_SUMMARY
echo "$GITHUB_STEP_SUMMARY_FOOTER" >> $GITHUB_STEP_SUMMARY
- name: Print coverage output
if: always() && (steps.run_tests.outcome == 'success' || steps.run_tests.outcome == 'failure')
env:
UV_NO_SYNC: 0
NOXSESSION: coverage
run: |
echo "$GITHUB_STEP_SUMMARY_HEADER" | sed "s/#name#/Coverage Summary/" >> $GITHUB_STEP_SUMMARY
nox -- report | tee -a $GITHUB_STEP_SUMMARY
echo "$GITHUB_STEP_SUMMARY_FOOTER" >> $GITHUB_STEP_SUMMARY
# thanks to aiohttp for this part of the workflow
check: # This job does nothing and is only used for the branch protection
name: Check CI passed
if: always()
needs:
- lint
- docs
- typing
- misc
- codemod
- test
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
jobs: ${{ toJSON(needs) }}