99 description : Pull request number
1010 required : true
1111 type : number
12+ suite :
13+ description : Benchmark suite
14+ required : true
15+ default : full
16+ type : choice
17+ options :
18+ - full
19+ - web
1220
1321permissions :
1422 contents : read
4654 throw new Error('Unable to determine pull request number');
4755 }
4856
57+ const suite = context.eventName === 'workflow_dispatch'
58+ ? context.payload.inputs?.suite ?? 'full'
59+ : 'full';
60+ const benchmarkScripts = { full: 'benchmark', web: 'benchmark:web' };
61+ if (!Object.hasOwn(benchmarkScripts, suite)) {
62+ throw new Error(`Unsupported benchmark suite: ${suite}`);
63+ }
64+
4965 const { owner, repo } = context.repo;
5066 const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
5167
5571 core.setOutput('base_ref', pr.data.base.ref);
5672 core.setOutput('head_ref', pr.data.head.ref);
5773 core.setOutput('head_repo', pr.data.head.repo.full_name);
74+ core.setOutput('suite', suite);
75+ core.setOutput('script', benchmarkScripts[suite]);
5876
5977 const sameRepo = pr.data.head.repo.full_name === `${owner}/${repo}`;
6078 core.setOutput('same_repo', sameRepo ? 'true' : 'false');
@@ -191,31 +209,49 @@ jobs:
191209 run : pnpm -C bench-head/packages/treecrdt-wa-sqlite/e2e exec playwright install --with-deps chromium
192210
193211 - name : Run benchmarks (base)
212+ id : benchmark_base
194213 if : steps.pr.outputs.same_repo == 'true'
214+ continue-on-error : true
195215 working-directory : bench-base
196216 env :
217+ BENCHMARK_SCRIPT : ${{ steps.pr.outputs.script }}
197218 PLAYWRIGHT_BROWSERS_PATH : ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
198- run : env -u CI pnpm run benchmark
219+ run : |
220+ set -o pipefail
221+ status=0
222+ env -u CI pnpm run "$BENCHMARK_SCRIPT" 2>&1 | tee benchmark.log || status=$?
223+ pnpm run benchmark:aggregate || status=$?
224+ exit "$status"
199225
200226 - name : Run benchmarks (head)
227+ id : benchmark_head
201228 if : steps.pr.outputs.same_repo == 'true'
229+ continue-on-error : true
202230 working-directory : bench-head
203231 env :
232+ BENCHMARK_SCRIPT : ${{ steps.pr.outputs.script }}
204233 PLAYWRIGHT_BROWSERS_PATH : ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
205- run : env -u CI pnpm run benchmark
234+ run : |
235+ set -o pipefail
236+ status=0
237+ env -u CI pnpm run "$BENCHMARK_SCRIPT" 2>&1 | tee benchmark.log || status=$?
238+ pnpm run benchmark:aggregate || status=$?
239+ exit "$status"
206240
207241 - name : Upload benchmark artifacts
208- if : steps.pr.outputs.same_repo == 'true'
242+ if : always() && steps.pr.outputs.same_repo == 'true'
209243 uses : actions/upload-artifact@v4
210244 with :
211245 name : benchmarks-${{ steps.pr.outputs.number }}
212246 if-no-files-found : warn
213247 path : |
214248 bench-base/benchmarks/**
215249 bench-head/benchmarks/**
250+ bench-base/benchmark.log
251+ bench-head/benchmark.log
216252
217253 - name : Prepare comment body
218- if : steps.pr.outputs.same_repo == 'true'
254+ if : always() && steps.pr.outputs.same_repo == 'true'
219255 id : render
220256 uses : actions/github-script@v7
221257 with :
@@ -300,6 +336,10 @@ jobs:
300336 const headSha = '${{ steps.pr.outputs.head_sha }}'.slice(0, 7);
301337 const baseRef = '${{ steps.pr.outputs.base_ref }}';
302338 const headRef = '${{ steps.pr.outputs.head_ref }}';
339+ const suite = '${{ steps.pr.outputs.suite }}';
340+ const baseOutcome = '${{ steps.benchmark_base.outcome }}';
341+ const headOutcome = '${{ steps.benchmark_head.outcome }}';
342+ const benchmarkFailed = [baseOutcome, headOutcome].includes('failure');
303343 const triggerLabel =
304344 context.eventName === 'workflow_dispatch' ? '`workflow_dispatch`' : '`/bench`';
305345
@@ -308,11 +348,16 @@ jobs:
308348 '## Benchmarks',
309349 '',
310350 `Triggered by ${triggerLabel} - Run: ${runUrl}`,
351+ `Suite: \`${suite}\` - Base command: ${baseOutcome} - Head command: ${headOutcome}`,
311352 `Base: ${baseRef} (${baseSha}) - Head: ${headRef} (${headSha})`,
312353 `Compared entries: ${keys.length} - Improved: ${improved} - Regressed: ${regressed}`,
313354 '',
314355 ];
315356
357+ if (benchmarkFailed) {
358+ parts.push('_At least one benchmark command failed. Available partial results and logs are included in the artifact._', '');
359+ }
360+
316361 if (keys.length === 0) {
317362 parts.push('_No benchmark results were found. Check the workflow logs for details._', '');
318363 } else {
@@ -324,13 +369,14 @@ jobs:
324369 'Artifacts:',
325370 '- base summary: `bench-base/benchmarks/summary.json`',
326371 '- head summary: `bench-head/benchmarks/summary.json`',
372+ '- command logs: `bench-base/benchmark.log`, `bench-head/benchmark.log`',
327373 ''
328374 );
329375
330376 return parts.join('\n');
331377
332378 - name : Comment results
333- if : steps.pr.outputs.same_repo == 'true'
379+ if : always() && steps.pr.outputs.same_repo == 'true' && steps.render.outcome == 'success '
334380 uses : actions/github-script@v7
335381 env :
336382 BENCH_BODY : ${{ steps.render.outputs.result }}
@@ -365,3 +411,10 @@ jobs:
365411 body,
366412 });
367413 }
414+
415+ - name : Fail after publishing partial results
416+ if : |
417+ always() &&
418+ steps.pr.outputs.same_repo == 'true' &&
419+ (steps.benchmark_base.outcome == 'failure' || steps.benchmark_head.outcome == 'failure')
420+ run : exit 1
0 commit comments