如何在GitHub Actions中使用Bencher


on:
push:
branches: main
jobs:
benchmark_with_bencher:
name: Continuous Benchmarking with Bencher
runs-on: ubuntu-latest
env:
BENCHER_PROJECT: save-walter-white
BENCHER_TESTBED: ubuntu-latest
BENCHER_ADAPTER: json
steps:
- uses: actions/checkout@v4
- uses: bencherdev/bencher@main
- name: Track Benchmarks with Bencher
run: |
bencher run \
--branch "$GITHUB_REF_NAME" \
--token "${{ secrets.BENCHER_API_TOKEN }}" \
--err \
"bencher mock"
  1. 创建一个GitHub Actions workflow 文件。 (例如:.github/workflows/benchmarks.yml)
  2. 在对main分支的 push事件上运行。查看GitHub Actions on Documentation以获得完整概览。也可以查看下面的Pull Requests
  3. 创建一个GitHub Actions job。 (例如: benchmark_with_bencher)
  4. 项目必须已经存在。设置--project标志或BENCHER_PROJECT环境变量为项目的slug或UUID (例如:BENCHER_PROJECT: save-walter-white)。
  5. 可选:设置 --testbed 参数或 BENCHER_TESTBED环境变量为Testbed的slug或UUID. (例如:BENCHER_TESTBED: ubuntu-latest)Testbed 必须事先存在。如果没有设置,那么localhost Testbed 会被默认使用。
  6. 可选:设置 --adapter 参数或BENCHER_ADAPTER 环境变量为想要的adapter名字。 (例如:BENCHER_ADAPTER: json) 如果没有设置,则会使用magic适配器。查看 基准测试适配器以获取完全概览。
  7. 检出你的源代码 (例如: uses: actions/checkout@v4
  8. 使用 GitHub Action 安装Bencher CLI。(例如:uses: bencherdev/bencher@main
  9. 通过 bencher run CLI子命令 跟踪你的基准测试
    1. 可选:设置 --branch 标志或BENCHER_BRANCH环境变量为Branch的slug或UUID。(例如:--branch main)Branch必须事先存在。如果没有设置,那么main Branch会被默认使用。
    2. API令牌必须已经存在。将BENCHER_API_TOKEN添加为仓库秘密。 (例如: Repo -> Settings -> Secrets and variables -> Actions -> New repository secret) 设置--token标志或BENCHER_API_TOKEN环境变量为API令牌。(例如:--token ${{ secrets.BENCHER_API_TOKEN }})
    3. 如果生成Alert,设置命令为失败。(例如:--err)Alert的生成需要事先存在一个阈值
    4. 运行你的基准测试并从结果中生成报告。 (例如:"bencher mock")

Pull请求

为了在Pull请求中抓取性能回归,你需要在PRs中运行你的基准测试。 如果只期望从同一个仓库的分支中取得PRs,以下示例提供修改然后运行on pull_request事件的方案。

⚠️ 这个解决方案只有在所有PRs都来自同一个仓库时才能工作! 查看下面的 Forks的Pull请求

on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
benchmark_pr_with_bencher:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
permissions:
pull-requests: write
name: Continuous Benchmarking with Bencher
runs-on: ubuntu-latest
env:
BENCHER_PROJECT: save-walter-white
BENCHER_TESTBED: ubuntu-latest
BENCHER_ADAPTER: json
steps:
- uses: actions/checkout@v4
- uses: bencherdev/bencher@main
- name: Track Benchmarks with Bencher
run: |
bencher run \
--if-branch "$GITHUB_REF_NAME" \
--else-if-branch "$GITHUB_BASE_REF" \
--else-if-branch main \
--github-actions "${{ secrets.GITHUB_TOKEN }}" \
--token "${{ secrets.BENCHER_API_TOKEN }}" \
--err \
"bencher mock"
  1. 创建一个GitHub Actions workflow 文件。(例如:.github/workflows/pr_benchmarks.yml
  2. 仅当来自同一仓库的拉取请求(pull_request)事件发生时运行。对于处理Fork PRs,请看下面的从Forks的Pull请求
  3. GITHUB_TOKEN 设置权限,将 pull-requests 的权限设为 write。根据您的 GitHub 设置,这可能并非必需。但是,对所有在2023年2月2日之后建立的组织和个人仓库来说,这是默认行为。查看GitHub 文档以获取完整概览。
  4. 选择分支来使用:
    1. 如果当前分支数据已经存在,则使用当前分支数据。(例如:--if-branch "$GITHUB_REF_NAME"
    2. 如果PR目标分支的数据和阈值已经存在,则创建一个克隆。(例如:--else-if-branch "$GITHUB_BASE_REF"
    3. 否则,请创建main分支数据和阈值的克隆。(例如:--else-if-branch main
  5. 设置GitHub API身份验证令牌。(例如:--github-actions "${{ secrets.GITHUB_TOKEN }}")当作为拉取请求的一部分设置此选项时,结果将以评论的形式添加到拉取请求中。这使用了GitHub Actions GITHUB_TOKEN 环境变量
  6. 查看bencher run文档,全面了解如何使用--ci-*标志配置拉取请求评论的所有方式。
  7. (未显示)创建第二个GitHub Actions workflow 文件,并使用上述初始示例在push事件发生到main分支时运行。(例如:.github/workflows/benchmarks.yml

Forks的Pull请求

如果你计划接受来自forks的pull请求, 在公共开源项目中这经常是必要的,那么你需要使用一个稍微不同的处理方式。 出于安全原因,你的BENCHER_API_TOKENGITHUB_TOKEN这样的秘密在forks PRs的GitHub Actions中是无法获取的。 也就是说,如果一个外部贡献者从一个fork打开一个PR,上面的例子就不能工作了。 对于fork PRs有两个选项:

有需求评审者的目标分支之中的Fork PR预览

⚠️ 审批任何 fork PR 之前,非常非常 重要的是要彻底的进行审核! 如果没有做到这一点,可能会导致一个 pwn 请求!

如果你不希望面对这种压力,可以查看下面的 从默认分支对 Fork PR 进行基准测试并上传

on:
pull_request_target:
types: [opened, reopened, synchronize]
jobs:
fork_pr_requires_review:
environment: ${{ (github.event.pull_request.head.repo.full_name == github.repository && 'internal') || 'external' }}
runs-on: ubuntu-latest
steps:
- run: true
benchmark_pr_with_bencher:
needs: fork_pr_requires_review
name: Benchmark PR with Bencher
runs-on: ubuntu-latest
env:
BENCHER_PROJECT: save-walter-white
BENCHER_ADAPTER: json
BENCHER_TESTBED: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
persist-credentials: false
- uses: bencherdev/bencher@main
- name: Track Benchmarks with Bencher
run: |
bencher run \
--if-branch '${{ github.event.number }}/merge' \
--else-if-branch '${{ github.base_ref }}' \
--else-if-branch main \
--github-actions "${{ secrets.GITHUB_TOKEN }}" \
--token "${{ secrets.BENCHER_API_TOKEN }}" \
--err \
"bencher mock"
  1. 创建一个GitHub Actions workflow 文件。(例如:.github/workflows/pr_benchmarks.yml)
  2. pull_request_target事件上运行。
  3. 创建一个名为fork_pr_requires_reviewjob, 等待审查者在每个fork的pull请求(external)运行前进行审查。
  4. 创建一个依赖于fork_pr_requires_review的第三个工作。
    1. 检出请求分支,但不保留git凭据。(例如:persist-credentials: false)
    2. 对所有不可信的输入使用单引号。(例如:--if-branch '${{ github.head_ref }}')
    3. 直接传递所有的秘密。(例如:--token "${{ secrets.BENCHER_API_TOKEN }}")
    4. bencher run运行并跟踪你的请求基准测试。
  5. (未选示)创建第二个GitHub Actions workflow文件并使用初始示例在main分支的push事件上运行。(例如:.github/workflows/benchmarks.yml)

这个设置方法的原因是pull_request_target在pull请求的目标分支的上下文中运行, 在这里你的BENCHER_API_TOKENGITHUB_TOKEN是可用的。 因此,这个工作流程只有在 目标 分支上才能运行。

为了配置这个,你需要创建两个GitHub Action Environment (例如:Repo -> Settings -> Environments -> New environment). internal环境应该没有Deployment protection rules. 然而,external环境应该设置Required reviewers为那些被信任的可以在基准化测试前审查fork PRs。

单独包装PR分支名称(head ref)是非常重要的。(例如:--if-branch '${{ github.head_ref }}')否则一个攻击者创建一个恶意命名的分支可能进行命令注入。查看GitHub 安全实验室阐述关于如何预防恶意请求来自不可信输入的完全概览。

避免设置任何秘密作为环境变量,例如GITHUB_TOKENBENCHER_API_TOKEN。 而是直接向bencher run传入秘密.(例如:--token "${{ secrets.BENCHER_API_TOKEN }}") 查看GitHub安全实验室阐述这篇博客文章 关于如何预防恶意请求的完全概览。

Default分支的预览Fork PR和上传

name: Run and Cache Benchmarks
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
benchmark:
name: Run Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Mock Benchmarking
run: |
/bin/echo '{ "bencher::mock_0": { "latency": { "value": 1.0 } } }' > benchmark_results.json
- name: Upload Benchmark Results
uses: actions/upload-artifact@v4
with:
name: benchmark_results.json
path: ./benchmark_results.json
- name: Upload GitHub Pull Request Event
uses: actions/upload-artifact@v4
with:
name: event.json
path: ${{ github.event_path }}
  1. 创建一个 Run and Cache Benchmarks 工作流程文件。(例如:.github/workflows/pr_benchmarks.yml)
  2. 工作流中所有的工作在pull_request事件上运行。
  3. 运行基准测试并保存结果到一个文件。(例如:benchmark_results.json)
  4. 作为一个artifact上传基准测试结果文件。
  5. 作为一个artifact上传pull_request事件对象。
name: Track Benchmarks
on:
workflow_run:
workflows: [Run and Cache Benchmarks]
types:
- completed
jobs:
track_with_bencher:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
env:
BENCHER_PROJECT: save-walter-white
BENCHER_ADAPTER: json
BENCHER_TESTBED: ubuntu-latest
BENCHMARK_RESULTS: benchmark_results.json
PR_EVENT: event.json
steps:
- name: Download Benchmark Results
uses: actions/github-script@v6
with:
script: |
async function downloadArtifact(artifactName) {
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == artifactName
})[0];
if (!matchArtifact) {
core.setFailed(`Failed to find artifact: ${artifactName}`);
}
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/${artifactName}.zip`, Buffer.from(download.data));
}
await downloadArtifact(process.env.BENCHMARK_RESULTS);
await downloadArtifact(process.env.PR_EVENT);
- name: Unzip Benchmark Results
run: |
unzip $BENCHMARK_RESULTS.zip
unzip $PR_EVENT.zip
- name: Export PR Event Data
uses: actions/github-script@v6
with:
script: |
let fs = require('fs');
let prEvent = JSON.parse(fs.readFileSync(process.env.PR_EVENT, {encoding: 'utf8'}));
core.exportVariable("PR_HEAD", `${prEvent.number}/merge`);
core.exportVariable("PR_BASE", prEvent.pull_request.base.ref);
core.exportVariable("PR_DEFAULT", prEvent.pull_request.base.repo.default_branch);
core.exportVariable("PR_NUMBER", prEvent.number);
- uses: bencherdev/bencher@main
- name: Track Benchmarks with Bencher
run: |
bencher run \
--if-branch '${{ env.PR_HEAD }}' \
--else-if-branch '${{ env.PR_BASE }}' \
--else-if-branch '${{ env.PR_DEFAULT }}' \
--ci-number '${{ env.PR_NUMBER }}' \
--github-actions "${{ secrets.GITHUB_TOKEN }}" \
--token "${{ secrets.BENCHER_API_TOKEN }}" \
--err \
--file "$BENCHMARK_RESULTS"
  1. 创建一个第二个工作流程文件,Track Benchmarks。(例如:.github/workflows/track_benchmarks.yml)
  2. 通过the workflow_run 事件Track Benchmarks链接到Run and Cache Benchmarks
  3. 下载缓存的基准测试结果和pull_request事件。
  4. 提取缓存的基准测试结果和pull_request事件。
  5. pull_request事件中必要的数据导出为环境变量。
  6. 使用bencher run对缓存的基准测试结果进行跟踪:
    1. 对所有不可信的输入使用单引号。(例如:--if-branch '${{ env.PR_HEAD }}'
    2. 明确的传入pull请求数字。(例如:--ci-number '${{ env.PR_NUMBER }}')。
    3. 传入基准测试结果文件的文件路径。(例如:--file "$BENCHMARK_RESULTS"
  7. (未选示)创建第三个GitHub Actions workflow文件并使用初始示例在main分支的push事件上运行。(例如:.github/workflows/benchmarks.yml)

这个设置方法的原因是workflow_run在仓库的默认分支的上下文中运行, 在这里你的BENCHER_API_TOKENGITHUB_TOKEN是可用的。 因此,这些工作流程只有在默认分支上才能运行。 查看使用触发的工作流的数据以获取完全概览。 在初次工作流中的pull请求数字,头分支和基分支需要明确的传递因为他们在workflow_run中不可用。

单独包装PR分支名称(head ref)是非常重要的。(例如:--if-branch '${{ env.PR_HEAD }}')否则一个攻击者创建一个恶意命名的分支可能进行命令注入。查看GitHub 安全实验室阐述关于如何预防恶意请求来自不可信输入的完全概览。

避免在Run and Cache Benchmarks工作流程文件中设置任何秘密为环境变量。 查看GitHub安全实验室阐述这篇博客文章 关于如何预防恶意请求的完全概览。



🐰恭喜!你已经学习到了如何在GitHub Actions中使用Bencher! 🎉


继续前进: 基准测试概述 ➡

🤖 该文档由 OpenAI GPT-4 自动生成。 它可能不准确并且可能包含错误。 如果您发现任何错误,请在 GitHub 上提出问题.