Reusable workflow that wires together the full Python / uv CI pipeline in a single call. The consuming repo controls when it runs; this workflow controls how.
Replace
<current-sha>with the current SHA from the root README.
push / pull_request / workflow_dispatch
│
▼
┌──────────┐
│ security │ gitleaks + pip-audit + guarddog (parallel)
└─────┬────┘
│ (gates everything below)
├──────────────┬──────────────────────────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────────────┐
│ lint │ │ test │ │ docker-build │
└──┬───┘ └──┬───┘ │ (optional) │
│ │ └──────┬───────┘
└──────────────┴─────────────────────────┘
│ (all must pass or be skipped)
▼
┌──────────────────┐
│vulnerability-scan│ Syft + Grype (code + Docker)
└──────────────────┘
docker-build is opt-in. When disabled it is skipped and the vulnerability scan still runs.
# .github/workflows/ci.yml (in your consuming repo)
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
uses: orangitfi/platform-tooling/.github/workflows/python-ci.yml@<current-sha>
with:
run-docker-scan: falsejobs:
ci:
uses: orangitfi/platform-tooling/.github/workflows/python-ci.yml@<current-sha>
with:
image-name: my-api
run-docker-build: truejobs:
ci:
uses: orangitfi/platform-tooling/.github/workflows/python-ci.yml@<current-sha>
with:
working-directory: .
test-command: pytest tests/ -v --tb=short
uv-sync-args: --extra test
run-ruff-lint: true
run-ruff-format: true
dockerfile-path: Dockerfile
run-docker-build: true
image-name: my-api
fail-on-severity: high
run-docker-scan: truejobs:
ci:
uses: orangitfi/platform-tooling/.github/workflows/python-ci.yml@<current-sha>
with:
uv-sync-args: "--extra test --extra dev"
test-command: "pytest tests/ -x --tb=short"| Input | Default | Description |
|---|---|---|
working-directory |
. |
Directory containing pyproject.toml and uv.lock |
test-command |
pytest |
Command passed to uv run for tests |
uv-sync-args |
"" |
Extra arguments for uv sync (e.g. --extra test) |
run-ruff-lint |
true |
Run ruff check in the lint job |
run-ruff-format |
true |
Run ruff format --check in the lint job |
dockerfile-path |
Dockerfile |
Path to the Dockerfile (used by lint, docker-build, and vulnerability scan) |
run-docker-build |
false |
Validate the Dockerfile builds as an early quality gate (opt-in) |
image-name |
(repo name) | Docker image name for docker-build and vulnerability scan |
fail-on-severity |
high |
Minimum Grype severity for the vulnerability scan |
run-docker-scan |
true |
Build and scan the Docker image in the vulnerability scan |
| Job | Calls | Condition |
|---|---|---|
security |
python-security-scan |
always |
lint |
python-lint |
always (after security) |
test |
python-test |
always (after security) |
docker-build |
python-build-and-publish-docker (publish: false) |
only when run-docker-build: true |
vulnerability-scan |
python-vulnerability-scan |
after all above pass or are skipped |
| node-ci | python-ci | |
|---|---|---|
| Build step | npm run build (always) |
no transpile step — uv sync happens inside test |
| Optional parallel jobs | functional-e2e, visual-test |
docker-build |
| Playwright | configurable | not applicable |
| Linter | npm lint script | ruff (check + format), both toggleable |
| Dependency install | npm ci |
uv sync --frozen |
- Single line of CI: the consuming repo needs one
uses:line to get security scanning, linting, testing, and vulnerability scanning — in the right order, with the right gates. - No Dockerfile? Set
run-docker-scan: falseandrun-docker-build: false— the pipeline still runs all the Python-specific quality checks. - Lockfile enforced:
uv sync --frozenfails immediately ifuv.lockis out of sync withpyproject.toml, catching drift before tests run. - Scan before install: security and guarddog/pip-audit scans run against the lockfile before
uv syncinstalls anything. A compromised package is blocked before it executes.
- The
docker-buildjob callspython-build-and-publish-dockerwithpublish: false— it just validates the Dockerfile compiles. No credentials or 1Password access needed. Enable it on repos where a broken Dockerfile would be a surprise. - The
uv-sync-argsinput is the right place to install optional dependency groups:--extra test,--extra dev, or--group testif using uv's dependency groups. - ruff is installed ephemerally via
uvxin the lint job — it does not need to be inpyproject.toml. The lint job will work on a fresh project that hasn't added ruff yet. - Add this workflow as the sole required status check in branch protection rules: one check covers the entire pipeline.
- For nightly DAST scanning of the deployed service, pair this workflow with
dast-scanin a separate scheduled workflow.