Skip to content

Commit 06f209a

Browse files
pepegapepega
authored andcommitted
feat: complete lab03 ci pipeline
1 parent b28a181 commit 06f209a

5 files changed

Lines changed: 212 additions & 116 deletions

File tree

.github/workflows/python-ci.yml

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,57 +15,64 @@ on:
1515
- "app_python/**"
1616
- ".github/workflows/python-ci.yml"
1717

18+
permissions:
19+
contents: read
20+
1821
concurrency:
1922
group: python-ci-${{ github.ref }}
2023
cancel-in-progress: true
2124

25+
env:
26+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
27+
2228
jobs:
23-
test:
29+
lint-test:
30+
name: Lint and test (Python ${{ matrix.python-version }})
2431
runs-on: ubuntu-latest
32+
strategy:
33+
fail-fast: true
34+
matrix:
35+
python-version: ["3.11", "3.12"]
2536

2637
steps:
27-
- name: Checkout repository
38+
- name: Checkout
2839
uses: actions/checkout@v4
2940

3041
- name: Set up Python
3142
uses: actions/setup-python@v5
3243
with:
33-
python-version: "3.12"
44+
python-version: ${{ matrix.python-version }}
3445
cache: "pip"
3546
cache-dependency-path: app_python/requirements.txt
3647

3748
- name: Install dependencies
38-
working-directory: app_python
3949
run: |
4050
python -m pip install --upgrade pip
41-
pip install -r requirements.txt
51+
python -m pip install -r app_python/requirements.txt
4252
43-
- name: Lint with ruff
44-
working-directory: app_python
45-
run: ruff check .
53+
- name: Lint (ruff)
54+
run: python -m ruff check app_python
4655

4756
- name: Run tests
48-
working-directory: app_python
49-
run: pytest -q
57+
run: python -m pytest -v app_python/tests
5058

5159
- name: Snyk security scan
52-
if: ${{ secrets.SNYK_TOKEN != '' }}
53-
uses: snyk/actions/python@master
60+
if: matrix.python-version == '3.12' && github.event_name == 'push' && secrets.SNYK_TOKEN != ''
61+
uses: snyk/actions/python@v3
5462
env:
5563
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
5664
with:
57-
args: --severity-threshold=high
65+
args: --severity-threshold=high --file=requirements.txt
66+
working-directory: app_python
5867

59-
docker:
60-
needs: test
61-
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/lab03') }}
68+
docker-build-push:
69+
name: Build and push Docker image
6270
runs-on: ubuntu-latest
63-
64-
env:
65-
DOCKER_IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/devops-info-service
71+
needs: lint-test
72+
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/lab03')
6673

6774
steps:
68-
- name: Checkout repository
75+
- name: Checkout
6976
uses: actions/checkout@v4
7077

7178
- name: Set up Docker Buildx
@@ -77,15 +84,23 @@ jobs:
7784
username: ${{ secrets.DOCKERHUB_USERNAME }}
7885
password: ${{ secrets.DOCKERHUB_TOKEN }}
7986

80-
- name: Generate version
81-
run: echo "VERSION=$(date +'%Y.%m.%d')" >> $GITHUB_ENV
87+
- name: Set image version
88+
run: |
89+
CALVER=$(date +"%Y.%m")
90+
VERSION="${CALVER}.${GITHUB_RUN_NUMBER}"
91+
echo "CALVER=${CALVER}" >> $GITHUB_ENV
92+
echo "VERSION=${VERSION}" >> $GITHUB_ENV
93+
echo "IMAGE_NAME=pepegx/devops-info-service" >> $GITHUB_ENV
8294
83-
- name: Build and push Docker image
95+
- name: Build and push
8496
uses: docker/build-push-action@v6
8597
with:
8698
context: app_python
8799
file: app_python/Dockerfile
88100
push: true
89101
tags: |
90-
${{ env.DOCKER_IMAGE }}:${{ env.VERSION }}
91-
${{ env.DOCKER_IMAGE }}:latest
102+
${{ env.IMAGE_NAME }}:${{ env.VERSION }}
103+
${{ env.IMAGE_NAME }}:${{ env.CALVER }}
104+
${{ env.IMAGE_NAME }}:latest
105+
cache-from: type=gha
106+
cache-to: type=gha,mode=max

app_python/README.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# DevOps Info Service
22

