Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .github/workflows/performance-comment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: Performance Comment

# Posts the perf comparison as a sticky PR comment.
#
# This runs in the *base* repository's context via `workflow_run`, which
# means it has a writable `GITHUB_TOKEN` even for pull requests opened from
# forks. The Performance workflow itself runs in the (potentially untrusted)
# PR context and only produces an artifact — no PR write permissions there.
#
# Security model:
# * The artifact from the PR-context workflow is treated as untrusted
# input. We only consume the JSON benchmark outputs (`base.json` /
# `pr.json`) and the PR number, and we regenerate `report.md` here
# using the base repository's checked-out `perf.compare` so the
# markdown posted under the writable token is never attacker-supplied.
# * The PR number is validated as a positive integer before being used.

on:
workflow_run:
workflows: ["Performance"]
types:
- completed

permissions:
contents: read
pull-requests: write
actions: read

jobs:
comment:
name: Post sticky PR comment
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request'

steps:
- name: Check out base repository (trusted)
uses: actions/checkout@v4

- name: Download perf-results artifact
id: download
# Don't fail this job if the upstream Performance run was cancelled
# or failed before producing the artifact — we just skip posting.
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: perf-results
path: perf-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

Comment thread
JacksonWeber marked this conversation as resolved.
- name: Resolve PR number
id: pr
if: steps.download.outcome == 'success'
run: |
set -euo pipefail
pr=""
if [ -f perf-results/pr-number.txt ]; then
pr="$(tr -d '[:space:]' < perf-results/pr-number.txt)"
fi

if ! [[ "$pr" =~ ^[1-9][0-9]*$ ]]; then
echo "pr-number.txt missing or not a positive integer (got: '${pr}'); skipping comment." >&2
echo "number=" >> "$GITHUB_OUTPUT"
exit 0
fi

echo "number=$pr" >> "$GITHUB_OUTPUT"

- name: Set up Python
if: steps.pr.outputs.number != ''
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Regenerate report from JSON (trusted code)
id: report
if: steps.pr.outputs.number != ''
run: |
set -euo pipefail
if [ ! -s perf-results/base.json ] || [ ! -s perf-results/pr.json ]; then
echo "base.json or pr.json missing from artifact; skipping comment." >&2
echo "found=false" >> "$GITHUB_OUTPUT"
exit 0
fi

# Use the base repo's perf.compare (trusted) to render the markdown
# from the PR-supplied JSON data, so we never post attacker-supplied
# markdown under the base repo's writable token.
python -m perf.compare \
--baseline perf-results/base.json \
--candidate perf-results/pr.json \
--threshold "${PERF_REGRESSION_THRESHOLD:-15}" \
--output report.md \
|| true

if [ -s report.md ]; then
echo "found=true" >> "$GITHUB_OUTPUT"
else
echo "found=false" >> "$GITHUB_OUTPUT"
echo "report.md was not produced; skipping comment." >&2
fi

- name: Post sticky PR comment
if: steps.pr.outputs.number != '' && steps.report.outputs.found == 'true'
uses: marocchino/sticky-pull-request-comment@v2
with:
header: perf-comparison
number: ${{ steps.pr.outputs.number }}
path: report.md
17 changes: 6 additions & 11 deletions .github/workflows/performance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ jobs:
--output "$GITHUB_WORKSPACE/report.md"
echo "exit_code=$?" >> "$GITHUB_OUTPUT"

- name: Record PR number
if: always() && github.event_name == 'pull_request'
run: echo "${{ github.event.pull_request.number }}" > "$GITHUB_WORKSPACE/pr-number.txt"

- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
Expand All @@ -103,17 +107,8 @@ jobs:
pr.json
base.json
report.md

- name: Post sticky PR comment
# Skip for PRs from forks — the default GITHUB_TOKEN has read-only
# access in that case and the comment API will reject the call. The
# JSON artifacts and the workflow log still contain the comparison.
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
continue-on-error: true
uses: marocchino/sticky-pull-request-comment@v2
with:
header: perf-comparison
path: report.md
pr-number.txt
if-no-files-found: ignore

- name: Fail on regression
if: steps.compare.outputs.exit_code != '0'
Expand Down
Loading