How to use Bencher in GitHub Actions
Depending on your use case, you can set up Continuous Benchmarking in GitHub Actions for your:
Make sure you have created an API token
and set it as a Repository secret named BENCHER_API_TOKEN
before continuing on!
Navigate to Your Repo -> Settings -> Secrets and variables -> Actions -> New repository secret
.
Name the secret BENCHER_API_TOKEN
and set the secret value to your API token.
In GitHub Actions,
secrets are not passed to the runner when a workflow is triggered from a forked repository.
Therefore, you will need to use a branch from the same repository
when adding any of the workflows below to your repository with a pull request.
If you try to add Bencher with a pull request from a fork,
then the BENCHER_API_TOKEN
secret will not be available.
${{ secrets.BENCHER_API_TOKEN }}
will be an empty string.
Base Branch
A cornerstone of Statistical Continuous Benchmarking is having a historical baseline for your base branch. This historical baseline can then be used to detect performance regressions in Pull Requests.
- Create a GitHub Actions
workflow
file. (ex:.github/workflows/base_benchmarks.yml
) - Run on
push
events to themain
branch. See the GitHub Actionson
documentation and GitHub Actionspush
documentation for a full overview. (ex:on: push: branches: main
) - Create a GitHub Actions
job
. (ex:jobs: benchmark_base_branch
) - Set the type of machine the job will run on.
See the GitHub Actions
runs-on
documentation for a full overview. (ex:runs-on: ubuntu-latest
) - Checkout your base branch source code.
(ex:
uses: actions/checkout@v4
) - Install the Bencher CLI using the GitHub Action.
(ex:
uses: bencherdev/bencher@main
) - Use the
bencher run
CLI subcommand to run yourmain
branch benchmarks. See thebencher run
CLI subcommand for a full overview. (ex:bencher run
) - Set the
--project
option to the Project slug. See the--project
docs for more details. (ex:--project save-walter-white-1234abcd
) - Set the
--token
option to theBENCHER_API_TOKEN
Repository secret. See the--token
docs for more details. (ex:--token '${{ secrets.BENCHER_API_TOKEN }}'
) - Set the
--branch
option to the Branch name. See branch selection for a full overview. (ex:--branch main
) - Set the
--testbed
option to the Testbed name. This should likely match the machine selected inruns-on
. See the--tested
docs for more details. (ex:--testbed ubuntu-latest
) - Set the
--adapter
option to the desired benchmark harness adapter. See benchmark harness adapters for a full overview. (ex:--adapter json
) - Set the
--err
flag to fail the command if an Alert is generated. See Threshold & Alerts for a full overview. (ex:--err
) - Specify the benchmark command arguments.
See benchmark command for a full overview.
(ex:
bencher mock
)
Pull Requests
In order to catch performance regression in Pull Requests, you will need to run your benchmarks on PRs.
If you only expect to have PRs from branches within the same repository
then you can simply create another workflow to run on
pull_request
events from the same repository.
⚠️ This solution only works if all PRs are from the same repository! See Pull Requests from Forks below.
-
Create a GitHub Actions
workflow
file. (ex:.github/workflows/pr_benchmarks.yml
) -
Run on
pull_request
events:opened
- A pull request was created.reopened
- A previously closed pull request was reopened.edited
- The title or body of a pull request was edited, or the base branch of a pull request was changed.synchronize
- A pull request’s head branch was updated. For example, the head branch was updated from the base branch or new commits were pushed to the head branch.
See the GitHub Actions
on
documentation and GitHub Actionspull_request
documentation for a full overview. (ex:on: pull_request: types: [opened, reopened, edited, synchronize]
) -
Create a GitHub Actions
job
. (ex:jobs: benchmark_pr_branch
) -
Run on
pull_request
events if and only if the pull request is from the same repository. ⚠️ DO NOT REMOVE THIS LINE! For handling Fork PRs see Pull Requests from Forks below. (ex:if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
) -
Set the permissions for the
GITHUB_TOKEN
towrite
forpull-requests
. Depending on your GitHub settings, this may not be required. But for all organizations and personal repos created after 02 Feb 2023, this is the default behavior. See the GitHub documentation for a full overview. (ex:permissions: pull-requests: write
) -
Set the type of machine the job will run on. See the GitHub Actions
runs-on
documentation for a full overview. (ex:runs-on: ubuntu-latest
) -
Checkout the PR branch source code. (ex:
uses: actions/checkout@v4
) -
Install the Bencher CLI using the GitHub Action. (ex:
uses: bencherdev/bencher@main
) -
Use the
bencher run
CLI subcommand to run your pull request branch benchmarks. See thebencher run
CLI subcommand for a full overview. (ex:bencher run
) -
Set the
--project
option to the Project slug. See the--project
docs for more details. (ex:--project save-walter-white-1234abcd
) -
Set the
--token
option to theBENCHER_API_TOKEN
Repository secret. See the--token
docs for more details. (ex:--token '${{ secrets.BENCHER_API_TOKEN }}'
) -
Set the
--branch
option to the PR branch name using the GitHub Actionsgithub
context. See branch selection for a full overview. (ex:--branch '${{ github.head_ref }}'
) -
Set the
--branch-start-point
option to the PR base Branch start point using the GitHub Actionsgithub
context. See branch selection for a full overview. (ex:--branch-start-point '${{ github.base_ref }}'
) -
Set the
--branch-start-point-hash
option to the PR base Branch start point hash using the GitHub Actionspull_request
event. See branch selection for a full overview. (ex:--branch-start-point-hash '${{ github.event.pull_request.base.sha }}'
) -
Set the
--branch-reset
flag to always reset the Branch to the start point. This will prevent benchmark data drift. See branch selection for a full overview. (ex:--branch-reset
) -
Set the
--testbed
option to the Testbed name. This should likely match the machine selected inruns-on
. See the--tested
docs for more details. (ex:--testbed ubuntu-latest
) -
Set the
--adapter
option to the desired benchmark harness adapter. See benchmark harness adapters for a full overview. (ex:--adapter json
) -
Set the
--err
flag to fail the command if an Alert is generated. See Threshold & Alerts for a full overview. (ex:--err
) -
Set the
--github-actions
option to the GitHub API authentication token to post results as a comment on the Pull Request using the GitHub ActionsGITHUB_TOKEN
environment variable. See the--github-actions
docs for more details. (ex:--github-actions '${{ secrets.GITHUB_TOKEN }}'
) -
Specify the benchmark command arguments. See benchmark command for a full overview. (ex:
bencher mock
)
Pull Requests from Forks
If you plan to accept pull requests from forks, as is often the case in public open source projects,
then you will need to handle things a little differently.
For security reasons, secrets such as your BENCHER_API_TOKEN
and the GITHUB_TOKEN
are not available in GitHub Actions for fork PRs.
That is if an external contributor opens up a PR from a fork the above example will not work.
There are two options for fork PRs:
- ⛑️ Safer: Benchmark Fork PR and Upload from Default Branch
- ⚠️ Riskier: Benchmark Fork PR from Target Branch with Required Reviewers
See this GitHub Security Lab write up and this blog post on preventing pwn requests for a full overview.
Benchmark Fork PR and Upload from Default Branch
This is the safe and suggested way to add Continuous Benchmarking to fork pull requests.
It requires two separate workflows.
The first workflow runs and caches the benchmark results in the pull_request
context.
No secrets such as your BENCHER_API_TOKEN
and the GITHUB_TOKEN
are available there.
Then a second workflow downloads the cached benchmark results in the workflow_run
context and uploads them to Bencher.
This works because workflow_run
runs in the context of the repository’s default branch,
where secrets such as your BENCHER_API_TOKEN
and the GITHUB_TOKEN
are available.
The pull request number, head branch, and base branch used in the initial pull_request
workflow
must also be explicitly passed into the workflow_run
workflow since they are not available there.
These workflows will only run if they exist on the default branch.
See using data from the triggering workflow for a full overview.
-
Create a first GitHub Actions
workflow
file. (ex:.github/workflows/run_fork_pr_benchmarks.yml
) -
Name this workflow so it can be referenced by the second workflow. (ex:
name: Run and Cache Benchmarks
) -
Run on
pull_request
events:opened
- A pull request was created.reopened
- A previously closed pull request was reopened.edited
- The title or body of a pull request was edited, or the base branch of a pull request was changed.synchronize
- A pull request’s head branch was updated. For example, the head branch was updated from the base branch or new commits were pushed to the head branch.
See the GitHub Actions
on
documentation and GitHub Actionspull_request
documentation for a full overview. (ex:on: pull_request: types: [opened, reopened, edited, synchronize]
) -
Create a GitHub Actions
job
. (ex:jobs: benchmark_fork_pr_branch
) -
Set the type of machine the job will run on. See the GitHub Actions
runs-on
documentation for a full overview. (ex:runs-on: ubuntu-latest
) -
Checkout the fork PR branch source code. (ex:
uses: actions/checkout@v4
) -
Run your benchmarks and save the results to a file. (ex:
/bin/echo '{ ... }' > benchmark_results.json
) -
Upload the benchmark results file as an artifact. (ex:
uses: actions/upload-artifact@v4
) -
Upload the
pull_request
event object as an artifact. (ex:uses: actions/upload-artifact@v4
)
- Create a first GitHub Actions
workflow
file. (ex:.github/workflows/track_fork_pr_benchmarks.yml
) - Name this workflow second workflow.
(ex:
name: Track Benchmarks with Bencher
) - Chain the two workflows with
the
workflow_run
event. (ex:on: workflow_run: ...
) - Create a GitHub Actions
job
. (ex:jobs: track_fork_pr_branch
) - Only run this job if the previous workflow’s conclusion was a success using
the GitHub Actions
workflow_run
event. (ex:if: github.event.workflow_run.conclusion == 'success'
) - Set the type of machine the job will run on.
See the GitHub Actions
runs-on
documentation for a full overview. (ex:runs-on: ubuntu-latest
) - Set the benchmark results and
pull_request
event object file names as environment variables. (ex:env: ...
) - Download the cached benchmark results and
pull_request
event. (ex:uses: actions/github-script@v6
) - Extract the cached benchmark results and
pull_request
event. (ex:unzip ...
) - Export the necessary data from the
pull_request
event as environment variables. (ex:core.exportVariable(...)
) - Install the Bencher CLI using the GitHub Action.
(ex:
uses: bencherdev/bencher@main
) - Use the
bencher run
CLI subcommand to track your fork pull branch benchmarks. See thebencher run
CLI subcommand for a full overview. (ex:bencher run
) - Set the
--project
option to the Project slug. See the--project
docs for more details. (ex:--project save-walter-white-1234abcd
) - Set the
--token
option to theBENCHER_API_TOKEN
Repository secret. See the--token
docs for more details. (ex:--token '${{ secrets.BENCHER_API_TOKEN }}'
) - Set the
--branch
option to the formatted fork PR number using the GitHub Actionspull_request
event. See branch selection for a full overview. (ex:--branch '${{ env.PR_HEAD }}'
) - Set the
--branch-start-point
option to the fork PR base Branch start point using the GitHub Actionspull_request
event. See branch selection for a full overview. (ex:--branch-start-point '${{ env.PR_BASE }}'
) - Set the
--branch-start-point-hash
option to the fork PR base Branch start point hash using the GitHub Actionspull_request
event. See branch selection for a full overview. (ex:--branch-start-point-hash '${{ env.PR_BASE_SHA }}'
) - Set the
--branch-reset
flag to always reset the Branch to the start point. This will prevent benchmark data drift. See branch selection for a full overview. (ex:--branch-reset
) - Set the
--testbed
option to the Testbed name. This should likely match the machine selected inruns-on
. See the--tested
docs for more details. (ex:--testbed ubuntu-latest
) - Set the
--adapter
option to the desired benchmark harness adapter. See benchmark harness adapters for a full overview. (ex:--adapter json
) - Set the
--err
flag to fail the command if an Alert is generated. See Threshold & Alerts for a full overview. (ex:--err
) - Set the
--github-actions
option to the GitHub API authentication token to post results as a comment on the Pull Request using the GitHub ActionsGITHUB_TOKEN
environment variable. See the--github-actions
docs for more details. (ex:--github-actions '${{ secrets.GITHUB_TOKEN }}'
) - Set the
--ci-number
option to the pull request number. See the--ci-number
docs for more details. (ex:--ci-number '${{ env.PR_NUMBER }}'
) - Set the
--file
option to the benchmark results file path. See benchmark command for a full overview. (ex:--file "$BENCHMARK_RESULTS"
)
Benchmark Fork PR from Target Branch with Required Reviewers
In order to guarantee that the code from a fork pull request is safe, this GitHub Action checks to see if the fork is from another repository. If the fork is from another repository, then it will need to be reviewed.
⚠️ It is very, very important to thoroughly review every fork PR before approving! Not doing so could result in a pwn request!
If you would prefer to not have that hanging over your head, see Benchmark Fork PR and Upload from Default Branch above.
In order to get this workflow configured, you need to create two
GitHub Actions Environments.
Navigate to Your Repo -> Settings -> Environments -> New environment
.
Create two new environments, internal
and external
.
The internal
environment should have no Deployment protection rules
.
However, the external
environment should have Required reviewers
set to those trusted to review fork PRs before benchmarking.
See this blog post for a full overview.
This setup works because pull_request_target
runs in the context of the pull request’s target branch,
where secrets such as your BENCHER_API_TOKEN
and the GITHUB_TOKEN
are available.
Therefore, this workflow will only run if it exists on the target branch.
Avoid setting any secrets as environment variables, such as GITHUB_TOKEN
and BENCHER_API_TOKEN
.
Instead explicitly pass in your secrets to bencher run
.
-
Create a GitHub Actions
workflow
file. (ex:.github/workflows/pr_target_benchmarks.yml
) -
Run on
pull_request
events:opened
- A pull request was created.reopened
- A previously closed pull request was reopened.edited
- The title or body of a pull request was edited, or the base branch of a pull request was changed.synchronize
- A pull request’s head branch was updated. For example, the head branch was updated from the base branch or new commits were pushed to the head branch.
See the GitHub Actions
on
documentation and GitHub Actionspull_request
documentation for a full overview. (ex:on: pull_request: types: [opened, reopened, edited, synchronize]
) -
Create a first GitHub Actions
job
to check if the workflow requires review. (ex:jobs: fork_pr_requires_review
) -
Set the
environment
tointernal
if and only if the pull request is from the same repository. Otherwise, set theenvironment
toexternal
, which will require an approval from a reviewer to continue on. ⚠️ DO NOT REMOVE THIS LINE! (ex:environment: ${{ (github.event.pull_request.head.repo.full_name == github.repository && 'internal') || 'external' }}
) -
Create a second GitHub Actions
job
to run your benchmarks. (ex:benchmark_fork_pr_branch
) -
Have the
benchmark_fork_pr_branch
job need thefork_pr_requires_review
job in order to run. ⚠️ DO NOT REMOVE THIS LINE! See the GitHub Actionsneeds
documentation for a full overview. (ex:needs: fork_pr_requires_review
) -
Set the type of machine the job will run on. See the GitHub Actions
runs-on
documentation for a full overview. (ex:runs-on: ubuntu-latest
) -
Checkout the fork PR source code. Since
pull_request_target
runs in the context of the pull request’s target branch, you still need to checkout the pull request branch. (ex:uses: actions/checkout@v4
)- Specify the fork PR repository (ex:
repository: ${{ github.event.pull_request.head.repo.full_name }}
) - Specify the fork PR hash (ex:
ref: ${{ github.event.pull_request.head.sha }}
) - Do not persist your
git
credential (ex:persist-credentials: false
)
- Specify the fork PR repository (ex:
-
Install the Bencher CLI using the GitHub Action. (ex:
uses: bencherdev/bencher@main
) -
Use the
bencher run
CLI subcommand to run your fork pull branch benchmarks. See thebencher run
CLI subcommand for a full overview. (ex:bencher run
) -
Set the
--project
option to the Project slug. See the--project
docs for more details. (ex:--project save-walter-white-1234abcd
) -
Set the
--token
option to theBENCHER_API_TOKEN
Repository secret. See the--token
docs for more details. (ex:--token '${{ secrets.BENCHER_API_TOKEN }}'
) -
Set the
--branch
option to the formatted fork PR number using the GitHub Actionspull_request
event. See branch selection for a full overview. (ex:--branch '${{ github.event.number }}/merge'
) -
Set the
--branch-start-point
option to the fork PR base Branch start point using the GitHub Actionsgithub
context. See branch selection for a full overview. (ex:--branch-start-point '${{ github.base_ref }}'
) -
Set the
--branch-start-point-hash
option to the fork PR base Branch start point hash using the GitHub Actionspull_request
event. See branch selection for a full overview. (ex:--branch-start-point-hash '${{ github.event.pull_request.base.sha }}'
) -
Set the
--branch-reset
flag to always reset the Branch to the start point. This will prevent benchmark data drift. See branch selection for a full overview. (ex:--branch-reset
) -
Set the
--testbed
option to the Testbed name. This should likely match the machine selected inruns-on
. See the--tested
docs for more details. (ex:--testbed ubuntu-latest
) -
Set the
--adapter
option to the desired benchmark harness adapter. See benchmark harness adapters for a full overview. (ex:--adapter json
) -
Set the
--err
flag to fail the command if an Alert is generated. See Threshold & Alerts for a full overview. (ex:--err
) -
Set the
--github-actions
option to the GitHub API authentication token to post results as a comment on the Pull Request using the GitHub ActionsGITHUB_TOKEN
environment variable. See the--github-actions
docs for more details. (ex:--github-actions '${{ secrets.GITHUB_TOKEN }}'
) -
Specify the benchmark command arguments. See benchmark command for a full overview. (ex:
bencher mock
)
🐰 Congrats! You have learned how to use Bencher in GitHub Actions! 🎉