CI/CD¶
CI (continuous integration): every push runs lint, tests, and builds automatically. CD (continuous delivery/deployment): every green build can — or does — ship. This is how code goes from commit to production without a hero.
Table of Contents¶
- Installation / Setup
- Core Syntax
- Common Operations
- Practical Examples
- Common Patterns
- Best Practices
- Common Mistakes
- Troubleshooting
- Related Cheat Sheets
- Official Documentation
Installation / Setup¶
CI runs on a runner (a machine that executes your pipeline). Options:
| Platform | File location | Notes |
|---|---|---|
| GitHub Actions | .github/workflows/*.yml |
free for public repos; see github-actions.md |
| GitLab CI | .gitlab-ci.yml |
built-in runners |
| Jenkins | Jenkinsfile |
self-hosted, flexible |
| CircleCI | .circleci/config.yml |
cloud or self-hosted |
Local sanity before pushing: run the same commands yourself (lint, pytest, docker build) so CI failures are surprises only in the pipeline, not the code.
Core Syntax¶
Pipeline anatomy¶
A job = one runner + ordered steps. Steps fail fast: a failed test stops the job; a failed job can block the pipeline.
The stages of a typical data/ML pipeline¶
| Stage | Checks | Example |
|---|---|---|
| Lint/format | style, obvious bugs | ruff check . |
| Type check | static correctness | mypy src/ |
| Unit tests | logic works | pytest |
| Build | it packages | docker build |
| Quality gates | performance regressions | model metric thresholds |
| Deploy | ship to env | push image + rollout |
Quality gates for ML (don't just check "tests pass")¶
- Data checks: schema, drift, missing-value thresholds (see Data Engineering).
- Model checks: eval metrics on a fixed benchmark ≥ baseline; no NaN weights; reproducibility seed.
- Package checks: import works, inference latency bound, image size bound.
Common Operations¶
The basic loop¶
git push origin feature/foo # CI kicks off on the PR
# watch in the UI / CLI
gh pr checks # statuses
gh run list / gh run watch # logs
# merge only when all gates pass
Artifacts: what CI produces¶
| Artifact | Consumed by |
|---|---|
| lint/test reports | humans + bots |
| coverage report | code-review gate |
container image (app:v1.2.0) |
deploy step |
| model file + metrics | release, model registry |
| benchmark report | humans |
Artifacts should be immutable and versioned (tag = commit SHA or semantic version).
Cache to keep pipelines fast¶
# typical caches: pip/uv cache, ~/.cache/huggingface, Docker layer cache
# GitHub Actions: actions/cache with key = hash of lockfile
Practical Examples¶
1. CI for a Python data package¶
# .github/workflows/ci.yml
on:
push: { branches: [main] }
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix: { python-version: ["3.11", "3.12"] }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: ${{ matrix.python-version }} }
- run: pip install -e ".[dev]"
- run: ruff check src tests
- run: mypy src
- run: pytest --cov=src --cov-report=xml
- uses: codecov/codecov-action@v4
2. Model quality gate before merge¶
# pseudo-steps (adjust to your framework)
- run: python scripts/eval.py --dataset benchmark.parquet --artifact model.pt
env:
MIN_ACCURACY: "0.90"
- run: python scripts/assert_gate.py --metric accuracy --min $MIN_ACCURACY
3. CD: build, push, deploy on tag¶
on:
push: { tags: ["v*"] }
jobs:
release:
runs-on: ubuntu-latest
permissions: { contents: write, packages: write }
steps:
- uses: actions/checkout@v4
- run: docker build -t ghcr.io/you/app:${{ github.ref_name }} .
- run: docker push ghcr.io/you/app:${{ github.ref_name }}
- uses: actions/gh-pages@v1 # e.g. docs; or call your deploy platform's CLI
Common Patterns¶
# Path filtering: don't run everything for a README change
on:
pull_request:
paths: ["src/**", "tests/**", ".github/**"]
# Concurrency: cancel stale runs on the same branch
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
# Deploy only after ALL checks pass AND review approves
# main branch protection: "Require status checks to pass" + "Require PR review"
Best Practices¶
- Test the cheapest things first (lint → unit → integration) so failures fail fast.
- Fail the pipeline on warnings you care about — silent warnings rot.
- Keep builds under ~10 minutes; cache aggressively; parallelize jobs.
- One source of truth for versions: lockfile, image tag, and artifact tag all derive from the commit.
- Never let devs skip CI — branch protection enforces it.
- Deploy from artifacts, not from a re-run — the tested image is the shipped image.
- Make rollback one click/one command (previous image/tag).
- Version, version, version: reproducible CI today prevents mysterious "it passed last week."
Common Mistakes¶
| Mistake | Consequence | Fix |
|---|---|---|
| Running lint/tests only locally | Broken main arrives silently | Require CI on PRs |
| No caching | 5-minute installs every run | actions/cache on lockfiles |
| Tests that depend on order/network | Flaky CI | Deterministic fixtures; pin services |
| Building twice (CI test image ≠ deploy image) | Untested artifact ships | Single build → test that artifact |
| Secrets in workflow files | Leaked credentials | ${{ secrets.X }} only; never inline |
| Huge monolith pipeline | Slow feedback | Split jobs; fail fast; parallelize |
| Ignoring flaky tests | "Known" failures mask real ones | Fix or quarantine, don't tolerate |
| Deploying on every merge without gates | Risky | Branch protection + staging |
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
| CI passes locally, fails remotely | Version drift | Lock deps; match Python versions |
| Pipeline stuck "queued" | No runners / concurrency | Check runner availability |
Secret *** where expected |
Not configured | Add to repo settings; verify name |
| Tests pass alone, fail together | Shared state | Isolate temp files/DBs per test |
permission denied in CI |
Token scopes | Grant contents: write, packages: write as needed |
| Slow pipeline | Install every run | Cache pip/uv/HF cache |
Related Cheat Sheets¶
- github-actions.md — the concrete YAML for GitHub
- devops.md — the practices wrapping CI/CD
- docker.md — what CI builds
- git.md · github.md — triggers and PR flow
- MLOps — model-specific gates and deployment
- Home · Central Navigation Index
Official Documentation¶
Last Updated¶
2026-09-06 — Initial version.