Cómo usar Bencher en 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. Crea un archivo workflow de GitHub Actions. (ej: .github/workflows/benchmarks.yml)
  2. Ejecutar en eventos push a la rama main. Consulte la documentación on de GitHub Actions para obtener una visión completa. También consulte Pull Requests a continuación.
  3. Crea un job de GitHub Actions. (ej: benchmark_with_bencher)
  4. El proyecto debe existir previamente. Establece la bandera --project o la variable de entorno BENCHER_PROJECT a la representación única o UUID del proyecto (ej: BENCHER_PROJECT: save-walter-white).
  5. Opcional: Configura la bandera --testbed o la variable de entorno BENCHER_TESTBED a la representación única o UUID de Testbed. (ej: BENCHER_TESTBED: ubuntu-latest) El Testbed debe existir previamente. Si no se establece esto, entonces se utilizará el Testbed localhost.
  6. Opcional: Configura la bandera --adapter o la variable de entorno BENCHER_ADAPTER al nombre del adaptador deseado. (ej: BENCHER_ADAPTER: json) Si no se establece esto, entonces se utilizará el adaptador magic. Consulte adaptadores de arnés de benchmark para obtener una visión completa.
  7. Haz una copia de tu código fuente. (ej: uses: actions/checkout@v4)
  8. Instala la CLI de Bencher utilizando la Acción de GitHub. (ej: uses: bencherdev/bencher@main)
  9. Rastrea tus benchmarks con el subcomando CLI bencher run:
    1. Opcional: Configura la bandera --branch o la variable de entorno BENCHER_BRANCH a la representación única o UUID de la rama. (ej: --branch main) La rama debe existir previamente. Si no se establece esto, entonces se utilizará la rama main.
    2. La ficha API ya debe existir. Añade BENCHER_API_TOKEN como un secreto del repositorio. (ej: Repo -> Settings -> Secrets and variables -> Actions -> New repository secret) Establece la bandera --token o la variable de entorno BENCHER_API_TOKEN a la ficha API. (ej: --token ${{ secrets.BENCHER_API_TOKEN }})
    3. Establece el comando para fallar si se genera una Alerta. (ej: --err) Para que se genere una Alerta, un Umbral debe existir previamente.
    4. Ejecuta tus benchmarks y genera un informe a partir de los resultados. (ej: "bencher mock")

Pull Requests

Para detectar la regresión del rendimiento en los Pull Requests, necesitarás ejecutar tus benchmarks en PRs. Si solo esperas tener PRs de ramas dentro del mismo repositorio, entonces puedes simplemente modificar el ejemplo anterior para también ejecutar on eventos pull_request.

⚠️ ¡Esta solución solo funciona si todos los PRs son del mismo repositorio! Consulta Pull Requests de Forks a continuación.

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. Crea un archivo de workflow para GitHub Actions. (ej: .github/workflows/pr_benchmarks.yml)
  2. Ejecutar en eventos de pull_request solo si la solicitud de extracción proviene del mismo repositorio. Para gestionar PRs de Forks, vea Solicitudes de extracción de Forks a continuación.
  3. Establece los permisos para el GITHUB_TOKEN en write para pull-requests. Dependiendo de tus configuraciones de GitHub, esto puede no ser necesario. Pero para todas las organizaciones y repositorios personales creados después del 02 de febrero de 2023, este es el comportamiento predeterminado. Consulta la documentación de GitHub para obtener una visión general completa.
  4. Selecciona la rama a utilizar:
    1. Usar los datos de la rama actual si ya existen. (ej: --if-branch "$GITHUB_REF_NAME")
    2. Crear un clon de los datos y umbrales de la rama objetivo de la PR si ya existen. (ej: --else-if-branch "$GITHUB_BASE_REF")
    3. De lo contrario, crea un clon de los datos y umbrales de la rama main. (ej: --else-if-branch main)
  5. Configurar el token de autenticación de la API de GitHub. (ej: --github-actions "${{ secrets.GITHUB_TOKEN }}") Cuando esta opción se establece como parte de una solicitud de extracción, entonces los resultados se añadirán a la solicitud de extracción como un comentario. Esto utiliza la variable de entorno GITHUB_TOKEN de GitHub Actions.
  6. Consulta la documentación de ejecución de bencher para obtener una visión general completa de todas las formas de configurar el comentario de solicitud de extracción con las banderas --ci-*.
  7. (No mostrado) Crea un segundo archivo de workflow para GitHub Actions y utiliza el ejemplo inicial de arriba para ejecutar en eventos push a la rama main. (ej: .github/workflows/benchmarks.yml)

Pull Requests desde Forks

Si planeas aceptar pull requests desde forks, como suele ser el caso en proyectos de código abierto públicos, entonces tendrás que manejar las cosas un poco diferente. Por razones de seguridad, secretos como tu BENCHER_API_TOKEN y el GITHUB_TOKEN no están disponibles en GitHub Actions para PRs de forks. Es decir, si un colaborador externo abre un PR desde un fork, el ejemplo anterior no funcionará. Hay dos opciones para PRs de forks:

Benchmark Fork PR desde la Rama Objetivo con Revisores Requeridos

⚠️ Es muy, muy importante revisar a fondo cualquier PR de bifurcación antes de aprobarlo! No hacerlo podría resultar en una petición pwn!

