name: Frontend bundle visualizer comment on: workflow_run: workflows: - Frontend bundle visualizer types: - completed permissions: actions: read contents: read issues: write pull-requests: read concurrency: group: frontend-bundle-visualizer-comment cancel-in-progress: false jobs: comment: name: Comment frontend bundle visualizer if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - name: Checkout report generator uses: actions/checkout@v6.0.2 with: persist-credentials: false - name: Download visualizer report uses: actions/download-artifact@v8 with: name: frontend-bundle-visualizer-report path: ${{ runner.temp }}/frontend-bundle-visualizer github-token: ${{ github.token }} repository: ${{ github.repository }} run-id: ${{ github.event.workflow_run.id }} - name: Generate visualizer report run: node .github/scripts/frontend-bundle-visualizer-report.mjs "$RUNNER_TEMP/frontend-bundle-visualizer/stats.json" "$RUNNER_TEMP/frontend-bundle-visualizer/report.md" - name: Comment on pull request uses: actions/github-script@v9 with: github-token: ${{ secrets.FRONTEND_BUNDLE_VISUALIZER_COMMENT_TOKEN || github.token }} script: | const fs = require('node:fs'); const path = require('node:path'); const marker = ''; const reportDir = path.join(process.env.RUNNER_TEMP, 'frontend-bundle-visualizer'); const reportPath = path.join(reportDir, 'report.md'); const prNumberPath = path.join(reportDir, 'pr-number.txt'); const headShaPath = path.join(reportDir, 'head-sha.txt'); const workflowRun = context.payload.workflow_run; const headSha = workflowRun.head_sha; const { owner, repo } = context.repo; if (!fs.existsSync(reportPath)) { core.setFailed('The frontend bundle visualizer artifact does not contain report.md.'); return; } const artifactHeadSha = fs.existsSync(headShaPath) ? fs.readFileSync(headShaPath, 'utf8').trim() : null; if (artifactHeadSha != null && artifactHeadSha !== headSha) { core.setFailed(`The artifact head SHA (${artifactHeadSha}) does not match the workflow run head SHA (${headSha}).`); return; } const associatedPullRequests = new Map(); for (const pullRequest of workflowRun.pull_requests ?? []) { if (Number.isInteger(pullRequest.number)) { associatedPullRequests.set(pullRequest.number, pullRequest); } } const pullRequestsForCommit = await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, { owner, repo, commit_sha: headSha, per_page: 100, }); for (const pullRequest of pullRequestsForCommit) { associatedPullRequests.set(pullRequest.number, pullRequest); } const artifactPrNumber = fs.existsSync(prNumberPath) ? Number(fs.readFileSync(prNumberPath, 'utf8').trim()) : null; let issue_number = null; if (Number.isInteger(artifactPrNumber) && associatedPullRequests.has(artifactPrNumber)) { issue_number = artifactPrNumber; } else if (!Number.isInteger(artifactPrNumber) && associatedPullRequests.size === 1) { issue_number = [...associatedPullRequests.keys()][0]; } else if (Number.isInteger(artifactPrNumber)) { core.setFailed(`The artifact pull request number (${artifactPrNumber}) is not associated with ${headSha}.`); return; } else { core.setFailed(`Could not determine the pull request associated with ${headSha}.`); return; } const report = fs.readFileSync(reportPath, 'utf8').trim(); const shortSha = headSha.slice(0, 7); let body = [ marker, '## Frontend bundle visualizer', '', `Head: \`${shortSha}\``, '', report, ].join('\n'); const maxCommentLength = 65_000; if (body.length > maxCommentLength) { const footer = [ '', '', `_Report truncated because it exceeded ${maxCommentLength.toLocaleString('en-US')} characters. See the [workflow artifact](${workflowRun.html_url}) for the full report._`, ].join('\n'); body = `${body.slice(0, maxCommentLength - footer.length)}${footer}`; } const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number, per_page: 100, }); const previous = comments.find((comment) => comment.user?.type === 'Bot' && comment.body?.includes(marker)); if (previous) { await github.rest.issues.updateComment({ owner, repo, comment_id: previous.id, body, }); } else { await github.rest.issues.createComment({ owner, repo, issue_number, body, }); }