Como usar o Bencher no GitHub Actions


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. Crie um arquivo workflow do GitHub Actions. (ex .github/workflows/benchmarks.yml)
  2. Execute em eventos push para a branch main. Veja a documentação on do GitHub Actions para uma visão geral completa. Consulte também Pull Requests abaixo.
  3. Crie um job do GitHub Actions. (ex benchmark_with_bencher)
  4. O Projeto já deve existir. Defina a flag --project ou a variável de ambiente BENCHER_PROJECT para o slug do Projeto ou UUID (ex: BENCHER_PROJECT: save-walter-white).
  5. Opcional: Defina a flag --testbed ou a variável de ambiente BENCHER_TESTBED para o slug Testbed ou UUID. (ex BENCHER_TESTBED: ubuntu-latest) O Testbed deve já existir. Se não for definido, então o Testbed localhost será usado.
  6. Defina a flag --adapter ou a variável de ambiente BENCHER_ADAPTER para o nome do adaptador desejado. (ex BENCHER_ADAPTER: json) Se isso não for definido, então o adaptador magic será usado. Consulte adaptadores de sistema de referência para uma visão geral completa.
  7. Faça checkout do seu código fonte. (ex uses: actions/checkout@v4)
  8. Instale o CLI do Bencher usando a Ação do GitHub. (ex uses: bencherdev/bencher@main)
  9. Rastreie seus benchmarks com o subcomando bencher run do CLI:
    1. Opcional: Configure a flag --branch ou a variável de ambiente BENCHER_BRANCH para o slug da Branch ou UUID. (ex: --branch main) A Branch deve já existir. Se isso não for definido, então a Branch main será usada.
    2. O token API já deve existir. Adicione BENCHER_API_TOKEN como um segredo do Repositório. (ex: Repo -> Configurações -> Segredos e variáveis -> Ações -> Novo segredo de repositório) Defina a flag --token ou a variável de ambiente BENCHER_API_TOKEN para o token API. (ex: --token ${{ secrets.BENCHER_API_TOKEN }})
    3. Configure o comando para falhar se um Alerta for gerado. (ex: --err) Para que um Alerta seja gerado, um Limiar já deve existir.
    4. Execute seus benchmarks e gere um Relatório a partir dos resultados. (ex: "bencher mock")

Pull Requests

Para identificar regressões de desempenho em Pull Requests, você precisa executar seus benchmarks em PRs. Se você espera apenas ter PRs de branches dentro do mesmo repositório, você pode simplesmente modificar o exemplo acima para também executar on eventos pull_request.

⚠️ Esta solução funciona apenas se todas as PRs forem do mesmo repositório! Veja Pull Requests de Forks abaixo.

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. Crie um arquivo workflow do GitHub Actions. (ex: .github/workflows/pr_benchmarks.yml)
  2. Execute em eventos pull_request apenas se o pull request for do mesmo repositório. Para lidar com Pull Requests de Forks, veja Pull Requests de Forks abaixo.
  3. Defina as permissões para o GITHUB_TOKEN como write para pull-requests. Dependendo das suas configurações do GitHub, isso pode não ser necessário. No entanto, para todas as organizações e repositórios pessoais criados após 02 de fevereiro de 2023, esse é o comportamento padrão. Consulte a documentação do GitHub para uma visão geral completa.
  4. Selecione a branch para usar:
    1. Use os dados da branch atual se já existirem. (ex: --if-branch "$GITHUB_REF_NAME")
    2. Crie um clone dos dados e limites da branch alvo do PR, se existirem. (ex: --else-if-branch "$GITHUB_BASE_REF")
    3. Caso contrário, crie uma cópia dos dados e limites da branch main. (ex: --else-if-branch main)
  5. Defina o token de autenticação da API do GitHub. (ex: --github-actions "${{ secrets.GITHUB_TOKEN }}") Quando esta opção é definida como parte de um pull request, os resultados serão adicionados ao pull request como um comentário. Isso usa a variável de ambiente GITHUB_TOKEN das GitHub Actions.
  6. Veja a documentação de execução do bencher para uma visão geral completa de todas as maneiras de configurar o comentário do pull request com as flags --ci-*.
  7. (Não mostrado) Crie um segundo arquivo workflow de GitHub Actions e use o exemplo inicial acima para executar em eventos push para a branch main. (ex: .github/workflows/benchmarks.yml)

Pull Requests de Forks

Se você planeja aceitar solicitações de pull de forks, como é frequentemente o caso em projetos de código aberto, então você precisará lidar com as coisas de maneira um pouco diferente. Por razões de segurança, segredos como seu BENCHER_API_TOKEN e o GITHUB_TOKEN não estão disponíveis nas GitHub Actions para PRs de forks. Ou seja, se um contribuidor externo abrir um PR a partir de um fork, o exemplo acima não funcionará. Existem duas opções para PRs de forks:

Benchmark de PR de Fork na Branch Alvo com Revisores Necessários

⚠️ É muito, muito importante revisar completamente qualquer PR de um fork antes de aprovar! Não fazer isso pode resultar em uma solicitação de pwn!