Si prefieres no tener eso sobre tu cabeza, consulta Benchmark Fork PR y Carga desde la Rama Default a continuación.

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. Crea un archivo workflow de GitHub Actions. (ej: .github/workflows/pr_benchmarks.yml)
  2. Ejecuta en los eventos pull_request_target.
  3. Crea un job llamado fork_pr_requires_review que requiere la aprobación de un Revisor Requerido antes de cada ejecución de pull request de fork (external).
  4. Crea un tercer job que depende de fork_pr_requires_review.
    1. Haz checkout de la rama del pull request pero no persistas las credenciales de git. (ej: persist-credentials: false)
    2. Usa comillas simples alrededor de todas las entradas no confiables. (ej: --if-branch '${{ github.head_ref }}')
    3. Pasa todos los secretos directamente. (ej: --token "${{ secrets.BENCHER_API_TOKEN }}")
    4. Ejecuta y rastrea tus benchmarks de pull request con bencher run.
  5. (No se muestra) Crea un segundo archivo workflow de GitHub Actions y usa el ejemplo inicial de arriba para ejecutar en eventos push a la rama main. (ej: .github/workflows/benchmarks.yml)

Esta configuración funciona porque pull_request_target se ejecuta en el contexto de la rama objetivo del pull request, donde los secretos como tu BENCHER_API_TOKEN y el GITHUB_TOKEN están disponibles. Por lo tanto, este flujo de trabajo solo se ejecutará si existe en la rama objetivo.

Para configurar esto, necesitas crear dos Entornos de Acción de GitHub (ej: Repo -> Settings -> Environments -> New environment). El entorno internal no debe tener ninguna Regla de protección de despliegue. Sin embargo, el entorno external debe tener Revisores requeridos establecido para aquellos que pueda confiar para revisar los PRs de fork antes de benchmarking.

Es muy importante envolver el nombre de la rama de PR (referencia de cabeza) en comillas simples. (ej: --if-branch '${{ github.head_ref }}') De lo contrario, un atacante podría crear una rama con nombre malicioso que realice inyección de comando. Consulta este informe del Laboratorio de Seguridad de GitHub sobre cómo evitar solicitudes pwn de entrada no confiable para una visión completa.

Evita configurar cualquier secreto como variables de entorno, como GITHUB_TOKEN y BENCHER_API_TOKEN. En lugar de eso, pásalos explícitamente a bencher run. (ej: --token "${{ secrets.BENCHER_API_TOKEN }}") Consulta este informe del Laboratorio de Seguridad de GitHub y este post de blog sobre cómo evitar solicitudes pwn para una visión completa.

Benchmark Fork PR y Subida desde la Rama 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. Crea un archivo de flujo de trabajo Run and Cache Benchmarks. (ej: .github/workflows/pr_benchmarks.yml)
  2. Ejecuta todos los trabajos en el flujo de trabajo en eventos de pull_request.
  3. Ejecuta los benchmarks y guarda los resultados en un archivo. (ej: benchmark_results.json)
  4. Sube el archivo de resultados de benchmark como un artefacto.
  5. Sube el objeto de evento pull_request como un artefacto.
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. Crea un segundo archivo de flujo de trabajo, Track Benchmarks. (ej: .github/workflows/track_benchmarks.yml)
  2. Añade al flujo de trabajo Track Benchmarks a Run and Cache Benchmarks con el evento workflow_run.
  3. Descarga los resultados de benchmark almacenados en caché y el evento pull_request.
  4. Extrae los resultados del benchmark almacenados en caché y el evento pull_request.
  5. Exporta los datos necesarios del evento pull_request como variables de entorno.
  6. Rastrea los resultados del benchmark almacenados en caché con bencher run:
    1. Usar comillas simples alrededor de toda la entrada no confiable. (ej: --if-branch '${{ env.PR_HEAD }}')
    2. Pasar explícitamente el número de pull request. (ej: --ci-number '${{ env.PR_NUMBER }}')
    3. Pasar el camino al archivo de resultados de benchmark. (ej: --file "$BENCHMARK_RESULTS")
  7. (No se muestra) Crea un tercer archivo de flujo de trabajo de GitHub Actions y usa el ejemplo inicial de arriba para ejecutar en eventos de push a la rama main. (ej: .github/workflows/benchmarks.yml)

Esta configuración funciona porque workflow_run se ejecuta en el contexto de la rama default del repositorio, donde los secretos como tu BENCHER_API_TOKEN y el GITHUB_TOKEN están disponibles. Por lo tanto, estos flujos de trabajo solo se ejecutarán si existen en la rama default. Consulte usando datos del flujo de trabajo de disparo para obtener una visión completa. El número de pull request, la rama de cabeza y la rama base utilizados en el flujo de trabajo inicial deben pasarse explícitamente ya que no están disponibles dentro de workflow_run.

Es muy importante envolver el nombre de la rama de PR (referencia de cabeza) en comillas simples. (ej: --if-branch '${{ env.PR_HEAD }}') De lo contrario, un atacante podría crear una rama con nombre malicioso que realice inyección de comando. Consulta este informe del Laboratorio de Seguridad de GitHub sobre cómo evitar solicitudes pwn de entrada no confiable para una visión completa.

Evita configurar cualquier secreto como variables de entorno en el archivo de flujo de trabajo Run and Cache Benchmarks. Consulta este escribo del Laboratorio de Seguridad de GitHub y este post de blog sobre cómo evitar solicitudes pwn para una visión completa.



🐰 ¡Felicidades! ¡Has aprendido a usar Bencher en GitHub Actions! 🎉


Continúa: Visión General del Benchmarking ➡

🤖 Este documento fue generado automáticamente por OpenAI GPT-4. Puede que no sea exacto y contenga errores. Si encuentra algún error, abra un problema en GitHub.