Skip to content

Benchmark Comment

Benchmark Comment #47

Workflow file for this run

# Posts the benchmark comparison comment produced by bench-compare.yml.
#
# bench-compare.yml runs on `pull_request`, where fork PRs get a read-only
# token that cannot post comments. This workflow runs on `workflow_run` with
# write permissions, downloads the comment artifact from the triggering run,
# and creates/updates the marker-managed PR comment — for fork and same-repo
# PRs alike.
#
# Security: this workflow has a write token but never checks out or executes
# PR code. The artifact content is treated as data only; the PR number it
# names is validated against the triggering run's head SHA before posting.
name: Benchmark Comment
on:
workflow_run:
workflows: ['Benchmark Comparison']
types: [completed]
permissions:
actions: read
pull-requests: write
jobs:
post-comment:
name: 'Post benchmark comment'
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request'
steps:
- uses: actions/download-artifact@v4
with:
name: bench-comment
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
path: comment-artifact
- name: Post PR comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const marker = '<!-- bench-compare -->';
let body = fs.readFileSync('comment-artifact/bench-comment.md', 'utf8');
const prNumber = parseInt(
fs.readFileSync('comment-artifact/pr-number.txt', 'utf8').trim(),
10
);
if (!Number.isInteger(prNumber) || prNumber <= 0) {
core.setFailed(`Invalid PR number in artifact: ${prNumber}`);
return;
}
// The artifact was produced by an untrusted run; confirm the PR it
// names is the one the triggering run actually built.
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});
if (pr.head.sha !== context.payload.workflow_run.head_sha) {
core.info(
`PR #${prNumber} head (${pr.head.sha}) does not match the ` +
`triggering run (${context.payload.workflow_run.head_sha}) — ` +
'stale or mismatched artifact, skipping.'
);
return;
}
// GitHub comments cap at 65536 characters; keep the summary
// tables and truncate the tail (full output stays in the job
// summary of the triggering run).
const limit = 65000;
if (body.length > limit) {
body =
body.slice(0, limit) +
'\n```\n\n</details>\n\n> Output truncated — full results in the workflow job summary.\n';
}
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
});
const existing = comments.find((c) => c.body.includes(marker));
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,
});
}