hey-cicd/
βββ app/
β βββ app.py # Flask application
β βββ templates/
β β βββ index.html # Dashboard UI
β βββ static/
β βββ css/styles.css
β βββ js/main.js
βββ tests/
β βββ test_app.py # Unit tests
βββ k8s/
β βββ deployment.yaml # Kubernetes Deployment
β βββ service.yaml # Kubernetes Service
βββ .github/
β βββ workflows/
β βββ devsecops.yml # CI/CD Pipeline
βββ Dockerfile
βββ requirements.txt
βββ requirements-dev.txt
βββ README.md
| Method | Route | Description |
|---|---|---|
GET |
/ |
Dashboard UI |
GET |
/health |
Health check |
GET |
/api/status |
App info, uptime, Python version |
GET |
/api/greet/<name> |
Returns a greeting for the name |
POST |
/api/add |
Adds two numbers |
POST |
/api/calculate |
Calculator (add/subtract/multiply/divide/power/modulo) |
POST |
/api/pipeline/run |
Simulates a CI/CD pipeline run |
git clone https://github.com/YOUR_USERNAME/hey-cicd.git
cd hey-cicdpython3 -m venv .venv
source .venv/bin/activate # Mac/Linux
# .venv\Scripts\activate # Windowspip install -r requirements.txtpython3 app/app.pyhttp://localhost:5001
pip install -r requirements-dev.txt
python3 -m pytest --cov=app --cov-report=term-missingExpected output:
tests/test_app.py::test_home PASSED
tests/test_app.py::test_health PASSED
tests/test_app.py::test_greet PASSED
tests/test_app.py::test_add_numbers PASSED
tests/test_app.py::test_add_numbers_missing_fields PASSED
tests/test_app.py::test_calculator_multiply PASSED
tests/test_app.py::test_calculator_divide_by_zero PASSED
tests/test_app.py::test_status PASSED
8 passed in 0.Xs
# Health check
curl http://localhost:5001/health
# Greet someone
curl http://localhost:5001/api/greet/Nensi
# Add two numbers
curl -X POST http://localhost:5001/api/add \
-H "Content-Type: application/json" \
-d '{"number1": 10, "number2": 20}'
# Calculator
curl -X POST http://localhost:5001/api/calculate \
-H "Content-Type: application/json" \
-d '{"a": 6, "b": 3, "operation": "multiply"}'- Docker Desktop installed and running
docker build -t hey-cicd:latest .docker run -p 5001:5001 hey-cicd:latesthttp://localhost:5001
# See running containers
docker ps
# Stop the container
docker stop <container-id>
# Remove the image
docker rmi hey-cicd:latest
# Run in background (detached mode)
docker run -d -p 5001:5001 hey-cicd:latestThe pipeline runs automatically every time you push code to main or open a pull request.
Push to GitHub
β
βΌ
βββββββββββββββββββ
β STEP 1: Tests β pytest β runs all 8 unit tests
ββββββββββ¬βββββββββ
β
ββββββββββΌβββββββββ
β STEP 2: SAST β CodeQL β scans code for security issues
ββββββββββ¬βββββββββ
β
ββββββββββΌβββββββββ
β STEP 3: SCA β pip-audit β checks for vulnerable packages
ββββββββββ¬βββββββββ
β (all 3 must pass)
ββββββββββΌβββββββββ
β STEP 4: Build β docker build β creates the Docker image
ββββββββββ¬βββββββββ
β
ββββββββββΌβββββββββ
β STEP 5: Scan β Trivy β scans the Docker image for CVEs
ββββββββββ¬βββββββββ
β
ββββββββββΌβββββββββ
β STEP 6: Push β Pushes image to GitHub Container Registry
ββββββββββ¬βββββββββ
β (only on push to main)
ββββββββββΌβββββββββ
β STEP 7: Deploy β kubectl apply β deploys to Kubernetes
βββββββββββββββββββ
# Make a change, commit, and push
git add .
git commit -m "your message"
git push origin mainThen go to your GitHub repo β Actions tab to watch it run.
Go to GitHub repo β Settings β Secrets and variables β Actions and add:
| Secret Name | Value |
|---|---|
KUBECONFIG |
Contents of your ~/.kube/config file (needed for Step 7 deploy) |
βΉοΈ
GITHUB_TOKENis automatically provided by GitHub β you don't need to add it manually.
After Step 6 runs, your image is available at:
ghcr.io/YOUR_USERNAME/hey-cicd:latest
Go to GitHub repo β Packages to see it.
Do this if you want to deploy without the pipeline, directly from your terminal.
- A running Kubernetes cluster (minikube, k3s, or cloud)
kubectlinstalled and connected to your cluster
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yamlkubectl get pods
kubectl get service session17-python# If using minikube
minikube service session17-python
# Or access via NodePort
http://<your-node-ip>:30001# See all running pods
kubectl get pods
# See logs from a pod
kubectl logs <pod-name>
# Delete the deployment
kubectl delete -f k8s/deployment.yaml
kubectl delete -f k8s/service.yaml| Concept | Tool Used | Where |
|---|---|---|
| Unit Testing | pytest + pytest-cov | tests/test_app.py |
| SAST (Static Application Security Testing) | GitHub CodeQL | Pipeline Step 2 |
| SCA (Software Composition Analysis) | pip-audit | Pipeline Step 3 |
| Containerisation | Docker | Dockerfile |
| Container Image Scanning | Trivy | Pipeline Step 5 |
| Container Registry | GitHub Container Registry (GHCR) | Pipeline Step 6 |
| Orchestration | Kubernetes | k8s/ folder |
| CI/CD Automation | GitHub Actions | .github/workflows/devsecops.yml |
- Python 3.12 + Flask 3.x
- Docker
- Kubernetes
- GitHub Actions