Lean CI
This project follows a strict "CI as thin wrapper" philosophy. GitHub Actions workflows must never contain substantive build logic. All logic lives in locally-runnable tox environments; CI just calls them.
Principles
Every CI step must be reproducible locally. A developer should be able to run the exact same command on their laptop. If a step only works inside GitHub Actions, it violates this rule.
Workflows call tox environments, not inline shell. Build and test logic belongs in
tox.inienvironments -- never in multi-line YAMLrun:blocks. CI runsuvx --with tox-uv tox -e <env>.No scattered version pinning. Python version is in
pyproject.toml(requires-python). Node is infrontend/package.json(engines.node). Tool versions are managed in.pre-commit-config.yaml(ruff, mypy) andpyproject.toml(deps). Not in workflow YAML. Jobs that need Node useactions/setup-nodewithnode-version-file: frontend/package.json.Minimal setup actions.
astral-sh/setup-uvandactions/checkoutonly. Noactions/setup-python(uv handles it). No other setup actions without explicit justification. Exception:test.ymljobsuiandui-workflow-packuseactions/setup-nodebecausetox -e ui/ui-workflow-packneednpm.Pin actions to commit SHAs. Mutable tags (
@v4) allow upstream changes to affect CI without review. Always pin to a full commit SHA with a comment noting the tag (ADR-015).
tox as orchestration layer (ADR-047)
tox is the sole developer orchestration tool. Every CI step maps to a tox environment that developers run locally.
| tox environment | What it does | CI workflow |
|---|---|---|
tox -e lint |
Lint, format, type check (prek: ruff + mypy + pydoclint) | prek.yml |
tox -e unit |
Unit tests with coverage (--cov-fail-under=36) |
test.yml |
tox -e integration |
Integration tests (requires OPA binary) | test.yml |
tox -e ai |
AI extra tests (abbenay) | test.yml |
tox -e ui |
Playwright UI tests | test.yml |
tox -e ui-workflow-pack |
Pack @apme/ui-workflow release tarball |
test.yml |
tox -e grpc |
Regenerate gRPC stubs | manual |
tox -e helm |
Lint + package Helm chart | helm-charts.yml |
tox -e build |
Build container images | container-images.yml (GHCR) |
tox -e up |
Start the APME pod | manual |
tox -e down |
Stop the APME pod | manual |
tox -e pm |
Build + start + open browser | manual |
Install: uv tool install tox --with tox-uv
Workflow structure
CI has six workflows in .github/workflows/:
- prek.yml: Runs
prek(ruff lint, ruff format, mypy strict, pydoclint, uv-lock). Quality gate for code style and type safety. - test.yml: Runs
tox -e unit,tox -e integration,tox -e ui,tox -e ai, andtox -e ui-workflow-packas separate jobs. Quality gate for correctness. Coverage threshold is enforced via--cov-fail-underintox.ini. - container-images.yml: Builds and pushes multi-arch container images
(
linux/amd64+linux/arm64, ADR-063) to GHCR onmain, version tags, andworkflow_dispatch, then merges manifests viacontainers/ci/merge-manifests.sh(image list:containers/ci/images.txt). - helm-charts.yml: Lints/packages the Helm chart (
tox -e helm) and publishes to GitHub Pages via chart-releaser whendeploy/helm/apme/**changes onmain. Also publishes values profiles (tox -e helm-pages-profiles) tohttps://ansible.github.io/apme/. - deprecation-scrape.yml: Monthly cron scraping ansible-core for deprecation gaps.
- pr-feedback.yml: Labels PRs with failing checks or merge conflicts.
prek.yml and test.yml trigger on pull_request targeting main and use
concurrency groups with cancel-in-progress to avoid stacking runs on rapid
pushes.
Rules for modifications
When adding or modifying CI:
- DO add new build logic as a tox environment in
tox.ini, then call it from the workflow withuvx --with tox-uv tox -e <env>. - DO use SHA-pinned actions with a tag comment (e.g.,
actions/checkout@de0fac2e... # v6). - DO set
FORCE_COLOR: 1andPY_COLORS: 1as workflow-level env vars for readable CI logs. - DO use
ubuntu-24.04explicitly rather thanubuntu-latest. - DO NOT put multi-line shell scripts in
run:blocks. If it needs more than one command, it belongs in a tox environment or a script inscripts/. The git dirty check is the one exception -- it is a CI-only guard with no local equivalent. - DO NOT add
actions/setup-pythonor other setup actions.setup-uvhandles the Python toolchain. - DO NOT hardcode tool versions in YAML. Versions belong in
.pre-commit-config.yamlorpyproject.toml. - DO NOT add secrets or publishing steps without explicit approval.