Se você preferir não ter isso pendente, veja Benchmark Fork PR e Upload a partir do Branch padrão abaixo.

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. Crie um arquivo workflow do GitHub Actions. (ex: .github/workflows/pr_benchmarks.yml)
  2. Execute em eventos pull_request_target.
  3. Crie um job chamado fork_pr_requires_review que requer aprovação de um Required Reviewer antes de cada execução de pull request do fork (external).
  4. Crie um terceiro job que depende de fork_pr_requires_review.
    1. Faça checkout da branch do pull request, mas não persista as credenciais do git. (ex: persist-credentials: false)
    2. Use aspas simples em torno de todas as entradas não confiáveis. (ex: --if-branch '${{ github.head_ref }}')
    3. Passe todos os segredos diretamente. (ex: --token "${{ secrets.BENCHER_API_TOKEN }}")
    4. Execute e rastreie seus benchmarks de pull request com bencher run.
  5. (Não demonstrado) Crie um segundo arquivo workflow das GitHub Actions e use o exemplo inicial acima para executar em eventos push para a branch main. (ex: .github/workflows/benchmarks.yml)

Esta configuração funciona porque pull_request_target é executado no contexto da branch de destino da solicitação de pull, onde segredos como seu BENCHER_API_TOKEN e o GITHUB_TOKEN estão disponíveis. Portanto, este fluxo de trabalho só será executado se existir na branch alvo.

Para configurar isso, você precisa criar dois GitHub Action Environments (ex: Repo -> Configurações -> Ambientes -> Novo ambiente). O ambiente internal não deve ter Regras de proteção de deploy. No entanto, o ambiente external deve ter Revisores necessários definido para aqueles confiáveis para revisar PRs de fork antes de fazer o benchmark.

É muito importante colocar o nome da branch PR (head ref) entre aspas simples. (ex: --if-branch '${{ github.head_ref }}') Caso contrário, um atacante poderia criar uma branch com um nome malicioso que executa a injeção de comando. Veja este esboço do GitHub Security Lab sobre a prevenção de solicitações pwn de entrada não confiável para uma visão geral completa.

Evite definir quaisquer segredos como variáveis de ambiente, como GITHUB_TOKEN e BENCHER_API_TOKEN. Passe explicitamente os segredos para bencher run. (ex --token "${{ secrets.BENCHER_API_TOKEN }}") Veja este esboço do GitHub Security Lab e este post de blog sobre como prevenir solicitações pwn para uma visão geral completa.

Benchmark de PR de Fork e Upload da Branch Default

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. Crie um arquivo de workflow Run and Cache Benchmarks. (ex: .github/workflows/pr_benchmarks.yml)
  2. Execute todos os jobs no workflow em eventos pull_request.
  3. Execute os benchmarks e salve os resultados em um arquivo. (ex: benchmark_results.json)
  4. Carregue o arquivo de resultados do benchmark como um artefato.
  5. Carregue o objeto de evento pull_request como um artefato.
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. Crie um segundo arquivo de workflow, Track Benchmarks. (ex: .github/workflows/track_benchmarks.yml)
  2. Encadeie Track Benchmarks para Run and Cache Benchmarks com o evento workflow_run.
  3. Baixe os resultados de benchmark em cache e o evento pull_request.
  4. Extraia os resultados de benchmark em cache e o evento pull_request.
  5. Exporte os dados necessários do evento pull_request como variáveis de ambiente.
  6. Rastreie os resultados de benchmark em cache com bencher run:
    1. Use aspas simples em torno de todas as entradas não confiáveis. (ex: --if-branch '${{ env.PR_HEAD }}')
    2. Passe explicitamente o número da solicitação de pull. (ex: --ci-number '${{ env.PR_NUMBER }}')
    3. Passe o caminho do arquivo para o arquivo de resultados de benchmark. (ex: --file "$BENCHMARK_RESULTS")
  7. (Não demonstrado) Crie um terceiro arquivo workflow das GitHub Actions e use o exemplo inicial acima para executar em eventos push para a branch main. (ex: .github/workflows/benchmarks.yml)

Este setup funciona porque workflow_run é executado no contexto da branch padrão do repositório, onde segredos como seu BENCHER_API_TOKEN e o GITHUB_TOKEN estão disponíveis. Portanto, esses fluxos de trabalho só serão executados se existirem na branch padrão. Veja usando dados do fluxo de trabalho de acionamento para uma visão geral completa. O número da pull request, a branch principal e a branch base usadas no fluxo de trabalho inicial devem ser passadas explicitamente, pois não estão disponíveis dentro do workflow_run.

É muito importante colocar o nome da branch PR (head ref) entre aspas simples. (ex: --if-branch '${{ env.PR_HEAD }}') Caso contrário, um atacante poderia criar uma branch com um nome malicioso que executa a injeção de comando. Veja este esboço do GitHub Security Lab sobre a prevenção de solicitações pwn de entrada não confiável para uma visão geral completa.

Evite definir quaisquer segredos como variáveis de ambiente no arquivo de workflow Run and Cache Benchmarks. Veja este esboço do GitHub Security Lab e este post de blog sobre como prevenir solicitações pwn para uma visão geral completa.



🎉 Parabéns! Você aprendeu a usar o Bencher nas GitHub Actions! 🐰


Continuar: Análise de Desempenho ➡

🤖 Este documento foi gerado automaticamente pelo OpenAI GPT-4. Pode não ser preciso e pode conter erros. Se você encontrar algum erro, abra um problema no GitHub.