30 Days of DevOps | Day 11/30 — CI/CD with GitHub Actions & Jenkins
A production-grade GitHub Actions CI pipeline for a Python Flask app — 5 stages (lint → test → build → security → notify), matrix testing across 3 Python versions, Docker build + smoke test, Trivy security scan, PR auto-comments, nightly dependency audit, and a local runner that mirrors the cloud pipeline exactly.
Push / PR
│
▼
┌───────────────────────────────────────────────────────────┐
│ JOB 1: Lint flake8 style check │
│ ✔ No style errors, no debug statements │
└────────────────────────┬──────────────────────────────────┘
│ needs: lint
┌────────────────────────▼──────────────────────────────────┐
│ JOB 2: Test pytest + coverage (3 Python versions) │
│ Matrix: py3.10 × py3.11 × py3.12 │
│ Coverage threshold: 80% │
└────────────────────────┬──────────────────────────────────┘
│ needs: test
┌────────────────────────▼──────────────────────────────────┐
│ JOB 3: Build docker build + smoke test │
│ Tags: sha-xxxx, branch-name, latest (on main) │
│ GHA cache: layers reused between runs │
└────────────────────────┬──────────────────────────────────┘
│ needs: build
┌────────────────────────▼──────────────────────────────────┐
│ JOB 4: Security Trivy image + Dockerfile scan │
│ Severity: HIGH, CRITICAL │
└────────────────────────┬──────────────────────────────────┘
│ needs: all, if: always()
┌────────────────────────▼──────────────────────────────────┐
│ JOB 5: Notify Markdown summary in Actions UI │
└───────────────────────────────────────────────────────────┘
day-11-github-actions-ci/
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # Main CI pipeline (5 jobs)
│ │ ├── pr-checks.yml # Fast PR validation + auto-comment
│ │ └── nightly-audit.yml # Scheduled dependency audit (2am UTC)
│ └── PULL_REQUEST_TEMPLATE.md # PR checklist template
├── app/
│ ├── src/
│ │ └── app.py # Flask app (health, info, calculate)
│ ├── tests/
│ │ └── test_app.py # 16 pytest tests
│ ├── requirements.txt # flask, pytest, pytest-cov, flake8
│ ├── setup.cfg # flake8 + pytest config
│ └── Dockerfile # multi-stage, non-root, healthcheck
├── scripts/
│ └── run_tests.sh # local CI runner (mirrors GHA)
├── docs/
│ └── how-actions-work.md # GitHub Actions concepts explained
├── .gitignore
└── README.md
git clone https://github.com/YOUR_USERNAME/day-11-github-actions-ci.git
cd day-11-github-actions-cichmod +x scripts/run_tests.sh
bash scripts/run_tests.shgit add .
git commit -m "feat: Day 11 - GitHub Actions CI pipeline"
git push origin mainGo to your repo → Actions tab → watch the pipeline run!
Triggers on every push + PRs to main.
on:
push:
branches: ['**']
pull_request:
branches: [main]| Job | What it does | Runs on |
|---|---|---|
| lint | flake8 on app/src/ |
every push |
| test | pytest matrix (3.10/3.11/3.12) | after lint |
| build | docker build + smoke test | after test |
| security | trivy image + config scan | after build |
| notify | markdown summary | always (even on failure) |
Fast lint + test on every PR. Posts result as a PR comment.
Runs pip-audit + Trivy at 2am UTC. Stores results as artifacts.
16 pytest tests covering unit + integration:
TestMath — add, multiply, divide (7 tests)
TestHealthEndpoint — status 200, returns "healthy" (3 tests)
TestInfoEndpoint — status 200, app name (2 tests)
TestCalculateEndpoint — all ops, errors, edge cases (7 tests)
# Run tests locally
cd app
pip install -r requirements.txt
pytest tests/ -v --cov=src --cov-report=term-missing| Feature | Where |
|---|---|
needs: job dependency |
test needs lint, build needs test |
| Matrix strategy | test on py3.10, 3.11, 3.12 in parallel |
if: always() |
notify job runs even on failure |
concurrency: |
cancel in-progress runs on new push |
cache: pip |
pip cache speeds up subsequent runs |
cache-from/to: gha |
Docker layer cache between runs |
upload-artifact |
save coverage + trivy reports |
GITHUB_STEP_SUMMARY |
custom markdown in Actions UI |
workflow_dispatch |
manual trigger from Actions tab |
schedule: cron |
nightly audit at 2am UTC |
Uncomment in ci.yml and add GitHub Secrets:
Settings → Secrets → Actions → New repository secret
DOCKERHUB_USERNAME = rohitrsrohit
DOCKERHUB_TOKEN = your-dockerhub-access-token
| Day | Topic |
|---|---|
| Days 6–10 | ✅ Docker & Container Security |
| Day 11 | ✅ GitHub Actions CI Pipeline (this project) |
| Day 12 | GitHub Actions CD with Deployment Gates |
| Day 13 | Jenkins Pipeline for Java Maven |
MIT — free to use, modify, and distribute.