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のドキュメンテーションをご覧ください。また、以下のプルリクエストもご覧ください。
  3. GitHub Actionsのjobを作成します(例:benchmark_with_bencher)。
  4. プロジェクトはすでに存在している必要があります。--projectフラグを設定するか、BENCHER_PROJECT環境変数をプロジェクトのスラッグまたはUUIDに設定します(例:BENCHER_PROJECT: save-walter-white)。
  5. オプション:--testbedフラグを設定するか、BENCHER_TESTBED環境変数をTestbedのスラッグまたはUUIDに設定します(例:BENCHER_TESTBED: ubuntu-latest)。Testbedは必ずすでに存在している必要があります。これが設定されていない場合、localhostのTestbedが使用されます。
  6. オプション:--adapterフラグを設定するか、BENCHER_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のスラッグまたは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. アラートが生成された場合にコマンドを失敗させるよう設定します(例:--err)。アラートが生成されるためには、閾値がすでに存在している必要があります。
    4. あなたのベンチマークを実行し、結果からレポートを生成します(例:"bencher mock")。

プルリクエスト

プルリクエストにおいてパフォーマンスの回帰をキャッチするためには、PRsでベンチマークを実行する必要があります。 PRsが同一のリポジトリからのものだけであると想定している場合、上記の例を修正してon pull_requestイベントも実行することができます。

⚠️ この解決策は、すべてのPRsが同一のリポジトリからのものである場合にのみ動作します! 下記のフォークからのプルリクエスト を参照してください。

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されたPRの処理については、以下のフォークからのプルリクエストを参照してください。
  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. --ci-* フラグでプルリクエストのコメントを設定するすべての方法については、bencher runドキュメンテーションを全て覧下さい。
  7. (表示されていません) 2つ目のGitHub Actions workflow ファイルを作成し、上記の初期例を使用してmainブランチへのpushイベントで実行します。(例: .github/workflows/benchmarks.yml)

フォークからのプルリクエスト

公開のオープンソースプロジェクトではよくあるように、フォークからのプルリクエストを受け入れるつもりなら、少し違う方法で対応する必要があります。 セキュリティ上の理由から、BENCHER_API_TOKENGITHUB_TOKENなどのシークレットは、フォークのPRでのGitHub Actionsでは利用できません。 つまり、外部の貢献者がフォークからPRを開いた場合、上記の例は機能しません。 フォークのPRには以下の2つのオプションがあります:

必須レビュアーを有するターゲットブランチからのフォークPRベンチマーク

⚠️ フォーク PRを承認する前には、非常に非常に徹底的にレビューすることが重要です! そうしないと、pwnリクエストの発生を招く可能性があります!

そんなリスクを背負いたくない場合は、以下のデフォルトブランチからのフォーク 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. on pull_request_targetイベントで実行します。
  3. 各フォークのプルリクエスト(external)の実行前に、必須のレビュアーからの承認が必要なfork_pr_requires_reviewという名前のjobを作成します。
  4. fork_pr_requires_reviewに依存する第三のjobを作成します。
    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がプルリクエストのターゲットブランチのコンテキストで実行されるため動作します。ここでは、BENCHER_API_TOKENGITHUB_TOKENなどのシークレットが利用可能です。したがって、このワークフローは、それがターゲットブランチ上に存在する場合にのみ実行されます。

これを設定するためには、2つのGitHub Action Environmentを作成する必要があります(例:Repo -> Settings -> Environments -> New environment)。internal環境にはDeployment protection rulesはありません。しかし、external環境には、フォークのPRのベンチマークする前に承認が必要な「必要なレビュアー」を設定します。

PRのブランチ名(head ref)をシングル引用符で囲むことが非常に重要です(例:--if-branch '${{ github.head_ref }}')。そうしなければ、攻撃者は悪意のあるブランチ名を作成してコマンドインジェクションを行うことができます。信頼できない入力からpwnリクエストを防止する方法については、GitHub Security Labの書き起こしを参照してください。

GITHUB_TOKENBENCHER_API_TOKENなどのシークレットを環境変数として設定することは避けてください。 代わりに、bencher runにシークレットを明示的に渡します(例:--token "${{ secrets.BENCHER_API_TOKEN }}")。 pwnリクエストを防止する方法については、GitHub Security Labの書き起こし およびこのブログ投稿 を参照してください。

デフォルトブランチからのフォーク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. ベンチマーク結果のファイルをアーティファクトとしてアップロードします。
  5. 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 eventを使用してTrack BenchmarksRun and Cache Benchmarksに連鎖させます。
  3. キャッシュされたベンチマーク結果とpull_requestイベントをダウンロードします。
  4. キャッシュされたベンチマーク結果とpull_requestイベントを抽出します。
  5. pull_requestイベントから必要なデータを環境変数としてエクスポートします。
  6. bencher runでキャッシュされたベンチマーク結果を追跡します:
    1. すべての信頼できない入力を一重引用符で囲みます(例:--if-branch '${{ env.PR_HEAD }}')。
    2. プルリクエスト番号を明示的に渡します(例:--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などのシークレットが利用可能です。したがって、これらのワークフローは、_default_ブランチ上に存在する場合にのみ実行されます。トリガリングワークフローからのデータの使用の全体概要を参照してください。初期のワークフローで使用されたプルリクエスト番号、ヘッドブランチ、ベースブランチは、workflow_run内では利用できないため、明示的に渡す必要があります。

PRのブランチ名(head ref)をシングル引用符で囲むことが非常に重要です(例:--if-branch '${{ env.PR_HEAD }}')。そうしなければ、攻撃者は悪意のあるブランチ名を作成してコマンドインジェクションを行うことができます。信頼できない入力からpwnリクエストを防止する方法については、GitHub Security Labの書き起こしを参照してください。

Run and Cache Benchmarksワークフローファイルでシークレットを環境変数として設定することは避けてください。 pwnリクエストの防止 およびこのブログ投稿 で詳しく説明しています。



🐰 おめでとうございます! GitHub ActionsでBencherの使い方を学びました! 🎉


次に進む:ベンチマーキングの概要 ➡

🤖 このドキュメントは OpenAI GPT-4 によって自動的に生成されました。 正確ではない可能性があり、間違いが含まれている可能性があります。 エラーを見つけた場合は、GitHub で問題を開いてください。.