-
-
Notifications
You must be signed in to change notification settings - Fork 0
423 lines (358 loc) · 16.3 KB
/
Copy pathci.yml
File metadata and controls
423 lines (358 loc) · 16.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# CI/CD Pipeline with LLM Governance Integration
# Comprehensive code quality, security, and LLM debt detection
#
# Features:
# - Standard quality checks (tests, linting, type checking)
# - SonarQube quality gate enforcement
# - LLM anti-pattern detection
# - Assumption tag verification
# - Security scanning
#
# Three-Layer Governance:
# Layer 1: Production Runtime Risks (RAD tags)
# Layer 2: LLM Development Debt (LLM tags)
# Layer 3: Automated Code Quality (SonarQube)
name: CI
on:
push:
branches: [main, master, develop]
pull_request:
types: [opened, synchronize, reopened]
branches: [main, master, develop]
workflow_dispatch:
permissions:
contents: read
pull-requests: write
checks: write
env:
PYTHON_VERSION: '3.12'
jobs:
# ============================================================================
# Job 1: Standard Quality Checks
# ============================================================================
quality-checks:
name: Code Quality Checks
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.2.1
with:
enable-cache: true
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install dependencies
run: |
uv sync --all-extras
pip install cookiecutter pre-commit yamllint tomli ruff basedpyright black bandit safety interrogate codespell
- name: Install Node.js tools
run: |
npm install -g markdownlint-cli
- name: Run Black formatter check
run: |
echo "::group::Black Formatting"
black --check --diff hooks/ || {
echo "::error::Code formatting issues detected. Run 'black hooks/' to fix."
exit 1
}
echo "::endgroup::"
- name: Run Ruff linter
run: |
echo "::group::Ruff Linting"
ruff check hooks/ --output-format=github
echo "::endgroup::"
- name: Run MyPy type checker
run: |
echo "::group::MyPy Type Checking"
mypy hooks/ --ignore-missing-imports --no-error-summary || {
echo "::warning::Type checking found issues"
}
echo "::endgroup::"
- name: Run tests with coverage
run: |
echo "::group::Test Execution"
if [ -d "tests" ]; then
uv run pytest \
--cov=hooks \
--cov-report=xml:coverage.xml \
--cov-report=term-missing \
--cov-report=html \
--cov-branch \
--cov-fail-under=80 \
-v
else
echo "::warning::No tests directory found"
echo '<?xml version="1.0" ?><coverage version="1.0"></coverage>' > coverage.xml
fi
echo "::endgroup::"
- name: Security scan with Bandit
run: |
echo "::group::Bandit Security Scan"
bandit -r hooks/ -f json -o bandit-report.json || {
echo "::warning::Security issues detected"
cat bandit-report.json
}
echo "::endgroup::"
- name: Dependency vulnerability scan
run: |
echo "::group::Safety Dependency Scan"
pip freeze | safety check --stdin || {
echo "::warning::Vulnerable dependencies detected"
}
echo "::endgroup::"
- name: Upload coverage
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
with:
name: coverage-reports
path: |
coverage.xml
htmlcov/
retention-days: 7
# ============================================================================
# Job 2: LLM Governance Checks
# ============================================================================
llm-governance:
name: LLM Governance & Assumption Verification
runs-on: ubuntu-latest
timeout-minutes: 15
outputs:
rad_tags: ${{ steps.check-tags.outputs.rad_tags }}
llm_tags: ${{ steps.check-tags.outputs.llm_tags }}
total_tags: ${{ steps.check-tags.outputs.total_tags }}
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Check for unverified assumption tags
id: check-tags
run: |
echo "::group::Scanning for Unverified Assumption Tags"
# Check for RAD tags (Layer 1: Production Runtime Risks)
CRITICAL_TAGS=$(grep -r "#CRITICAL" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
ASSUME_TAGS=$(grep -r "#ASSUME" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
EDGE_TAGS=$(grep -r "#EDGE" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
# Check for LLM debt tags (Layer 2: LLM Development Debt)
LLM_MOCK=$(grep -r "#LLM-MOCK" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
LLM_PLACEHOLDER=$(grep -r "#LLM-PLACEHOLDER" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
LLM_LOGIC=$(grep -r "#LLM-LOGIC" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
LLM_SCAFFOLD=$(grep -r "#LLM-SCAFFOLD" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
LLM_INFERRED=$(grep -r "#LLM-INFERRED" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
LLM_TEST_FIRST=$(grep -r "#LLM-TEST-FIRST" --include="*.py" hooks/ 2>/dev/null | wc -l || echo "0")
TOTAL_RAD=$((CRITICAL_TAGS + ASSUME_TAGS))
TOTAL_LLM=$((LLM_MOCK + LLM_PLACEHOLDER + LLM_LOGIC + LLM_SCAFFOLD + LLM_INFERRED + LLM_TEST_FIRST))
TOTAL_TAGS=$((TOTAL_RAD + TOTAL_LLM))
echo "rad_tags=$TOTAL_RAD" >> $GITHUB_OUTPUT
echo "llm_tags=$TOTAL_LLM" >> $GITHUB_OUTPUT
echo "total_tags=$TOTAL_TAGS" >> $GITHUB_OUTPUT
echo "### Assumption Tag Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Layer 1 (Production Runtime Risks):**" >> $GITHUB_STEP_SUMMARY
echo "- #CRITICAL tags: $CRITICAL_TAGS" >> $GITHUB_STEP_SUMMARY
echo "- #ASSUME tags: $ASSUME_TAGS" >> $GITHUB_STEP_SUMMARY
echo "- #EDGE tags: $EDGE_TAGS" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Layer 2 (LLM Development Debt):**" >> $GITHUB_STEP_SUMMARY
echo "- #LLM-MOCK: $LLM_MOCK" >> $GITHUB_STEP_SUMMARY
echo "- #LLM-PLACEHOLDER: $LLM_PLACEHOLDER" >> $GITHUB_STEP_SUMMARY
echo "- #LLM-LOGIC: $LLM_LOGIC" >> $GITHUB_STEP_SUMMARY
echo "- #LLM-SCAFFOLD: $LLM_SCAFFOLD" >> $GITHUB_STEP_SUMMARY
echo "- #LLM-INFERRED: $LLM_INFERRED" >> $GITHUB_STEP_SUMMARY
echo "- #LLM-TEST-FIRST: $LLM_TEST_FIRST" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Total unverified tags: $TOTAL_TAGS**" >> $GITHUB_STEP_SUMMARY
if [ "$TOTAL_RAD" -gt 0 ]; then
echo "::error::Found $TOTAL_RAD unverified production risk tags (#CRITICAL, #ASSUME)"
fi
if [ "$TOTAL_LLM" -gt 0 ]; then
echo "::warning::Found $TOTAL_LLM unverified LLM debt tags"
fi
echo "::endgroup::"
- name: Block PR if critical tags found
if: steps.check-tags.outputs.rad_tags > 0
run: |
echo "::error::❌ PR blocked: Found ${{ steps.check-tags.outputs.rad_tags }} unverified production risk tags"
echo ""
echo "Please verify and remove these tags before merging:"
grep -rn "#CRITICAL\|#ASSUME" --include="*.py" hooks/ || true
echo ""
echo "See CLAUDE.md for verification workflow"
exit 1
- name: Detect LLM anti-patterns
run: |
echo "::group::LLM Anti-Pattern Detection"
# Hardcoded localhost/URLs
echo "Checking for hardcoded localhost/URLs..."
if grep -rE "(localhost|127\.0\.0\.1|http://|https://)" --include="*.py" hooks/ | \
grep -v "#LLM-PLACEHOLDER" | grep -v "^[[:space:]]*#"; then
echo "::warning::Found hardcoded URLs without #LLM-PLACEHOLDER tag"
fi
# Hardcoded secrets patterns
echo "Checking for potential hardcoded secrets..."
if grep -rE "(api_key|password|secret|token)\s*=\s*[\"'][^\"']+[\"']" --include="*.py" hooks/ | \
grep -v "#LLM-PLACEHOLDER" | grep -v "^[[:space:]]*#"; then
echo "::error::Potential hardcoded secret detected"
exit 1
fi
# Magic numbers
echo "Checking for magic numbers..."
MAGIC_NUMBERS=$(grep -rE "\b[0-9]{2,}\b" --include="*.py" hooks/ | \
grep -v "#LLM-PLACEHOLDER" | \
grep -v "^[[:space:]]*#" | \
wc -l || echo "0")
if [ "$MAGIC_NUMBERS" -gt 10 ]; then
echo "::warning::Found $MAGIC_NUMBERS potential magic numbers without constants"
fi
echo "::endgroup::"
# ============================================================================
# Job 3: SonarQube Quality Gate (Enhanced with LLM Governance)
# ============================================================================
sonarqube-quality-gate:
name: SonarQube Quality Gate
runs-on: ubuntu-latest
needs: [quality-checks, llm-governance]
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Download coverage reports
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: coverage-reports
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Wait for Quality Gate
uses: sonarsource/sonarqube-quality-gate-action@master
continue-on-error: true
timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
id: quality-gate
- name: Quality Gate Result
run: |
echo "::group::Quality Gate Status"
echo "Quality Gate Status: ${{ steps.quality-gate.outputs.quality-gate-status }}"
echo "View detailed results: https://sonarcloud.io/project/overview?id=ByronWilliamsCPA_cookiecutter-python-template"
echo "::endgroup::"
- name: Quality Gate Status (Non-Blocking)
if: steps.quality-gate.outputs.quality-gate-status == 'FAILED'
run: |
echo "::warning::⚠️ Quality Gate FAILED (non-blocking for template repo)"
echo ""
echo "SonarQube quality gate did not pass. Consider reviewing:"
echo "- Review issues at: https://sonarcloud.io/project/overview?id=ByronWilliamsCPA_cookiecutter-python-template"
echo "- Note: Duplicate code warnings are expected in cookiecutter templates"
echo "- SonarCloud analysis primarily for main branch monitoring"
- name: Generate unified governance report
if: always()
run: |
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
## 🎯 Three-Layer Governance Report
### Layer 1: Production Runtime Risks (RAD)
- #CRITICAL tags: ${{ needs.llm-governance.outputs.rad_tags > 0 && '❌ FOUND' || '✅ None' }}
- Status: ${{ needs.llm-governance.outputs.rad_tags > 0 && '**BLOCKED**' || '**PASSED**' }}
### Layer 2: LLM Development Debt
- LLM debt tags: ${{ needs.llm-governance.outputs.llm_tags > 0 && '⚠️ FOUND' || '✅ None' }}
- Status: ${{ needs.llm-governance.outputs.llm_tags > 0 && '**WARNING**' || '**PASSED**' }}
### Layer 3: Automated Code Quality (SonarQube)
- Quality Gate: ${{ steps.quality-gate.outputs.quality-gate-status || 'N/A' }}
- Status: ${{ steps.quality-gate.outputs.quality-gate-status == 'PASSED' && '**PASSED**' || '**INFORMATIONAL** (non-blocking)' }}
### Overall Status
${{ needs.llm-governance.outputs.rad_tags > 0 && '❌ **PR BLOCKED** - Fix RAD tags before merging' || '✅ **READY TO MERGE**' }}
---
**Next Steps:**
- Fix all CRITICAL issues
- Verify and remove assumption tags
- Ensure quality gate passes
- Review SonarQube findings
EOF
# ============================================================================
# Job 4: Template Validation
# ============================================================================
validate-template:
name: Validate Cookiecutter Template
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Python
uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install cookiecutter
run: |
pip install cookiecutter
- name: Generate test project
run: |
echo "::group::Template Generation Test"
cd /tmp
cookiecutter --no-input /home/runner/work/cookiecutter-python-template/cookiecutter-python-template
echo "✓ Template generated successfully"
ls -la my_python_project/
echo "::endgroup::"
- name: Validate generated project structure
run: |
echo "::group::Structure Validation"
cd /tmp/my_python_project
# Check key files exist
test -f README.md || { echo "::error::README.md missing"; exit 1; }
test -f pyproject.toml || { echo "::error::pyproject.toml missing"; exit 1; }
test -f CLAUDE.md || { echo "::error::CLAUDE.md missing"; exit 1; }
test -d src || { echo "::error::src/ directory missing"; exit 1; }
test -d tests || { echo "::error::tests/ directory missing"; exit 1; }
echo "✓ All key files and directories present"
echo "::endgroup::"
- name: Check for LLM governance in generated project
run: |
echo "::group::LLM Governance Validation"
cd /tmp/my_python_project
# Check if CLAUDE.md exists
if [ ! -f CLAUDE.md ]; then
echo "::error::CLAUDE.md missing from generated project"
exit 1
fi
echo "✓ CLAUDE.md exists in generated project"
# Optional: Check if LLM governance is documented (don't fail if missing)
if grep -q "#LLM-MOCK\|#LLM-PLACEHOLDER\|#CRITICAL" CLAUDE.md 2>/dev/null; then
echo "✓ LLM governance tags found in documentation"
else
echo "ℹ LLM governance tags documentation is optional"
fi
echo "::endgroup::"
# ============================================================================
# Summary Job
# ============================================================================
ci-summary:
name: CI Summary
runs-on: ubuntu-latest
needs: [quality-checks, llm-governance, sonarqube-quality-gate, validate-template]
if: always()
steps:
- name: Generate final summary
run: |
echo "### 🚀 CI Pipeline Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "All governance layers checked:" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Code Quality Checks" >> $GITHUB_STEP_SUMMARY
echo "- ✅ LLM Governance Validation" >> $GITHUB_STEP_SUMMARY
echo "- ✅ SonarQube Quality Gate" >> $GITHUB_STEP_SUMMARY
echo "- ✅ Template Validation" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Status: ${{ job.status }}**" >> $GITHUB_STEP_SUMMARY