diff --git a/.github/workflows/costguard-pr-comment.yml b/.github/workflows/costguard-pr-comment.yml index 6b1c5a6..bdbb181 100644 --- a/.github/workflows/costguard-pr-comment.yml +++ b/.github/workflows/costguard-pr-comment.yml @@ -9,32 +9,83 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Detect analyzable files + id: detect + run: | + FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD 2>/dev/null | grep -E '\.(prompt|txt|md)$' || true) + if [ -z "$FILES" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + echo "No analyzable prompt files changed — skipping analysis." + else + echo "skip=false" >> $GITHUB_OUTPUT + echo "Analyzable files found:" + echo "$FILES" + fi + - name: Run CostGuardAI id: costguard + if: steps.detect.outputs.skip == 'false' run: npx @camj78/costguardai@latest ci --fail-on-risk 70 --format json > result.json continue-on-error: true - name: Post PR comment + if: steps.detect.outputs.skip == 'false' uses: actions/github-script@v7 with: script: | const fs = require('fs'); - const data = JSON.parse(fs.readFileSync('result.json', 'utf8')); - const body = ` -❌ CostGuardAI Analysis + if (!fs.existsSync('result.json')) { + console.log('result.json not found — skipping comment.'); + return; + } + + let data; + try { + const raw = fs.readFileSync('result.json', 'utf8').trim(); + if (!raw) { + console.log('result.json is empty — skipping comment.'); + return; + } + data = JSON.parse(raw); + } catch (e) { + console.log('result.json could not be parsed — skipping comment: ' + e.message); + return; + } + + const files = Array.isArray(data.files) ? data.files : []; + + if (files.length === 0) { + console.log('No files in analysis output — skipping comment.'); + return; + } -Safety Score: ${data.safety_score}/100 + const highestRiskFile = files.reduce((max, f) => { + return (f.risk_score ?? 0) > (max.risk_score ?? 0) ? f : max; + }, files[0] || {}); -Top risks: -${data.top_risks?.map(r => `- ${r}`).join('\n') || '- None'} + const riskScore = highestRiskFile.risk_score ?? 0; + const safetyScore = 100 - riskScore; + const fileName = highestRiskFile.file || 'unknown'; + const riskDrivers = Array.isArray(highestRiskFile.risk_drivers) + ? highestRiskFile.risk_drivers + : []; -Prevent this automatically in CI: + const driversText = riskDrivers.length > 0 + ? riskDrivers.map(r => "- " + r).join("\n") + : "- None identified"; -\`\`\`yaml -- uses: Camj78/costguardai-action@v1 -\`\`\` -`; + const body = + "## CostGuardAI Safety Report\n\n" + + "**File:** `" + fileName + "`\n" + + "**Safety Score:** " + safetyScore + "/100 (100 - risk score of " + riskScore + ")\n\n" + + "**Risk Drivers:**\n" + + driversText + "\n\n" + + "---\n" + + "Add CostGuardAI to your CI pipeline:\n\n" + + "```yaml\n" + + "- uses: Camj78/costguardai-action@v1\n" + + "```"; await github.rest.issues.createComment({ issue_number: context.issue.number,