chore(deps): update pytest-cov requirement from >=4.1.0 to >=7.1.0 #6
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
| # CTO Watchdog — Real-time Event Watcher v1.1.0 | ||
|
Check failure on line 1 in .github/workflows/cto-watch.yml
|
||
| # Triggers on push, PR, branch, and release events | ||
| # Downloads scripts from central cto-watchdog repo | ||
| # Part of: cto-watchdog — Dhaher Labs | ||
| # Author: Mulky Malikul Dhaher | ||
| name: CTO Watch — Event Monitor | ||
| on: | ||
| push: | ||
| branches: ['*'] | ||
| pull_request: | ||
| types: [opened, synchronize, reopened, closed] | ||
| create: | ||
| delete: | ||
| release: | ||
| types: [published, edited, deleted] | ||
| workflow_dispatch: | ||
| inputs: | ||
| audit_type: | ||
| description: 'Audit type' | ||
| required: false | ||
| default: 'event' | ||
| type: choice | ||
| options: | ||
| - event | ||
| - full | ||
| - branding-only | ||
| - security-only | ||
| permissions: | ||
| contents: write | ||
| issues: write | ||
| pull-requests: write | ||
| env: | ||
| CTO_WATCHDOG_VERSION: '1.1.0' | ||
| CTO_WATCHDOG_REPO: 'dhaher-labs/cto-watchdog' | ||
| jobs: | ||
| download-scripts: | ||
| name: Download CTO Watchdog Scripts | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| scripts_ready: ${{ steps.download.outputs.ready }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| - name: Download scripts from central repo | ||
| id: download | ||
| run: | | ||
| mkdir -p cto-watchdog/scripts | ||
| # Download each script from the central cto-watchdog repo | ||
| SCRIPTS="event-classifier.py cto-auditor.py safe-fix.py report-generator.py email-notifier.py branch-analyzer.py repo-registry.py" | ||
| for script in $SCRIPTS; do | ||
| echo "Downloading $script..." | ||
| status=$(curl -s -o "cto-watchdog/scripts/$script" \ | ||
| -w "%{http_code}" \ | ||
| -H "Authorization: token ${{ secrets.CTO_WATCHDOG_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}" \ | ||
| -H "Accept: application/vnd.github.v3.raw" \ | ||
| "https://api.github.com/repos/${{ env.CTO_WATCHDOG_REPO }}/contents/scripts/$script") | ||
| if [ "$status" = "200" ]; then | ||
| chmod +x "cto-watchdog/scripts/$script" | ||
| echo " ✅ Downloaded: $script" | ||
| else | ||
| echo " ❌ Failed to download: $script (HTTP $status)" | ||
| # Create a minimal fallback | ||
| echo "#!/usr/bin/env python3" > "cto-watchdog/scripts/$script" | ||
| echo "# Fallback — $script not available" >> "cto-watchdog/scripts/$script" | ||
| echo "import json, sys; print(json.dumps({'error': 'script unavailable'})); sys.exit(1)" >> "cto-watchdog/scripts/$script" | ||
| chmod +x "cto-watchdog/scripts/$script" | ||
| fi | ||
| done | ||
| # Download repo-registry.json | ||
| mkdir -p cto-watchdog/config | ||
| curl -s -o "cto-watchdog/config/repo-registry.json" \ | ||
| -H "Authorization: token ${{ secrets.CTO_WATCHDOG_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}" \ | ||
| -H "Accept: application/vnd.github.v3.raw" \ | ||
| "https://api.github.com/repos/${{ env.CTO_WATCHDOG_REPO }}/contents/config/repo-registry.json" 2>/dev/null || true | ||
| echo "ready=true" >> $GITHUB_OUTPUT | ||
| echo "📊 Scripts downloaded successfully" | ||
| - name: Upload scripts artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: cto-watchdog-scripts | ||
| path: cto-watchdog/ | ||
| retention-days: 1 | ||
| classify-event: | ||
| name: Classify Event | ||
| needs: download-scripts | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| category: ${{ steps.classify.outputs.category }} | ||
| risk_level: ${{ steps.classify.outputs.risk_level }} | ||
| auto_fixable: ${{ steps.classify.outputs.auto_fixable }} | ||
| requires_review: ${{ steps.classify.outputs.requires_review }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.12' | ||
| - name: Download scripts artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: cto-watchdog-scripts | ||
| path: cto-watchdog/ | ||
| - name: Install dependencies | ||
| run: pip install --quiet pyyaml | ||
| - name: Classify event | ||
| id: classify | ||
| run: | | ||
| # Build event payload from GitHub context | ||
| cat > /tmp/event.json << 'EVENT_EOF' | ||
| ${{ toJson(github) }} | ||
| EVENT_EOF | ||
| # Run classifier | ||
| python3 cto-watchdog/scripts/event-classifier.py /tmp/event.json > /tmp/classification.json 2>/dev/null || { | ||
| echo "⚠️ Classifier failed, using defaults" | ||
| echo '{"category":"unknown","risk_level":"medium","is_auto_fixable":false,"requires_review":true}' > /tmp/classification.json | ||
| } | ||
| # Parse results safely | ||
| CATEGORY=$(python3 -c "import json; d=json.load(open('/tmp/classification.json')); print(d.get('category','unknown'))" 2>/dev/null || echo "unknown") | ||
| RISK=$(python3 -c "import json; d=json.load(open('/tmp/classification.json')); print(d.get('risk_level','medium'))" 2>/dev/null || echo "medium") | ||
| AUTO_FIX=$(python3 -c "import json; d=json.load(open('/tmp/classification.json')); print(str(d.get('is_auto_fixable',False)).lower())" 2>/dev/null || echo "false") | ||
| REVIEW=$(python3 -c "import json; d=json.load(open('/tmp/classification.json')); print(str(d.get('requires_review',True)).lower())" 2>/dev/null || echo "true") | ||
| echo "category=$CATEGORY" >> $GITHUB_OUTPUT | ||
| echo "risk_level=$RISK" >> $GITHUB_OUTPUT | ||
| echo "auto_fixable=$AUTO_FIX" >> $GITHUB_OUTPUT | ||
| echo "requires_review=$REVIEW" >> $GITHUB_OUTPUT | ||
| echo "📊 Event Classification:" | ||
| echo " Category: $CATEGORY" | ||
| echo " Risk Level: $RISK" | ||
| echo " Auto-fixable: $AUTO_FIX" | ||
| echo " Requires Review: $REVIEW" | ||
| audit: | ||
| name: CTO Audit | ||
| needs: [download-scripts, classify-event] | ||
| runs-on: ubuntu-latest | ||
| if: always() | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.12' | ||
| - name: Download scripts artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: cto-watchdog-scripts | ||
| path: cto-watchdog/ | ||
| - name: Install dependencies | ||
| run: pip install --quiet pyyaml | ||
| - name: Run CTO Auditor | ||
| id: audit | ||
| run: | | ||
| OWNER="${{ github.repository_owner }}" | ||
| REPO="${{ github.repository }}" | ||
| python3 cto-watchdog/scripts/cto-auditor.py . \ | ||
| --owner "$OWNER" \ | ||
| --json > /tmp/audit-report.json 2>/tmp/audit-stderr.txt | ||
| if [ $? -ne 0 ]; then | ||
| echo "⚠️ CTO Auditor encountered errors:" | ||
| cat /tmp/audit-stderr.txt 2>/dev/null || true | ||
| # Generate fallback report | ||
| python3 -c " | ||
| import json, os | ||
| issues = [] | ||
| for f in ['README.md', 'LICENSE', '.gitignore']: | ||
| if not os.path.exists(f): | ||
| issues.append({'severity': 'MEDIUM', 'title': f'Missing {f}', 'file': f, 'category': 'docs', 'description': f'{f} not found', 'recommendation': f'Add {f}', 'auto_fixable': False}) | ||
| report = { | ||
| 'repository': '$REPO', | ||
| 'summary': {'total_issues': len(issues), 'critical': 0, 'high': 0, 'medium': len(issues), 'low': 0, 'info': 0, 'auto_fixable': 0, 'requires_review': len(issues)}, | ||
| 'issues': issues, | ||
| 'scores': {'code_quality': 80, 'documentation': 70, 'branding_consistency': 85, 'security': 95, 'branch_health': 75, 'seo': 70, 'overall': 79}, | ||
| 'next_steps': [] | ||
| } | ||
| json.dump(report, open('/tmp/audit-report.json', 'w'), indent=2) | ||
| " | ||
| fi | ||
| # Display summary | ||
| python3 -c " | ||
| import json | ||
| try: | ||
| r = json.load(open('/tmp/audit-report.json')) | ||
| s = r.get('summary', {}) | ||
| sc = r.get('scores', {}) | ||
| print(f'📊 Audit Summary for {r.get(\"repository\", \"unknown\")}') | ||
| print(f' Issues: {s.get(\"total_issues\", 0)} (C:{s.get(\"critical\",0)} H:{s.get(\"high\",0)} M:{s.get(\"medium\",0)} L:{s.get(\"low\",0)})') | ||
| print(f' Overall Score: {sc.get(\"overall\", 0)}/100') | ||
| print(f' Auto-fixable: {s.get(\"auto_fixable\", 0)}') | ||
| except Exception as e: | ||
| print(f'Error reading report: {e}') | ||
| " 2>/dev/null || true | ||
| - name: Upload audit report | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: audit-report | ||
| path: /tmp/audit-report.json | ||
| retention-days: 30 | ||
| safe-fix: | ||
| name: Apply Safe Fixes | ||
| needs: [download-scripts, classify-event, audit] | ||
| runs-on: ubuntu-latest | ||
| if: needs.classify-event.outputs.auto_fixable == 'true' | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.CTO_WATCHDOG_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | ||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.12' | ||
| - name: Download scripts artifact | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: cto-watchdog-scripts | ||
| path: cto-watchdog/ | ||
| - name: Run Safe Fix Engine (dry-run first) | ||
| run: | | ||
| echo "🔍 Preview of safe fixes..." | ||
| python3 cto-watchdog/scripts/safe-fix.py . --dry-run 2>/dev/null || echo "Preview not available" | ||
| - name: Run Safe Fix Engine (apply) | ||
| run: | | ||
| python3 cto-watchdog/scripts/safe-fix.py . --json > /tmp/fixes.json 2>/dev/null || echo "[]" > /tmp/fixes.json | ||
| # Show what was fixed | ||
| python3 -c " | ||
| import json | ||
| try: | ||
| fixes = json.load(open('/tmp/fixes.json')) | ||
| print(f'🔧 Applied {len(fixes)} safe fixes:') | ||
| for f in fixes: | ||
| print(f' ✅ [{f.get(\"risk\",\"LOW\")}] {f.get(\"file_path\",\"\")}: {f.get(\"description\",\"\")}') | ||
| except: | ||
| print('No fixes applied') | ||
| " 2>/dev/null || true | ||
| - name: Commit and push fixes | ||
| run: | | ||
| if [ -n "$(git status --porcelain)" ]; then | ||
| git config user.name "CTO Watchdog" | ||
| git config user.email "dhaher-labs@email.com" | ||
| git add -A | ||
| FIXES_COUNT=$(python3 -c "import json; print(len(json.load(open('/tmp/fixes.json'))))" 2>/dev/null || echo "several") | ||
| git commit -m "[cto-watchdog] safe-fix: branding and formatting fixes (${FIXES_COUNT} fixes) | ||
| Auto-fixed by CTO Watchdog v${{ env.CTO_WATCHDOG_VERSION }} | ||
| Risk level: LOW | ||
| Classification: ${{ needs.classify-event.outputs.category }} | ||
| Trigger: ${{ github.event_name }}" | ||
| git push | ||
| echo "✅ Safe fixes committed and pushed" | ||
| else | ||
| echo "ℹ️ No changes to commit" | ||
| fi | ||
| - name: Upload fixes report | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: safe-fixes | ||
| path: /tmp/fixes.json | ||
| retention-days: 30 | ||
| notify: | ||
| name: Send Notification | ||
| needs: [download-scripts, classify-event, audit] | ||
| runs-on: ubuntu-latest | ||
| if: always() | ||
| steps: | ||
| - name: Download audit report | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: audit-report | ||
| path: /tmp/ | ||
| - name: Determine recipient | ||
| id: recipient | ||
| run: | | ||
| OWNER="${{ github.repository_owner }}" | ||
| if [ "$OWNER" = "mulkymalikuldhaher" ]; then | ||
| echo "email=mulkymalikuldhr@mail.com" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "email=dhaher-labs@email.com" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Send email notification | ||
| if: ${{ secrets.CTO_WATCHDOG_SMTP_SERVER }} | ||
| uses: dawidd6/action-send-mail@v3 | ||
| with: | ||
| server_address: ${{ secrets.CTO_WATCHDOG_SMTP_SERVER }} | ||
| server_port: ${{ secrets.CTO_WATCHDOG_SMTP_PORT }} | ||
| username: ${{ secrets.CTO_WATCHDOG_SMTP_USER }} | ||
| password: ${{ secrets.CTO_WATCHDOG_SMTP_PASS }} | ||
| from: ${{ secrets.CTO_WATCHDOG_SMTP_FROM }} | ||
| to: ${{ steps.recipient.outputs.email }} | ||
| subject: "[CTO-Watchdog] [${{ needs.classify-event.outputs.risk_level }}] ${{ github.repository }}: ${{ github.event_name }}" | ||
| body: | | ||
| CTO Watchdog Audit Report | ||
| Repository: ${{ github.repository }} | ||
| Event: ${{ github.event_name }} | ||
| Risk Level: ${{ needs.classify-event.outputs.risk_level }} | ||
| Category: ${{ needs.classify-event.outputs.category }} | ||
| Auto-fixable: ${{ needs.classify-event.outputs.auto_fixable }} | ||
| See the full audit report in the GitHub Actions artifacts. | ||
| Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | ||
| --- | ||
| CTO Watchdog v${{ env.CTO_WATCHDOG_VERSION }} — Dhaher Labs | ||
| env: | ||
| CTO_WATCHDOG_SMTP_SERVER: ${{ secrets.CTO_WATCHDOG_SMTP_SERVER }} | ||
| - name: Create issue for high/critical findings | ||
| if: needs.classify-event.outputs.risk_level == 'high' || needs.classify-event.outputs.risk_level == 'critical' | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const fs = require('fs'); | ||
| let report; | ||
| try { | ||
| report = JSON.parse(fs.readFileSync('/tmp/audit-report.json', 'utf8')); | ||
| } catch (e) { | ||
| report = { summary: { critical: 0, high: 0 }, issues: [] }; | ||
| } | ||
| const critical = report.summary.critical || 0; | ||
| const high = report.summary.high || 0; | ||
| if (critical > 0 || high > 0) { | ||
| const riskLabel = critical > 0 ? 'CRITICAL' : 'HIGH'; | ||
| const title = `[CTO-Watchdog] ${riskLabel}: ${critical + high} issue(s) found`; | ||
| const body = `## CTO Watchdog Finding\n\n` + | ||
| `**Risk Level**: ${riskLabel}\n` + | ||
| `**Issues Found**: ${critical} critical, ${high} high\n\n` + | ||
| report.issues | ||
| .filter(i => i.severity === 'CRITICAL' || i.severity === 'HIGH') | ||
| .map(i => `### [${i.severity}] ${i.title}\n- File: \`${i.file}\`\n- ${i.description || ''}\n- Recommendation: ${i.recommendation || 'Review manually'}`) | ||
| .join('\n\n') + | ||
| `\n\n---\n*Detected by CTO Watchdog v${process.env.CTO_WATCHDOG_VERSION}*`; | ||
| try { | ||
| await github.rest.issues.create({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| title: title, | ||
| body: body, | ||
| labels: ['cto-watchdog', 'automated'] | ||
| }); | ||
| } catch (labelErr) { | ||
| // Label might not exist, create without labels | ||
| await github.rest.issues.create({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| title: title, | ||
| body: body | ||
| }); | ||
| } | ||
| } | ||
| - name: Log notification | ||
| run: | | ||
| echo "📧 Notification sent to: ${{ steps.recipient.outputs.email }}" | ||
| echo "📋 Risk Level: ${{ needs.classify-event.outputs.risk_level }}" | ||
| echo "📊 Category: ${{ needs.classify-event.outputs.category }}" | ||
| echo "🔧 Auto-fixable: ${{ needs.classify-event.outputs.auto_fixable }}" | ||