@@ -2,95 +2,156 @@ name: CI
22
33on :
44 push :
5- branches : ["main"]
5+ branches : [ "main" ]
66 pull_request :
7- branches : ["main"]
7+ branches : [ "main" ]
88
99jobs :
1010 test-and-scan :
1111 runs-on : ubuntu-latest
12+ env :
13+ PYTHONIOENCODING : " UTF-8"
1214
1315 steps :
14- - uses : actions/checkout@v3
15-
16- - name : Set up Python
17- uses : actions/setup-python@v3
16+ # ────────────────────────────────
17+ # 1. Check out repository
18+ # ────────────────────────────────
19+ - name : Check out code
20+ uses : actions/checkout@v4
21+ with :
22+ fetch-depth : 0 # full history for accurate lint / blame if needed
23+
24+ # ────────────────────────────────
25+ # 2. Set up Python with cache
26+ # ────────────────────────────────
27+ - name : Set up Python 3.11
28+ id : setup-python
29+ uses : actions/setup-python@v4
1830 with :
19- python-version : ' 3.11'
31+ python-version : " 3.11"
32+ cache : " pip"
2033
21- - name : Install dependencies
34+ # ────────────────────────────────
35+ # 3. Install dependencies
36+ # ────────────────────────────────
37+ - name : Install project and security tooling
2238 run : |
39+ echo "::group::Installing dependencies"
2340 python -m pip install --upgrade pip
2441 pip install -r requirements.txt -r requirements-dev.txt
2542 pip install bandit coverage
43+ echo "::endgroup::"
2644
27- - name : Run tests with coverage
45+ # ────────────────────────────────
46+ # 4. Run tests + coverage
47+ # ────────────────────────────────
48+ - name : Execute tests and collect coverage
2849 run : |
29- coverage run --source=ChatApp,WebSocketChatApp -m pytest
30- coverage xml
50+ echo "::group::pytest"
51+ coverage run --source=ChatApp,WebSocketChatApp -m pytest -q
52+ echo "::endgroup::"
53+ coverage xml -o coverage.xml
3154 coverage report -m > coverage.txt
3255
33- - name : Generate coverage-summary.json
34- if : github.ref == 'refs/heads/main'
56+ # ────────────────────────────────
57+ # 5. Fail if coverage < 70 %
58+ # (change the threshold as needed)
59+ # ────────────────────────────────
60+ - name : Enforce minimum coverage
3561 run : |
36- RATE=$(awk '/TOTAL/ {gsub("%",""); printf "%.2f", $4}' coverage.txt)
37- if [ -z "$RATE" ]; then
38- echo "Coverage extraction failed. Setting line_rate_pct to 0.00"
39- RATE=0.00
62+ RATE=$(awk '$1 == "TOTAL" {gsub("%",""); print $(NF)}' coverage.txt)
63+ echo "Total coverage: $RATE %"
64+ THRESHOLD=70
65+ if [ "$(echo "$RATE < $THRESHOLD" | bc -l)" -eq 1 ]; then
66+ echo "Coverage is below ${THRESHOLD}% – failing the build."
67+ exit 1
4068 fi
41- echo "{ \"line_rate_pct\": $RATE }" > coverage-summary.json
4269
43- - name : Commit coverage reports
70+ # ────────────────────────────────
71+ # 6. Generate coverage-summary.json (only on main)
72+ # ────────────────────────────────
73+ - name : Generate JSON summary
74+ if : github.ref == 'refs/heads/main'
75+ run : |
76+ RATE=$(awk '$1 == "TOTAL" {gsub("%",""); printf "%.2f", $(NF)}' coverage.txt)
77+ printf '{ "line_rate_pct": %.2f }\n' "$RATE" > coverage-summary.json
78+ cat coverage-summary.json
79+
80+ - name : Commit coverage artefacts
4481 if : github.ref == 'refs/heads/main'
4582 uses : EndBug/add-and-commit@v9
4683 with :
4784 add : |
4885 coverage.xml
4986 coverage-summary.json
50- message : Update coverage reports [skip ci]
51-
52- - name : Bandit Scan
53- run : bandit -r websocket_chatapp -lll -f json -o bandit.json
87+ message : chore(ci): update coverage artefacts [skip ci]
5488
55- - name : Set up Docker Buildx
89+ # ────────────────────────────────
90+ # 7. Bandit static analysis
91+ # ────────────────────────────────
92+ - name : Run Bandit (SAST)
93+ run : |
94+ echo "::group::Bandit scan"
95+ bandit -r websocket_chatapp -lll -f json -o bandit.json
96+ echo "::endgroup::"
97+
98+ # ────────────────────────────────
99+ # 8. Build Docker image (Buildx + cache)
100+ # ────────────────────────────────
101+ - name : Set up Buildx
56102 uses : docker/setup-buildx-action@v2
57103
58- - name : Build Docker image
104+ - name : Build application image
59105 uses : docker/build-push-action@v5
60106 with :
61107 context : .
62108 tags : chatapp:test
63109 load : true
64110 cache-from : type=gha
65- cache-to : type=gha,mode=max
111+ cache-to : type=gha,mode=max
66112
67- - name : Trivy Image Scan
113+ # ────────────────────────────────
114+ # 9. Trivy image vulnerability scan
115+ # ────────────────────────────────
116+ - name : Scan image with Trivy
68117 id : trivy
69118 uses : aquasecurity/trivy-action@0.31.0
70119 with :
71120 image-ref : chatapp:test
72121 format : json
73122 output : trivy-image.json
74- ignore-unfixed : true
75- exit-code : 0
123+ ignore-unfixed : true # suppress non-fixed CVEs
124+ exit-code : 0 # allow post-processing
76125
77- - name : Fail on CVSS > 7
126+ - name : Fail pipeline if CVSS high/critical
78127 run : |
128+ echo "::group::Evaluating Trivy results"
79129 if jq '.Results[].Vulnerabilities[] | select((.CVSS.nvd.V3Score // 0) > 7)' trivy-image.json | grep -q .; then
80- echo 'Vulnerabilities with CVSS > 7 found '
130+ echo 'High/critical CVEs detected ( CVSS > 7) – failing build. '
81131 exit 1
82132 fi
133+ echo "::endgroup::"
83134
84- - name : Install gitleaks
135+ # ────────────────────────────────
136+ # 10. Secrets detection with Gitleaks
137+ # ────────────────────────────────
138+ - name : Install Gitleaks
85139 run : |
86140 curl -sSL https://github.com/gitleaks/gitleaks/releases/download/v8.24.3/gitleaks_8.24.3_linux_x64.tar.gz -o gitleaks.tar.gz
87141 tar -xzf gitleaks.tar.gz gitleaks
88142 sudo mv gitleaks /usr/local/bin/
89143
90- - name : Scan secrets with gitleaks
91- run : gitleaks detect --source . --no-git -c .gitleaks.toml --report-format=json --report-path=gitleaks.json
92-
93- - name : Upload artifacts
144+ - name : Scan repository for secrets
145+ run : |
146+ echo "::group::Gitleaks"
147+ gitleaks detect --source . --no-git -c .gitleaks.toml \
148+ --report-format json --report-path gitleaks.json
149+ echo "::endgroup::"
150+
151+ # ────────────────────────────────
152+ # 11. Upload artefacts
153+ # ────────────────────────────────
154+ - name : Publish security artefacts
94155 if : always()
95156 uses : actions/upload-artifact@v4
96157 with :
0 commit comments