Python CI Workflow
Purpose
Make Python CI prove the same behavior maintainers care about locally.
The practical job is to choose Python setup, uv installation, dependency sync, pytest, Ruff, mypy, package-build checks, path filters, and matrix scope without making CI broader or noisier than the project needs.
When To Use
- Use this skill when adding or changing CI for a Python repository.
- Use this skill when local Python validation and CI disagree.
- Use this skill when adding Python packages, services, FastAPI apps, FastMCP servers, or workspace members to an existing CI workflow.
- Use this skill before package or release workflows depend on CI results.
Source Check
Use repo-local files, checked-out dependency sources, Dash MCP or Dash HTTP for installed docsets, and then official project documentation when Dash/local coverage is missing or stale:
CI Planning Workflow
- Inspect local validation commands and project metadata:
rg --files -g 'pyproject.toml' -g 'uv.lock' -g '.python-version' -g '.github/workflows/*.yml' -g '.github/workflows/*.yaml'
- Inspect existing workflow files:
rg --files .github/workflows -g '*.yml' -g '*.yaml'
- Check Python version sources:
requires-python
.python-version
- workflow
python-version
- repository docs
- Decide job scope:
- dependency sync
- tests
- lint
- format check
- type check
- package build
- Decide matrix scope:
- one Python version for app/service CI unless compatibility is the point
- multiple Python versions for public packages that promise a version range
- one OS unless filesystem, process, path, native dependency, or user-facing CLI behavior requires cross-platform checks
- Keep local and CI commands aligned.
Local And CI Command Boundaries
For local development, prefer the narrowest useful shape:
uv sync --dev
uv run pytest
uv run ruff check .
uv run mypy .
Add formatting verification only when the repo enforces Ruff formatting:
uv run ruff format --check .
Add package validation only for package surfaces:
uv build
For reproducible CI in a repository that commits uv.lock, use a locked sync.
Include all extras only when the job intentionally validates every extra:
# Typical locked CI job
uv sync --locked --dev
# Use only when every optional feature is part of this job's contract
uv sync --locked --all-extras --dev
Do not copy --all-extras into application CI by default. A service with no
published extras should validate its actual runtime and development dependency
groups instead.
For workspaces, target package-specific jobs explicitly when the repo does not need a full workspace sweep:
uv run --package <package-name> pytest
uv run --package <package-name> mypy .
uv build --package <package-name>
GitHub Actions Shape
Use the repo's existing workflow style first.
For new GitHub Actions workflows:
- install
uv through the official setup action or documented installer path
- use
uv sync --locked --dev when the repository commits uv.lock; add
--all-extras only when the job intentionally validates all extras
- cache only when it measurably helps and the cache key includes lockfile state
- keep package build or publish steps separate from normal validation
- avoid CI secrets unless a workflow truly needs private package sources or publishing
Output Shape
Return:
Existing CI: workflows, Python versions, uv setup, and checks.
Local parity: local commands CI should mirror.
Change: workflow, matrix, cache, package build, or docs update.
Commands: exact local commands and CI job commands.
Residual risk: checks still manual, secrets needed, or matrix not covered.
Guardrails
- Do not publish packages from CI unless the user explicitly asked for release automation.
- Do not add broad OS or Python matrices without a concrete compatibility reason.
- Do not make CI depend on globally installed Python tools.
- Do not add machine-local paths, private checkout paths, or local package sources to workflows.
- Do not hide failing local validation by making CI narrower than the repo's documented checks.