From 26af0cace4ec3ae95ce574db0f3426cb718586f0 Mon Sep 17 00:00:00 2001 From: Alexander Gilin Date: Tue, 16 Jun 2026 17:42:20 +0300 Subject: [PATCH] fix(ci): fix PR comment posting for fork PRs GitHub's GITHUB_TOKEN has read-only access for pull_request events from forks, so the peter-evans/create-or-update-comment step was failing with "Resource not accessible by integration". Split the commenting logic into a separate workflow_run-triggered workflow (pr-comment.yml) that runs in the base repo context where GITHUB_TOKEN has full write access. The CI workflow now saves PR metadata (PR number, SHA, run ID, artifact ID) as a short-lived artifact which the comment workflow downloads and uses to post/update the PR comment. --- .github/workflows/ci.yml | 47 +++++------------ .github/workflows/pr-comment.yml | 91 ++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/pr-comment.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7dbad95..82baf2fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: read # Checkout repository code - pull-requests: write # Create/update PR build report comments - issues: write # Required by peter-evans/create-or-update-comment to write comments via the Issues API strategy: matrix: node_version: @@ -60,40 +58,19 @@ jobs: # default is 90 days; however, we want to avoid excessive storage usage... retention-days: ${{ env.artifact-retention-days }} - - name: Find PR Comment - uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0 + - name: Save PR comment metadata if: github.event_name == 'pull_request' - id: find-pr-comment - with: - issue-number: ${{ github.event.number }} - comment-author: "github-actions[bot]" - body-includes: ${{ env.retention-comment }} + run: | + mkdir -p /tmp/pr-comment-meta + echo '${{ github.event.number }}' > /tmp/pr-comment-meta/pr-number.txt + echo '${{ github.sha }}' > /tmp/pr-comment-meta/head-sha.txt + echo '${{ github.run_id }}' > /tmp/pr-comment-meta/run-id.txt + echo '${{ steps.upload_build_artifacts.outputs.artifact-id }}' > /tmp/pr-comment-meta/artifact-id.txt - - name: Create / Update PR Comment - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 + - name: Upload PR comment metadata + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: github.event_name == 'pull_request' - env: - HEAD_SHA: ${{ github.sha }} - JOB_PATH: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - ARTIFACT_ID: ${{ steps.upload_build_artifacts.outputs.artifact-id }} with: - edit-mode: replace - issue-number: ${{ github.event.number }} - comment-id: ${{ steps.find-pr-comment.outputs.comment-id }} - body: | - ## Build Report - - [![badge]]($JOB_PATH) - - Please note: - - 1. Files only stay for around ${{ env.artifact-retention-days }} days! - 2. ${{ env.retention-comment }} - - | Name | Link | - |------------|--------------------------------------------------------| - | Commit | ${{ env.HEAD_SHA }} | - | Logs | ${{ env.JOB_PATH }} | - | VSIX Files | ${{ env.JOB_PATH }}/artifacts/${{ env.ARTIFACT_ID }} | - - [badge]: https://img.shields.io/badge/Build-Success!-3fb950?logo=github&style=for-the-badge + name: pr-comment-meta + path: /tmp/pr-comment-meta/ + retention-days: 1 diff --git a/.github/workflows/pr-comment.yml b/.github/workflows/pr-comment.yml new file mode 100644 index 00000000..d3835670 --- /dev/null +++ b/.github/workflows/pr-comment.yml @@ -0,0 +1,91 @@ +name: PR Build Comment +on: + workflow_run: + workflows: ["Continuous Integration"] + types: + - completed + +# Restrictive default: no permissions unless explicitly granted at the job level +permissions: {} + +env: + retention-comment: This comment will be **updated** with the data of the **last successful** build of this PR. + +jobs: + comment: + name: Post build report comment + runs-on: ubuntu-latest + # Only runs on CI triggered by a pull_request event (not direct pushes to main) + if: github.event.workflow_run.event == 'pull_request' + permissions: + pull-requests: write # Create/update PR build report comments + issues: write # Required by create-or-update-comment to write via the Issues API + actions: read # Read workflow run artifacts + + steps: + - name: Download PR comment metadata + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + name: pr-comment-meta + path: /tmp/pr-comment-meta + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + + - name: Post or update PR comment + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 + env: + RETENTION_COMMENT: ${{ env.retention-comment }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const fs = require('fs'); + const prNumber = fs.readFileSync('/tmp/pr-comment-meta/pr-number.txt', 'utf8').trim(); + const headSha = fs.readFileSync('/tmp/pr-comment-meta/head-sha.txt', 'utf8').trim(); + const runId = fs.readFileSync('/tmp/pr-comment-meta/run-id.txt', 'utf8').trim(); + const artifactId = fs.readFileSync('/tmp/pr-comment-meta/artifact-id.txt','utf8').trim(); + + const jobPath = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${runId}`; + const retentionComment = process.env.RETENTION_COMMENT; + + const body = [ + '## Build Report', + '', + `[![badge](https://img.shields.io/badge/Build-Success!-3fb950?logo=github&style=for-the-badge)](${jobPath})`, + '', + 'Please note:', + '', + '1. Files only stay for around 14 days!', + `2. ${retentionComment}`, + '', + '| Name | Link |', + '|------------|---------------------------------------------|', + `| Commit | ${headSha} |`, + `| Logs | ${jobPath} |`, + `| VSIX Files | ${jobPath}/artifacts/${artifactId} |`, + ].join('\n'); + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: parseInt(prNumber), + }); + + const existing = comments.find(c => + c.user.login === 'github-actions[bot]' && c.body.includes(retentionComment) + ); + + 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: parseInt(prNumber), + body, + }); + }