Linting in CI/CD
CI lint gates block merges when code violates style, type, or security rules. Every pull request gets the same checks regardless of local editor setup.
Search across all documentation pages
CI lint gates block merges when code violates style, type, or security rules. Every pull request gets the same checks regardless of local editor setup.
# .github/workflows/lint.yml
name: lint
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv sync --frozen --group dev
- run: uv run ruff check .
- run: uv run ruff format --check .
- run: uv run mypy src/When to reach for this:
name: quality
on:
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
with:
python-version: "3.14"
- run: uv sync --frozen --group dev
- run: uv run ruff check .
- run: uv run ruff format --check .
- run: uv run mypy src/
- run: uv run pytest --cov=src --cov-fail-under=80
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv sync --frozen --group dev
- run: uv run pre-commit run --all-filesWhat this demonstrates:
uv sync --frozen ensures lockfile integritysrc/ only (not tests)| Step | Command | Fails on |
|---|---|---|
| Lint | ruff check . | Style, bugs, security |
| Format | ruff format --check . | Unformatted code |
| Types | mypy src/ | Type errors |
| Hooks | pre-commit run --all-files | Any hook failure |
.venv keyed on lockfile hash.ruff_cache and .mypy_cache--check on formatter - CI auto-commits formatting. Fix: ruff format --check fails instead of fixing.uv.lock; use --frozen.uv sync --group dev before mypy.migrations/ - noisy failures on generated code. Fix: exclude in ruff config.| Alternative | Use When | Don't Use When |
|---|---|---|
| Pre-commit.ci | Managed hook runner | Self-hosted CI required |
| Reviewdog | Inline PR annotations | Simple pass/fail is enough |
| SonarQube | Enterprise compliance | Small projects |
Separate jobs run in parallel and give clearer failure messages.
setup-uv handles caching. Key on uv.lock hash.
No. Fail and let the developer fix locally. Auto-fix in CI hides the problem.
Use ruff check $(git diff --name-only origin/main) for speed on large repos.
Same commands in a script: block after installing uv.
Yes, on push to main as a safety net. PR checks are the primary gate.
pytest --cov-fail-under=80 in the test job alongside lint.
Use path filters: paths-ignore: ['**.md'] on the workflow trigger.
Tools like reviewdog or GitHub Actions problem matchers annotate the diff.
Scope mypy to src/, cache aggressively, lint only changed files on large repos.
Stack versions: This page was written for Python 3.14.0, FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026