Allow panda.yml to request benchmark runs for PRs

This change introduces a new command to `@TheRespectPanda` bot,
allowing him to dispatch the ci-perf.yml workflow benchmarks for
a pull request.

Initially, the bot will just trigger it and return the workflow
run URL for manual inspection. Future iterations on this feature
could then grab the benchmark results and update the comment.
This commit is contained in:
Alexandre Gomes Gaigalas 2026-02-20 01:31:30 -03:00
commit 26c0292bf9
No known key found for this signature in database
GPG key ID: C68060CCE0AE33F0
2 changed files with 118 additions and 9 deletions

View file

@ -21,19 +21,41 @@ on:
options:
- 'latest'
- 'rebaseline'
pull:
description: 'Pull request number to benchmark (optional)'
required: false
jobs:
tests:
name: Benchmarks
pr-benchmarks:
name: PR Benchmarks
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.pull }}
runs-on: ubuntu-latest
env:
BENCHMARK_SHA: ${{ github.sha }}
permissions:
contents: write
contents: read
steps:
- uses: actions/checkout@v6
with:
persist-credentials: true
persist-credentials: false
- name: Checkout PR head
run: |
set -euo pipefail
PR=${{ github.event.inputs.pull }}
# Validate that PR is numeric to avoid injection or unexpected ref resolution
if ! printf '%s\n' "$PR" | grep -Eq '^[0-9]+$'; then
echo "Invalid pull request number: '$PR'. Expected a numeric value." >&2
exit 1
fi
# fetch the pull request head (works for forks)
git fetch origin "pull/${PR}/head:pr-${PR}" || git fetch origin "+refs/pull/${PR}/head:pr-${PR}"
git checkout "pr-${PR}"
echo "Checked out PR #${PR}"
echo "BENCHMARK_SHA=$(git rev-parse --verify HEAD)" >> "$GITHUB_ENV"
- uses: ./.github/actions/setup-action
with:
extensions: xdebug
@ -48,13 +70,57 @@ jobs:
run: |
# Baseline does not exist or rebaseline requested. Generate it.
if [ -z "$(ls -A .phpbench)" ] || [ "${{ github.event.inputs.baseline || 'latest' }}" = "rebaseline" ]; then
vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${GITHUB_SHA}
vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${BENCHMARK_SHA}
# Baseline exists. Compare against it.
else
vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${GITHUB_SHA} --ref=latest --tolerate-failure
vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${BENCHMARK_SHA} --ref=latest --tolerate-failure
fi
# Generate report for human consumption
vendor/bin/phpbench report --report=aggregate --ref=latest |
tail -n+2 | head -n-2 | tr '+' '|' > report.md
cat report.md > "$GITHUB_STEP_SUMMARY"
historical-benchmarks:
name: Historical Benchmarks
if: >-
github.event_name != 'workflow_dispatch' ||
!github.event.inputs.pull
runs-on: ubuntu-latest
env:
BENCHMARK_SHA: ${{ github.sha }}
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
persist-credentials: true
- uses: ./.github/actions/setup-action
with:
extensions: xdebug
- name: Fetch Benchmarks
run: |
git fetch origin benchmarks
mkdir -p .phpbench
git checkout origin/benchmarks -- .phpbench || echo "No previous benchmarks found"
- name: Run Benchmarks
run: |
# Baseline does not exist or rebaseline requested. Generate it.
if [ -z "$(ls -A .phpbench)" ] || [ "${{ github.event.inputs.baseline || 'latest' }}" = "rebaseline" ]; then
vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${BENCHMARK_SHA}
# Baseline exists. Compare against it.
else
vendor/bin/phpbench run --report=aggregate --progress=plain --store --tag=${BENCHMARK_SHA} --ref=latest --tolerate-failure
fi
# Generate report for human consumption
vendor/bin/phpbench report --report=aggregate --ref=latest |
tail -n+2 | head -n-2 | tr '+' '|' > report.md

View file

@ -3,34 +3,77 @@ name: TheRespectPanda Bot
on:
issue_comment:
types: [created]
if: startsWith(github.event.comment.body, '@TheRespectPanda')
jobs:
listen-comment:
if: startsWith(github.event.comment.body, '@TheRespectPanda')
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
actions: write
steps:
- uses: actions/github-script@v8
with:
github-token: ${{ secrets.PANDA_GITHUB_PAT }}
script: |
const body = (context.payload.comment && context.payload.comment.body).trim() || '';
const usage = 'Usage: `@TheRespectPanda ping|help`';
const usage = 'Usage: `@TheRespectPanda ping|help|benchmark`';
if (!body.startsWith('@TheRespectPanda')) {
return;
}
let answer;
switch (body) {
case '@TheRespectPanda ping':
answer = 'Pong! 🐼';
break;
case '@TheRespectPanda':
case '@TheRespectPanda help':
answer = 'Hello! I am TheRespectPanda Bot. ' + usage;
break;
case '@TheRespectPanda benchmark':
// Only runnable on pull requests
if (!context.payload.issue.pull_request) {
answer = 'The `benchmark` command can only be used on pull requests.';
break;
}
// Only members can trigger benchmarks
const association = context.payload.comment.author_association;
const allowedAssociations = ['OWNER', 'MEMBER', 'COLLABORATOR'];
if (!allowedAssociations.includes(association)) {
answer = 'Only repository members can trigger benchmarks.';
break;
}
try {
// dispatch the perf workflow
const ref = (context.payload.repository && context.payload.repository.default_branch) || 'main';
const workflowId = 'ci-perf.yml';
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: workflowId,
ref,
inputs: {
baseline: 'latest',
pull: String(context.issue.number)
}
});
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/workflows/${workflowId}`;
answer = `Triggered phpbench benchmarks for PR #${context.issue.number} — workflow run: ${runUrl}`;
} catch (err) {
answer = `Failed to trigger benchmarks: ${err.message}`;
}
break;
default:
answer = "I'm sorry, I don't understand that command. " + usage;
}