Skip to content

Commit d40baf8

Browse files
committed
ci: introduce benchmark comparison workflow
This is a workflow which can be triggered by commenting "\benchmark" on PRs (or through manual dispatch). It uses the `benchmark_compare.py` to run and compare benchmarks. The comparison result is posted as a comment on the PR.
1 parent 22dba1b commit d40baf8

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
name: Compare Benchmarks
2+
3+
on:
4+
issue_comment:
5+
types: [created]
6+
workflow_dispatch:
7+
inputs:
8+
pr_number:
9+
description: 'PR number to run benchmarks for'
10+
required: true
11+
type: number
12+
13+
jobs:
14+
benchmark:
15+
if: >
16+
(github.event_name == 'issue_comment'
17+
&& github.event.issue.pull_request
18+
&& contains(github.event.comment.body, '/benchmark')
19+
&& (github.event.comment.author_association == 'COLLABORATOR' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER')
20+
) || github.event_name == 'workflow_dispatch'
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 30
23+
permissions:
24+
contents: read
25+
pull-requests: write
26+
actions: read
27+
statuses: write
28+
29+
steps:
30+
- name: Get PR details
31+
id: pr
32+
uses: actions/github-script@v7
33+
with:
34+
script: |
35+
let pullRequestNumber;
36+
37+
if (context.eventName === 'workflow_dispatch') {
38+
// Manual dispatch - use provided PR number
39+
pullRequestNumber = parseInt('${{ github.event.inputs.pr_number }}');
40+
} else {
41+
// Issue comment trigger - use issue number
42+
pullRequestNumber = context.issue.number;
43+
}
44+
45+
// Fetch PR details
46+
const { data: pullRequest } = await github.rest.pulls.get({
47+
owner: context.repo.owner,
48+
repo: context.repo.repo,
49+
pull_number: pullRequestNumber,
50+
});
51+
52+
const is_fork = pullRequest.head.repo.full_name !== pullRequest.base.repo.full_name;
53+
return {
54+
base_sha: pullRequest.base.sha,
55+
head_sha: pullRequest.head.sha,
56+
base_ref: pullRequest.base.ref,
57+
head_ref: pullRequest.head.ref,
58+
head_repo_full_name: pullRequest.head.repo.full_name,
59+
base_repo_full_name: pullRequest.base.repo.full_name,
60+
is_fork: is_fork,
61+
pr_number: pullRequestNumber
62+
};
63+
64+
- name: Create status check
65+
uses: actions/github-script@v7
66+
with:
67+
script: |
68+
const prData = ${{ steps.pr.outputs.result }};
69+
await github.rest.repos.createCommitStatus({
70+
owner: context.repo.owner,
71+
repo: context.repo.repo,
72+
sha: prData.head_sha,
73+
state: 'pending',
74+
target_url: `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`,
75+
description: 'Running benchmark comparison...',
76+
context: 'comparing benchmarks'
77+
});
78+
79+
- name: Checkout base repository
80+
uses: actions/checkout@v4
81+
with:
82+
fetch-depth: 0
83+
84+
- name: Add fork as remote and fetch (if needed)
85+
if: fromJson(steps.pr.outputs.result).is_fork
86+
run: |
87+
echo "PR is from fork: ${{ fromJson(steps.pr.outputs.result).head_repo_full_name }}"
88+
git remote add fork https://github.com/${{ fromJson(steps.pr.outputs.result).head_repo_full_name }}.git
89+
git fetch fork
90+
91+
- name: Verify commits
92+
run: |
93+
echo "=== Git Remotes ==="
94+
git remote -v
95+
echo "=== Base commit ==="
96+
echo "Base SHA: ${{ fromJson(steps.pr.outputs.result).base_sha }}"
97+
git log --oneline -1 ${{ fromJson(steps.pr.outputs.result).base_sha }} 2>/dev/null || echo "Base commit not found locally"
98+
echo "=== Head commit ==="
99+
echo "Head SHA: ${{ fromJson(steps.pr.outputs.result).head_sha }}"
100+
git log --oneline -1 ${{ fromJson(steps.pr.outputs.result).head_sha }} 2>/dev/null || echo "Head commit not found locally"
101+
102+
- name: Install Dependencies
103+
run: |
104+
sudo apt-get update
105+
sudo apt-get install -y cmake ninja-build libboost-dev libboost-locale-dev libboost-random-dev libboost-regex-dev libeigen3-dev libmimalloc-dev
106+
107+
- name: Run Benchmark Script
108+
working-directory: ${{ github.workspace }}
109+
run: python3 bin/admin/benchmark_compare.py ${{ fromJson(steps.pr.outputs.result).base_sha }} ${{ fromJson(steps.pr.outputs.result).head_sha }}
110+
111+
- name: Upload Results
112+
uses: actions/upload-artifact@v4
113+
with:
114+
name: benchmark-data
115+
path: |
116+
${{ github.workspace }}/${{ fromJson(steps.pr.outputs.result).base_sha }}-results.json
117+
${{ github.workspace }}/${{ fromJson(steps.pr.outputs.result).head_sha }}-results.json
118+
${{ github.workspace }}/${{ fromJson(steps.pr.outputs.result).base_sha }}-${{ fromJson(steps.pr.outputs.result).head_sha }}-comparison.txt
119+
retention-days: 30
120+
121+
- name: Post Results
122+
uses: actions/github-script@v7
123+
with:
124+
script: |
125+
const fs = require('fs');
126+
const path = require('path');
127+
128+
const prData = ${{ steps.pr.outputs.result }};
129+
const comparisonFile = `${prData.base_sha}-${prData.head_sha}-comparison.txt`;
130+
const comparisonPath = path.join('${{ github.workspace }}', comparisonFile);
131+
132+
let comparisonResults = '';
133+
try {
134+
comparisonResults = fs.readFileSync(comparisonPath, 'utf8');
135+
} catch (error) {
136+
comparisonResults = 'Error reading comparison results: ' + error.message;
137+
}
138+
139+
const commentBody = `## Benchmark Comparison Results
140+
141+
\`\`\`
142+
${comparisonResults}
143+
\`\`\`
144+
145+
#### [Full benchmark data](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
146+
`;
147+
148+
github.rest.issues.createComment({
149+
issue_number: prData.pr_number,
150+
owner: context.repo.owner,
151+
repo: context.repo.repo,
152+
body: commentBody
153+
});
154+
155+
- name: Update status check - Success
156+
if: success()
157+
uses: actions/github-script@v7
158+
with:
159+
script: |
160+
const prData = ${{ steps.pr.outputs.result }};
161+
await github.rest.repos.createCommitStatus({
162+
owner: context.repo.owner,
163+
repo: context.repo.repo,
164+
sha: prData.head_sha,
165+
state: 'success',
166+
target_url: `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`,
167+
description: 'Benchmark comparison completed successfully',
168+
context: 'comparing benchmarks'
169+
});
170+
171+
- name: Notify on Failure
172+
if: failure()
173+
uses: actions/github-script@v7
174+
with:
175+
script: |
176+
const prData = ${{ steps.pr.outputs.result }};
177+
178+
// Update status check to failed
179+
await github.rest.repos.createCommitStatus({
180+
owner: context.repo.owner,
181+
repo: context.repo.repo,
182+
sha: prData.head_sha,
183+
state: 'failure',
184+
target_url: `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`,
185+
description: 'Benchmark comparison failed',
186+
context: 'comparing benchmarks'
187+
});
188+
189+
// Post failure comment
190+
github.rest.issues.createComment({
191+
issue_number: prData.pr_number,
192+
owner: context.repo.owner,
193+
repo: context.repo.repo,
194+
body: 'Benchmark comparison failed. Check the workflow logs for details.'
195+
});

0 commit comments

Comments
 (0)