|
1 | | -# Lab 3 — Continuous Integration (CI/CD): Implementation Report |
| 1 | +# Lab 3 — CI/CD: Implementation Report |
2 | 2 |
|
3 | 3 | **Student:** Danil Fishchenko |
4 | 4 | **Date:** January 31, 2026 |
|
8 | 8 |
|
9 | 9 | ## 1. Overview |
10 | 10 |
|
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. |
15 | 15 |
|
16 | 16 | --- |
17 | 17 |
|
18 | 18 | ## 2. Workflow Evidence |
19 | 19 |
|
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 | +``` |
22 | 35 |
|
| 36 | +**Local lint (ruff):** |
23 | 37 | ``` |
24 | | -.... [100%] |
25 | | -4 passed in 0.07s |
| 38 | +All checks passed! |
26 | 39 | ``` |
27 | 40 |
|
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 | +``` |
29 | 51 |
|
| 52 | +`GET /` |
30 | 53 | ``` |
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 | +} |
33 | 94 | ``` |
34 | 95 |
|
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 |
37 | 101 |
|
38 | 102 | --- |
39 | 103 |
|
40 | 104 | ## 3. Best Practices Implemented |
41 | 105 |
|
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. |
48 | 113 |
|
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. |
50 | 115 |
|
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. |
52 | 117 |
|
53 | 118 | --- |
54 | 119 |
|
55 | 120 | ## 4. Key Decisions |
56 | 121 |
|
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. |
61 | 126 |
|
62 | 127 | --- |
63 | 128 |
|
64 | | -## 5. Challenges (Optional) |
| 129 | +## 5. Challenges & Solutions |
65 | 130 |
|
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. |
0 commit comments