3-
[![Python CI](https://github.com/pepegx/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg)](https://github.com/pepegx/DevOps-Core-Course/actions/workflows/python-ci.yml)
3+
[![Python CI](https://github.com/pepegx/DevOps-Core-Course/actions/workflows/python-ci.yml/badge.svg?branch=master)](https://github.com/pepegx/DevOps-Core-Course/actions/workflows/python-ci.yml)
44

55
> A web service that provides comprehensive system and runtime information for DevOps monitoring and diagnostics.
66
@@ -114,20 +114,6 @@ HOST=127.0.0.1 PORT=3000 DEBUG=true python app.py
114114
gunicorn -w 4 -b 0.0.0.0:5000 app:app
115115
```
116116

117-
## Test Suite
118-
119-
Run the test suite locally with:
120-
121-
```bash
122-
pytest -q
123-
```
124-
125-
Run linting locally with:
126-
127-
```bash
128-
ruff check .
129-
```
130-
131117
## Docker
132118

133119
### Build Image (pattern)
@@ -225,6 +211,18 @@ Health check endpoint for monitoring systems and Kubernetes probes.
225211

226212
## Testing
227213

214+
### Unit Tests (pytest)
215+
216+
```bash
217+
python -m pytest -v
218+
```
219+
220+
Or with python3:
221+
222+
```bash
223+
python3 -m pytest -v
224+
```
225+
228226
### Using curl
229227

230228
```bash

app_python/docs/LAB03.md

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Lab 3 — Continuous Integration (CI/CD): Implementation Report
1+
# Lab 3 — CI/CD: Implementation Report
22

33
**Student:** Danil Fishchenko
44
**Date:** January 31, 2026
@@ -8,59 +8,124 @@
88

99
## 1. Overview
1010

11-
- **Testing framework:** pytest — concise syntax, strong fixtures ecosystem, widely used for Flask apps.
12-
- **Coverage:** Tests validate `GET /`, `GET /health`, and error responses (404, 405).
13-
- **Workflow triggers:** Runs on push to `master` and `lab03`, and on PRs to `master`, only when `app_python/**` or workflow file changes.
14-
- **Versioning strategy:** **CalVer** (`YYYY.MM.DD`) for Docker tags — simple and time-based for a continuously deployed service.
11+
**Testing framework:** `pytest` (simple syntax, strong fixtures ecosystem, widely used in Python projects).
12+
**Coverage:** All endpoints (`GET /`, `GET /health`) plus error case (`GET /missing`).
13+
**CI trigger:** Runs on `push` to `master`/`lab03` and on PRs to `master`, limited to `app_python/**` and workflow changes.
14+
**Versioning strategy:** **CalVer** (`YYYY.MM` + GitHub `run_number`). This fits continuous delivery and makes releases time‑based.
1515

1616
---
1717

1818
## 2. Workflow Evidence
1919

20-
-**Successful workflow run:** TODO — add GitHub Actions run link after push.
21-
-**Tests passing locally (terminal output):**
20+
**Local tests (pytest):**
21+
```
22+
========================== test session starts ==========================
23+
platform darwin -- Python 3.14.0, pytest-7.4.3, pluggy-1.6.0 -- /Users/pepega/Documents/devopss/DevOps-Core-Course/.venv/bin/python
24+
cachedir: .pytest_cache
25+
rootdir: /Users/pepega/Documents/devopss/DevOps-Core-Course
26+
plugins: flask-1.3.0
27+
collected 3 items
28+
29+
app_python/tests/test_app.py::test_index_success PASSED [ 33%]
30+
app_python/tests/test_app.py::test_health_success PASSED [ 66%]
31+
app_python/tests/test_app.py::test_not_found_error PASSED [100%]
32+
33+
=========================== 3 passed in 0.10s ===========================
34+
```
2235

36+
**Local lint (ruff):**
2337
```
24-
.... [100%]
25-
4 passed in 0.07s
38+
All checks passed!
2639
```
2740

28-
-**Local run + endpoint checks:**
41+
**Local endpoint testing:**
42+
43+
`GET /health`
44+
```
45+
{
46+
"status": "healthy",
47+
"timestamp": "2026-01-31T11:10:41.601350+00:00",
48+
"uptime_seconds": 5
49+
}
50+
```
2951

52+
`GET /`
3053
```
31-
{"endpoints":[{"description":"Service and system information","method":"GET","path":"/"},{"description":"Health check endpoint","method":"GET","path":"/health"}],"request":{"client_ip":"127.0.0.1","me
32-
{"status":"healthy","timestamp":"2026-01-31T11:00:38.653301+00:00","uptime_seconds":1}
54+
{
55+
"endpoints": [
56+
{
57+
"description": "Service and system information",
58+
"method": "GET",
59+
"path": "/"
60+
},
61+
{
62+
"description": "Health check endpoint",
63+
"method": "GET",
64+
"path": "/health"
65+
}
66+
],
67+
"request": {
68+
"client_ip": "127.0.0.1",
69+
"method": "GET",
70+
"path": "/",
71+
"user_agent": "curl/8.7.1"
72+
},
73+
"runtime": {
74+
"current_time": "2026-01-31T11:10:44.028830+00:00",
75+
"timezone": "UTC",
76+
"uptime_human": "0 hours, 0 minutes",
77+
"uptime_seconds": 7
78+
},
79+
"service": {
80+
"description": "DevOps course info service",
81+
"framework": "Flask",
82+
"name": "devops-info-service",
83+
"version": "1.0.0"
84+
},
85+
"system": {
86+
"architecture": "arm64",
87+
"cpu_count": 10,
88+
"hostname": "pepegas-MacBook-Air.local",
89+
"platform": "Darwin",
90+
"platform_version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:08:48 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8132",
91+
"python_version": "3.14.0"
92+
}
93+
}
3394
```
3495

35-
-**Docker image on Docker Hub:** https://hub.docker.com/r/pepegx/devops-info-service
36-
-**Status badge in README:** Added to the top of the app README.
96+
**Workflow run (CI):**
97+
https://github.com/pepegx/DevOps-Core-Course/actions/workflows/python-ci.yml
98+
99+
**Docker Hub image:**
100+
https://hub.docker.com/r/pepegx/devops-info-service
37101

38102
---
39103

40104
## 3. Best Practices Implemented
41105

42-
- **Dependency caching:** `actions/setup-python` with pip cache speeds up repeated installs.
43-
- **Job dependencies:** Docker build/push runs only after tests pass (`needs: test`).
44-
- **Conditional publishing:** Docker push runs only on `master` or `lab03` branch pushes.
45-
- **Workflow concurrency:** Cancels outdated runs on the same branch.
46-
- **Path filters:** CI runs only when `app_python/` changes.
47-
- **Snyk scanning:** Optional dependency vulnerability scan with `SNYK_TOKEN` secret.
106+
- **Matrix testing (3.11, 3.12):** catches version-specific issues early.
107+
- **Job dependency:** Docker build/push only runs after tests pass.
108+
- **Conditional deploy:** image push only on `master`/`lab03` pushes.
109+
- **Workflow concurrency:** cancels outdated runs to save CI minutes.
110+
- **Least-privilege permissions:** `contents: read` only.
111+
- **Dependency caching:** `actions/setup-python` cache enabled for pip.
112+
- **Docker layer cache:** `cache-from`/`cache-to` with Buildx to speed rebuilds.
48113

49-
**Caching speed improvement:** TODO — record cache hit/miss times from Actions.
114+
**Caching note:** The workflow is configured to cache pip dependencies; verify cache hit and time saved in the Actions logs after the first run.
50115

51-
**Snyk results:** TODO — record findings or note “no issues found.”
116+
**Snyk:** Integrated via `snyk/actions/python@v3` with `--severity-threshold=high`. Runs only on push when `SNYK_TOKEN` is present.
52117

53118
---
54119

55120
## 4. Key Decisions
56121

57-
- **Versioning Strategy:** CalVer aligns with frequent service updates without manual SemVer management.
58-
- **Docker Tags:** `${VERSION}` (CalVer) and `latest` for stable consumers.
59-
- **Workflow Triggers:** Focused on `app_python/` to avoid unnecessary CI in a monorepo.
60-
- **Test Coverage:** Core endpoints and error cases tested; system-specific values are asserted by structure, not exact host values.
122+
**Versioning Strategy:** CalVer (`YYYY.MM.<run_number>`). It matches a continuous delivery workflow and avoids manual tag management.
123+
**Docker Tags:** `pepegx/devops-info-service:<YYYY.MM.RUN>`, `<YYYY.MM>`, and `latest`.
124+
**Workflow Triggers:** Push/PR limited to `app_python/**` to avoid unnecessary CI runs in a monorepo.
125+
**Test Coverage:** Endpoint success cases and 404 error handler are covered; low‑value paths (e.g., startup logs) are not.
61126

62127
---
63128

64-
## 5. Challenges (Optional)
129+
## 5. Challenges & Solutions
65130

66-
- **None encountered** during local test setup.
131+
- **Background app startup for local testing:** Used `nohup env HOST=127.0.0.1 PORT=3000 ...` to run locally without blocking the shell.

app_python/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ gunicorn==21.2.0
77
# Development and Testing
88
pytest==7.4.3
99
pytest-flask==1.3.0
10-
ruff==0.6.9
10+
ruff==0.6.8

0 commit comments

Comments
 (0)