Skip to content

fix: improve generated project quality and fix template issues #2

fix: improve generated project quality and fix template issues

fix: improve generated project quality and fix template issues #2

Workflow file for this run

# 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@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install cookiecutter black ruff mypy pytest pytest-cov bandit safety
- 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
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@v4
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
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
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]
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download coverage reports
uses: actions/download-artifact@v4
with:
name: coverage-reports
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- name: Wait for Quality Gate
uses: sonarsource/sonarqube-quality-gate-action@master
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=williaby_cookiecutter-python-template"
echo "::endgroup::"
- name: Block PR if quality gate fails
if: steps.quality-gate.outputs.quality-gate-status == 'FAILED'
run: |
echo "::error::❌ Quality Gate FAILED"
echo ""
echo "SonarQube quality gate did not pass. Please fix the issues:"
echo "- Review issues at: https://sonarcloud.io/project/overview?id=williaby_cookiecutter-python-template"
echo "- Fix all BLOCKER and CRITICAL issues"
echo "- Ensure test coverage ≥ 80%"
echo "- Reduce code duplication"
exit 1
- 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: ${{ steps.check-tags.outputs.rad_tags > 0 && '❌ FOUND' || '✅ None' }}
- Status: ${{ steps.check-tags.outputs.rad_tags > 0 && '**BLOCKED**' || '**PASSED**' }}
### Layer 2: LLM Development Debt
- LLM debt tags: ${{ steps.check-tags.outputs.llm_tags > 0 && '⚠️ FOUND' || '✅ None' }}
- Status: ${{ steps.check-tags.outputs.llm_tags > 0 && '**WARNING**' || '**PASSED**' }}
### Layer 3: Automated Code Quality (SonarQube)
- Quality Gate: ${{ steps.quality-gate.outputs.quality-gate-status }}
- Status: ${{ steps.quality-gate.outputs.quality-gate-status == 'PASSED' && '**PASSED**' || '**FAILED**' }}
### Overall Status
${{ (steps.check-tags.outputs.rad_tags > 0 || steps.quality-gate.outputs.quality-gate-status == 'FAILED') && '❌ **PR BLOCKED** - Fix issues 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@v4
- name: Set up Python
uses: actions/setup-python@v5
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_project/
echo "::endgroup::"
- name: Validate generated project structure
run: |
echo "::group::Structure Validation"
cd /tmp/my_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_project
if grep -q "LLM Development Governance" CLAUDE.md; then
echo "✓ LLM Governance section found in CLAUDE.md"
else
echo "::error::LLM Governance section missing from generated CLAUDE.md"
exit 1
fi
if grep -q "#LLM-MOCK\|#LLM-PLACEHOLDER\|#CRITICAL" CLAUDE.md; then
echo "✓ LLM governance tags documented"
else
echo "::warning::LLM governance tags not fully documented"
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