Theme: "Put it all together with a real pipeline (test → build → package → deploy-ish)."
Walk through a realistic CI/CD pipeline that ties together concepts from every previous demo: triggers, caching, environments, secrets, artifacts, and step summaries.
- CI triggers —
on: push+on: pull_requestwith path filters so the pipeline only runs when relevant code changes - Caching —
actions/cachefor pip dependencies keyed onrequirements.txthash (from Demo 3) - Quality gates — Ruff linter + Pytest must both pass before building
- Matrix testing — Run tests against Python 3.11 and 3.12 in parallel
- Build artifacts — Package the app with metadata and upload via
actions/upload-artifact(from Demo 5) - Environment-based deployment — Staging and production environments with approval gates (from Demo 2)
- Build once, deploy many — The same artifact flows from build → staging → production
| File | Purpose |
|---|---|
.github/workflows/07-python-cicd.yml |
Full CI/CD pipeline — lint, test, build, deploy |
demos/demo7/app.py |
Minimal Python math service |
demos/demo7/test_app.py |
Pytest tests for the app |
demos/demo7/requirements.txt |
Python dependencies (pytest, ruff) |
┌─────────┐ ┌─────────────────┐
│ Lint │ │ Test (3.11) │
push / PR ──────►│ (Ruff) │ │ Test (3.12) │ (parallel)
└────┬────┘ └────────┬────────┘
│ │
└───────┬───────────┘
▼
┌──────────────┐
│ Build │
│ (artifact) │
└──────┬───────┘
▼
┌────────────────┐
│ Deploy → │
│ Staging │
└────────┬───────┘
▼
┌────────────────┐
│ Deploy → │ ← approval gate
│ Production │
└────────────────┘
Runs the Ruff linter and formatter check:
- run: ruff check . --output-format=text
- run: ruff format --check .Fast feedback — catches style issues before tests even run.
Tests against Python 3.11 and 3.12 in parallel:
strategy:
matrix:
python-version: ["3.11", "3.12"]- Caches pip to avoid re-downloading on every run
- Generates JUnit XML + text output
- Uploads test results as artifacts (downloadable per version)
Only runs after lint + test both pass (needs: [lint, test]):
- Packages
app.py+requirements.txt - Stamps a
BUILD_INFO.txtwith commit SHA, branch, run number - Uploads the build artifact with a 5-day retention
Uses environment: staging:
- Downloads the build artifact
- Verifies contents
- Simulates deployment + smoke test (runs the app)
Uses environment: production:
- Same artifact that passed staging
- If you've configured required reviewers on the
productionenvironment, this job pauses for approval - Demonstrates "build once, deploy many" — no rebuild between environments
| Concept | Source Demo | Used Here |
|---|---|---|
| Triggers & path filters | Demo 1 | on: push, on: pull_request with paths: |
| Environments & approval | Demo 2 | environment: staging, environment: production |
| Caching | Demo 3 | actions/cache for pip |
| Artifacts | Demo 5 | Upload test results + build package |
| Step summaries | Demo 5 | Each job writes to $GITHUB_STEP_SUMMARY |
| Log groups | Demo 5 | ::group:: for organized output |
- Push a change to
demos/demo7/or run manually from the Actions tab - Watch lint and test jobs run in parallel
- After they pass, the build job creates and uploads the artifact
- Staging deploys automatically; production waits for approval (if configured)
- Download the test-results artifacts to see JUnit XML output
- Check the Summary tab — each job writes a status card
Edit test_app.py and add a test that fails:
def test_intentional_failure(self):
assert add(1, 1) == 3 # This will fail!Push and watch:
- Test job fails with clear output
- Build and deploy jobs are skipped (they depend on test)
- Test artifacts are still uploaded (
if: always()) so you can debug
To see the full deployment flow with approval gates:
- Go to Settings → Environments
- Create
staging(no protection rules) - Create
productionwith Required reviewers enabled - Run the workflow — production will pause and wait for approval
- This is the standard pattern teams replicate and scale — lint, test, build, deploy through environments
- CI/CD is just workflows + environments + permissions + consistent building blocks from the previous demos
- Build once, deploy many — the same artifact flows from staging to production with no rebuild