Skip to content

tox

tox #1037

Workflow file for this run

---
name: tox
on:
merge_group:
branches:
- "main"
push:
branches:
- "main"
pull_request:
branches:
- "main"
schedule:
- cron: "0 0 * * *"
workflow_call:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
FORCE_COLOR: 1 # tox, pytest
PY_COLORS: 1
jobs:
build:
name: py${{ matrix.python-version }}-${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
# macOS rows validate arm64 wheels; only the bookend pythons.
matrix:
include:
- { os: ubuntu-24.04, python-version: "3.10", tox_env: py310 }
- { os: ubuntu-24.04, python-version: "3.11", tox_env: py311 }
- { os: ubuntu-24.04, python-version: "3.12", tox_env: py312 }
- { os: macos-14, python-version: "3.10", tox_env: py310 }
- { os: macos-14, python-version: "3.12", tox_env: py312 }
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0 # needed by setuptools-scm
submodules: true
- name: Install uv and pin python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: ${{ matrix.python-version }}
# bindep.txt -> apt; macOS ships these headers in its SDK,
# Windows skips the dep via a platform_system marker.
- name: Install bindep system packages (apt)
if: ${{ contains(matrix.os, 'ubuntu') }}
run: |
sudo apt-get update -y
mapfile -t apt_packages < <(uvx bindep -b -f bindep.txt || true)
if [ ${#apt_packages[@]} -gt 0 ]; then
sudo apt-get install -y --no-install-recommends "${apt_packages[@]}"
fi
- name: Install tox
run: uv tool install tox --with tox-uv --with "setuptools>=65.3"
- run: tox -e ${{ matrix.tox_env }} -- -k test_unit
- name: Archive logs
uses: actions/upload-artifact@v4
with:
name: logs-py${{ matrix.python-version }}-${{ matrix.os }}.zip
include-hidden-files: true
path: |
.tox/**/log/
.tox/**/.coverage*
.tox/**/coverage.xml
- name: Report failure if git reports dirty status
run: |
if [[ -n $(git status -s) ]]; then
# shellcheck disable=SC2016
echo -n '::error file=git-status::'
printf '### Failed as git reported modified and/or untracked files\n```\n%s\n```\n' "$(git status -s)" | tee -a "$GITHUB_STEP_SUMMARY"
exit 99
fi
# https://github.com/actions/toolkit/issues/193
# Tooling envs that don't depend on python version.
# `py310-devel` (vs bare `devel`) gives tox-uv a pyXY factor to
# resolve, avoiding a sys.executable path that uv venv -p rejects.
extras:
name: ${{ matrix.tox_env }}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
tox_env: [pkg, py310-devel]
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
submodules: true
- name: Install uv and pin python 3.10
uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: "3.10"
- name: Install bindep system packages (apt)
run: |
sudo apt-get update -y
mapfile -t apt_packages < <(uvx bindep -b -f bindep.txt || true)
if [ ${#apt_packages[@]} -gt 0 ]; then
sudo apt-get install -y --no-install-recommends "${apt_packages[@]}"
fi
- name: Install tox
run: uv tool install tox --with tox-uv --with "setuptools>=65.3"
- run: tox -e ${{ matrix.tox_env }} ${{ contains(matrix.tox_env, 'devel') && '-- -k test_unit' || '' }}
- name: Archive logs
uses: actions/upload-artifact@v4
with:
name: logs-${{ matrix.tox_env }}.zip
include-hidden-files: true
path: |
.tox/**/log/
.tox/**/.coverage*
.tox/**/coverage.xml
- name: Report failure if git reports dirty status
run: |
if [[ -n $(git status -s) ]]; then
# shellcheck disable=SC2016
echo -n '::error file=git-status::'
printf '### Failed as git reported modified and/or untracked files\n```\n%s\n```\n' "$(git status -s)" | tee -a "$GITHUB_STEP_SUMMARY"
exit 99
fi
# https://github.com/actions/toolkit/issues/193
# Disabled - pre-commit.ci runs the same hooks on every PR.
lint:
if: false
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: "3.10"
- run: uv tool install tox --with tox-uv --with "setuptools>=65.3"
- run: tox -e lint
# Disabled - Read the Docs builds the docs on every push.
docs:
if: false
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v6
with:
enable-cache: true
python-version: "3.10"
- run: uv tool install tox --with tox-uv --with "setuptools>=65.3"
- run: tox -e docs
check:
if: always()
environment: check
permissions:
id-token: write
checks: read
needs:
- build
- extras
runs-on: ubuntu-24.04
steps:
# checkout needed for codecov action which needs codecov.yml file
- uses: actions/checkout@v5
- name: Merge logs into a single archive
uses: actions/upload-artifact/merge@v4
with:
name: logs.zip
include-hidden-files: true
pattern: logs-*.zip
# artifacts like py312.zip and py312-macos do have overlapping files
separate-directories: true
- name: Download artifacts
uses: actions/download-artifact@v5
continue-on-error: true # to allow rerunning this job
with:
name: logs.zip
path: .
- name: Check each test stage produced at least one coverage artifact
run: |
shopt -s nullglob
# Only `logs-py*` artifacts come from envs that run the
# test suite; `logs-pkg.zip` and friends never write
# coverage.xml so we don't expect any inside them.
missing=()
for artifact in logs-py*.zip; do
if ! find "$artifact" -name coverage.xml | grep -q .; then
missing+=("$artifact")
fi
done
if [ ${#missing[@]} -gt 0 ]; then
printf '::error::No coverage.xml in: %s\n' "${missing[*]}"
exit 1
fi
# Single uploads inside check job for codecov to allow use to retry
# it when it fails without running tests again. Fails often enough!
- name: Upload junit xml reports
# PRs from forks might not have access to the secret
if: env.CODECOV_TOKEN
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN || env.CODECOV_TOKEN }}
uses: codecov/test-results-action@v1
with:
name: ${{ matrix.name }}
files: "*/tests/output/junit/*.xml"
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload coverage data
uses: codecov/codecov-action@v5
with:
name: ${{ matrix.name }}
# verbose: true # optional (default = false)
fail_ci_if_error: true
use_oidc: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) }} # cspell:ignore oidc
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}
- name: Delete Merged Artifacts
uses: actions/upload-artifact/merge@v4
with:
include-hidden-files: true
delete-merged: true
pattern: logs-*.zip