Skip to content

Benchmark

Benchmark #107

Workflow file for this run

name: Benchmark
on:
schedule:
# Run daily at 06:00 UTC
- cron: '0 6 * * *'
push:
branches: [main]
paths:
- 'src/**'
- 'benchmarks/**'
- 'package.json'
pull_request:
branches: [main]
paths:
- 'src/**'
- 'benchmarks/**'
workflow_dispatch: # Allow manual trigger
permissions:
contents: write
pull-requests: write
actions: read
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run build
# Install competitors for comparison
- run: npm install --no-save pino winston bunyan consola
# Run throughput benchmark and capture output. Throughput.mjs writes
# ./benchmark-throughput.json with structured metrics in addition to
# human-readable console output.
- name: Run throughput benchmark
id: bench
run: |
echo "## Benchmark Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
node benchmarks/throughput.mjs 2>&1 | tee benchmark-output.txt >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
# Run size benchmark; writes ./benchmark-size.json
- name: Run size benchmark
run: |
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Bundle Size" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
node benchmarks/size.mjs 2>&1 | tee -a benchmark-output.txt >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
# Pull the most recent baseline JSON from the latest successful main run.
# Missing artifact (first run, deleted, etc.) is non-fatal — compare.mjs
# will mark every metric as 🆕.
- name: Download baseline benchmark from main
if: github.event_name == 'pull_request'
uses: dawidd6/action-download-artifact@v6
with:
workflow: benchmark.yml
branch: main
name: benchmark-baseline-main
path: baseline
if_no_artifact_found: warn
search_artifacts: true
# Build markdown comparison report (traffic-light tables per metric).
# Always exits 0 — never blocks the merge.
- name: Compare against baseline
if: github.event_name == 'pull_request'
env:
KONSOLE_RAW_OUTPUT: ${{ github.workspace }}/benchmark-output.txt
run: node benchmarks/compare.mjs
# Store this run's full results as a per-SHA artifact (kept 90 days).
- name: Upload per-SHA benchmark results
uses: actions/upload-artifact@v4
with:
name: benchmark-results-${{ github.sha }}
path: |
benchmark-output.txt
benchmark-throughput.json
benchmark-size.json
retention-days: 90
# On main pushes & schedule, also upload under a stable name so PR runs
# can fetch this as the baseline. Overwrites the previous baseline.
- name: Upload baseline benchmark (main only)
if: github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4
with:
name: benchmark-baseline-main
path: |
benchmark-throughput.json
benchmark-size.json
retention-days: 90
overwrite: true
# Post comment on PR with benchmark comparison
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
let body;
try {
body = fs.readFileSync('benchmark-report.md', 'utf8');
} catch (e) {
// Fallback if compare.mjs failed to write a report — embed raw output.
const raw = fs.readFileSync('benchmark-output.txt', 'utf8');
body = '## 📊 Benchmark Results\n\n' +
'_Comparison report unavailable — showing raw output._\n\n' +
'<details><summary>Full output</summary>\n\n```\n' + raw + '\n```\n</details>';
}
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find((c) =>
c.user.type === 'Bot' && c.body.includes('📊 Benchmark Results'),
);
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: context.issue.number,
body,
});
}