fix(hooks): add comprehensive cruft skip patterns for binary files #91
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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=williaby_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=williaby_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 |