@@ -13,93 +13,139 @@ jobs:
1313 PYTHONIOENCODING : " UTF-8"
1414
1515 steps :
16+ # ───────────────────────────────────────────────
17+ # 1. Checkout
18+ # ───────────────────────────────────────────────
1619 - name : 🔄 Check out code
1720 uses : actions/checkout@v4
1821 with :
1922 fetch-depth : 0
2023
21- - name : 🐍 Set up Python 3.11 with cache
22- id : setup-python
24+ # ───────────────────────────────────────────────
25+ # 2. Setup Python + cache pip
26+ # ───────────────────────────────────────────────
27+ - name : 🐍 Set up Python 3.11
2328 uses : actions/setup-python@v4
2429 with :
2530 python-version : " 3.11"
2631 cache : " pip"
2732
28- - name : 📦 Install dependencies and tooling
33+ # ───────────────────────────────────────────────
34+ # 3. Install deps & tooling
35+ # ───────────────────────────────────────────────
36+ - name : 📦 Install dependencies & tooling
2937 run : |
3038 echo "::group::Installing dependencies"
3139 python -m pip install --upgrade pip
3240 pip install -r requirements.txt -r requirements-dev.txt
3341 pip install bandit coverage
3442 echo "::endgroup::"
3543
44+ # ───────────────────────────────────────────────
45+ # 4. Run tests & produce coverage reports
46+ # ───────────────────────────────────────────────
3647 - name : 🧪 Run tests and collect coverage
3748 run : |
3849 echo "::group::pytest"
3950 coverage run --source=ChatApp,WebSocketChatApp -m pytest -q
4051 echo "::endgroup::"
52+ # XML report for downstream tools
4153 coverage xml -o coverage.xml
54+ # human-readable text report
4255 coverage report -m > coverage.txt
56+ echo "::group::Coverage report"
57+ cat coverage.txt
58+ echo "::endgroup::"
59+
60+ # ───────────────────────────────────────────────
61+ # 5. Extract coverage percentage
62+ # ───────────────────────────────────────────────
63+ - name : 📤 Extract coverage percent
64+ id : extract_coverage
65+ run : |
66+ # find the TOTAL line, strip '%' and take last column
67+ RATE=$(awk '/^TOTAL/ { gsub("%",""); print $NF }' coverage.txt)
68+ # default to 0.00 if empty
69+ if [ -z "$RATE" ]; then
70+ echo "WARNING: could not parse coverage; defaulting to 0.00"
71+ RATE="0.00"
72+ fi
73+ echo "extracted coverage=$RATE"
74+ echo "coverage=$RATE" >> $GITHUB_OUTPUT
4375
44- - name : ✅ Enforce minimum test coverage
76+ # ───────────────────────────────────────────────
77+ # 6. Enforce minimum coverage (fail build if below threshold)
78+ # ───────────────────────────────────────────────
79+ - name : ✅ Enforce minimum coverage
4580 run : |
46- RATE=$(awk '$1 == "TOTAL" {gsub("%",""); print $(NF)}' coverage.txt)
81+ RATE="${{ steps.extract_coverage.outputs.coverage }}"
4782 echo "📊 Total test coverage: $RATE%"
4883 THRESHOLD=70
49- if [ " $(echo "$RATE < $THRESHOLD" | bc -l)" -eq 1 ] ; then
50- echo "❌ Coverage is below ${THRESHOLD}% – failing the build."
84+ if (( $(echo "$RATE < $THRESHOLD" | bc -l) )) ; then
85+ echo "❌ Coverage $RATE% is below ${THRESHOLD}% — failing build."
5186 exit 1
5287 fi
5388
54- - name : 📤 Extract coverage percent for badge
55- id : get_coverage
56- if : github.ref == 'refs/heads/main'
57- run : |
58- RATE=$(awk '$1 == "TOTAL" {gsub("%",""); printf "%.2f", $(NF)}' coverage.txt)
59- echo "coverage=$RATE" >> $GITHUB_OUTPUT
60-
89+ # ───────────────────────────────────────────────
90+ # 7. Prepare badge directory
91+ # ───────────────────────────────────────────────
6192 - name : 🗂 Ensure badge folder exists
6293 if : github.ref == 'refs/heads/main'
6394 run : mkdir -p .github/badges
6495
96+ # ───────────────────────────────────────────────
97+ # 8. Generate coverage badge
98+ # ───────────────────────────────────────────────
6599 - name : 🏷️ Generate coverage badge
66100 if : github.ref == 'refs/heads/main'
67101 uses : emibcn/badge-action@v1
68102 with :
69103 label : coverage
70- status : " ${{ steps.get_coverage .outputs.coverage }}%"
104+ status : " ${{ steps.extract_coverage .outputs.coverage }}%"
71105 color : green
72106 path : .github/badges/coverage.svg
73107
74- - name : 📌 Commit coverage badge
108+ # ───────────────────────────────────────────────
109+ # 9. Commit coverage reports & badge
110+ # ───────────────────────────────────────────────
111+ - name : 📌 Commit coverage artefacts
75112 if : github.ref == 'refs/heads/main'
76113 uses : EndBug/add-and-commit@v9
77114 with :
78115 add : |
116+ coverage.xml
117+ coverage.txt
79118 .github/badges/coverage.svg
80- coverage-summary.json
81- message : " chore(ci): update coverage badge [skip ci]"
82-
119+ message : chore(ci): update coverage reports & badge [skip ci]
83120
84- - name : 🛡️ Bandit Static Analysis (SAST)
121+ # ───────────────────────────────────────────────
122+ # 10. Bandit static analysis
123+ # ───────────────────────────────────────────────
124+ - name : 🛡️ Run Bandit
85125 run : |
86126 echo "::group::Bandit scan"
87127 bandit -r websocket_chatapp -lll -f json -o bandit.json
88128 echo "::endgroup::"
89129
130+ # ───────────────────────────────────────────────
131+ # 11. Build Docker image (Buildx + cache)
132+ # ───────────────────────────────────────────────
90133 - name : 🐳 Set up Docker Buildx
91134 uses : docker/setup-buildx-action@v2
92135
93- - name : 🏗️ Build Docker Image
136+ - name : 🏗️ Build Docker image
94137 uses : docker/build-push-action@v5
95138 with :
96139 context : .
97140 tags : chatapp:test
98141 load : true
99142 cache-from : type=gha
100- cache-to : type=gha,mode=max
143+ cache-to : type=gha,mode=max
101144
102- - name : 🔍 Scan Docker Image (Trivy)
145+ # ───────────────────────────────────────────────
146+ # 12. Trivy vulnerability scan
147+ # ───────────────────────────────────────────────
148+ - name : 🔍 Scan image with Trivy
103149 id : trivy
104150 uses : aquasecurity/trivy-action@0.31.0
105151 with :
@@ -109,29 +155,35 @@ jobs:
109155 ignore-unfixed : true
110156 exit-code : 0
111157
112- - name : 🚨 Fail on High CVSS (Trivy)
158+ - name : 🚨 Fail on high CVSS
113159 run : |
114160 echo "::group::Evaluating Trivy results"
115161 if jq '.Results[].Vulnerabilities[] | select((.CVSS.nvd.V3Score // 0) > 7)' trivy-image.json | grep -q .; then
116- echo ' ❌ High/critical CVEs detected – failing build.'
162+ echo " ❌ High/critical CVEs (CVSS > 7) — failing build."
117163 exit 1
118164 fi
119165 echo "::endgroup::"
120166
167+ # ───────────────────────────────────────────────
168+ # 13. Secret scanning with Gitleaks
169+ # ───────────────────────────────────────────────
121170 - name : 🔐 Install Gitleaks
122171 run : |
123172 curl -sSL https://github.com/gitleaks/gitleaks/releases/download/v8.24.3/gitleaks_8.24.3_linux_x64.tar.gz -o gitleaks.tar.gz
124173 tar -xzf gitleaks.tar.gz gitleaks
125174 sudo mv gitleaks /usr/local/bin/
126175
127- - name : 🔍 Secret scan with Gitleaks
176+ - name : 🔍 Run Gitleaks
128177 run : |
129- echo "::group::Gitleaks"
178+ echo "::group::Gitleaks scan "
130179 gitleaks detect --source . --no-git -c .gitleaks.toml \
131180 --report-format json --report-path gitleaks.json
132181 echo "::endgroup::"
133182
134- - name : ☁️ Upload Security Artifacts
183+ # ───────────────────────────────────────────────
184+ # 14. Upload security artefacts
185+ # ───────────────────────────────────────────────
186+ - name : ☁️ Upload security artefacts
135187 if : always()
136188 uses : actions/upload-artifact@v4
137189 with :
@@ -141,3 +193,4 @@ jobs:
141193 trivy-image.json
142194 gitleaks.json
143195 coverage.xml
196+ coverage.txt
0 commit comments