name: Performance Benchmarks
on:
push:
branches: [main]
paths-ignore:
- '**.md'
- 'LICENSE'
- '.gitignore'
- 'docs/**'
pull_request:
branches: [main]
paths-ignore:
- '**.md'
- 'LICENSE'
- '.gitignore'
- 'docs/**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
permissions:
contents: read
pull-requests: write
jobs:
benchmark:
name: Run Benchmarks
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
key: bench
- name: Cache baseline (from main branch)
uses: actions/cache@v5
with:
path: .wasm-slim/benchmarks
key: benchmark-baseline-${{ github.ref }}
restore-keys: |
benchmark-baseline-refs/heads/main
- name: Build bench-tracker
run: cargo build --release --bin bench-tracker
- name: Run benchmarks
run: cargo bench --quiet
- name: Check for regressions (PR only)
if: github.event_name == 'pull_request'
id: regression-check
continue-on-error: true
run: |
cargo run --release --bin bench-tracker -- compare \
--fail-on-regression \
--max-regression 10.0
- name: Compare with baseline (PR only)
if: github.event_name == 'pull_request'
id: compare
run: |
echo "## Benchmark Results" > benchmark-comment.md
echo "" >> benchmark-comment.md
if cargo run --release --bin bench-tracker -- compare > benchmark-output.txt 2>&1; then
cat benchmark-output.txt >> benchmark-comment.md
echo "" >> benchmark-comment.md
echo "✓ No significant performance regressions detected" >> benchmark-comment.md
else
cat benchmark-output.txt >> benchmark-comment.md
echo "" >> benchmark-comment.md
echo "⚠️ Performance regressions detected!" >> benchmark-comment.md
fi
- name: Comment on PR with results
if: github.event_name == 'pull_request'
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
let body = '## Benchmark Results\n\n';
try {
const output = fs.readFileSync('benchmark-comment.md', 'utf8');
body = output;
} catch (err) {
body += 'Benchmark comparison not available.\n';
}
// Add failure notice if regressions detected
if ('${{ steps.regression-check.outcome }}' === 'failure') {
body = '⚠️ **Performance Regression Detected**\n\n' + body;
}
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('## Benchmark Results')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
- name: Update baseline (main branch only)
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
cargo run --release --bin bench-tracker -- baseline \
--version "main-${{ github.sha }}"
- name: Upload benchmark results
uses: actions/upload-artifact@v6
if: always()
with:
name: criterion-results
path: target/criterion
retention-days: 30
- name: Upload baseline
uses: actions/upload-artifact@v6
if: github.ref == 'refs/heads/main'
with:
name: benchmark-baseline
path: .wasm-slim/benchmarks/baseline.json
retention-days: 90
- name: Fail on regression
if: steps.regression-check.outcome == 'failure'
run: |
echo "::error::Performance regression detected! Review the benchmark results above."
exit 1