Skip to content

feat(renovate): add Renovate dependency update configuration #159

feat(renovate): add Renovate dependency update configuration

feat(renovate): add Renovate dependency update configuration #159

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@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=0 \
-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 --ignore 51457 || {
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