Repository navigation
151 lines (134 loc) · 5.78 KB
/
Copy pathperformance-comment.yml
File metadata and controls
151 lines (134 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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 we regenerate `report.md` here using the base
# repository's checked-out `perf.compare`.
# * The comment target comes only from trusted `workflow_run` metadata.
# Its current head must still match the SHA that produced the artifact.
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- 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@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: perf-results
path: perf-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve PR number
id: pr
if: steps.download.outcome == 'success'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const run = context.payload.workflow_run;
const pullRequests = run.pull_requests ?? [];
if (pullRequests.length > 1) {
core.warning(`Expected at most one pull request, found ${pullRequests.length}; skipping comment.`);
core.setOutput("number", "");
return;
}
let pullNumber;
if (pullRequests.length === 1) {
pullNumber = pullRequests[0].number;
} else {
const headRepository = run.head_repository;
const headOwner = headRepository?.owner?.login;
if (!headRepository?.id || !headOwner || !run.head_branch || !run.head_sha) {
core.warning("Workflow run is missing trusted head metadata; skipping comment.");
core.setOutput("number", "");
return;
}
const candidates = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: "all",
head: `${headOwner}:${run.head_branch}`,
per_page: 100,
});
const matches = candidates.filter((pullRequest) =>
pullRequest.head.sha === run.head_sha &&
pullRequest.head.ref === run.head_branch &&
pullRequest.head.repo?.id === headRepository.id
);
if (matches.length !== 1) {
core.warning(`Expected one PR matching the workflow head, found ${matches.length}; skipping comment.`);
core.setOutput("number", "");
return;
}
pullNumber = matches[0].number;
}
const { data: pullRequest } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullNumber,
});
if (pullRequest.head.sha !== run.head_sha) {
core.warning(`PR #${pullNumber} no longer points to workflow SHA ${run.head_sha}; skipping comment.`);
core.setOutput("number", "");
return;
}
core.setOutput("number", pullNumber);
- name: Set up Python
if: steps.pr.outputs.number != ''
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
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@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
with:
header: perf-comparison
number: ${{ steps.pr.outputs.number }}
path: report.md