Docs / CI

CI integration

One command runs your Beryl tests from any pipeline, streams progress into the job log, and exits 0 only when every test passed. Nothing to install: the browsers run in Beryl's cloud.

Set this up from Settings → CI

Open your project's Settings → CI in the web app. Create a CI token there (it is shown once, so store it in your CI provider's secrets as BERYL_CI_TOKEN), then save a workflow with the environment and groups you want. Each saved workflow gets a ready script with the ids already filled in, which you copy straight into your pipeline. Doing it this way means no ids to look up by hand.

A CI token (beryl_ci_...) is scoped to one project and can only trigger runs and read their results, so it is safe to hand to a pipeline. Revoking it in Settings → CI cuts off every pipeline using it at once. The CLI reads it from either BERYL_TOKEN or BERYL_API_KEY.

The script

This is what Settings → CI generates. It runs anywhere a shell can run Node 20+, so write it by hand if you would rather not use the app:

BERYL_TOKEN=$BERYL_CI_TOKEN npx --yes @beryl-so/cli@latest runs trigger \
  --workspace <workspace-id> \
  --project <project-id> \
  --env <environment-id> \
  --group "Smoke" \
  --watch
  • --env: which environment to run against (required)
  • --watch: follow progress and exit non-zero on failure. Without it the command prints the run id and returns immediately
  • --timeout: with --watch, stop waiting after this many minutes and exit 1; the run keeps going in Beryl. No limit by default
  • --group: run only these groups, repeatable. Omit to run every active test
  • --url-override: run against a preview deploy instead of the environment's URL
  • --header: send KEY=VALUE on every navigation, to get past a protected preview such as x-vercel-protection-bypass
  • --fail-fast: exit at the first failing test instead of waiting for the rest

The step exits with:

  • 0 every test passed
  • 1 a test failed, or the wait timed out
  • 2 usage error
  • 3 auth error

Examples

GitHub Actions (any other CI is the same one-liner):

- name: Beryl tests
  env:
    BERYL_TOKEN: ${{ secrets.BERYL_CI_TOKEN }}
  run: |
    npx --yes @beryl-so/cli@latest runs trigger \
      --workspace <workspace-id> --project <project-id> \
      --env <environment-id> --watch

A protected preview deploy, stopping at the first failure:

npx --yes @beryl-so/cli@latest runs trigger \
  --workspace <workspace-id> --project <project-id> --env <environment-id> \
  --url-override https://preview-123.example.com \
  --header x-vercel-protection-bypass=$VERCEL_BYPASS \
  --watch --timeout 30 --fail-fast

Each test prints a line as it settles. On GitHub Actions every failing test also becomes a workflow annotation, and the job summary gets a results table with a link to the run.

After the run

The run lands in the project's history labeled CI. A connected Slack channel gets every finished CI run; the completion email stays quiet unless a new regression appeared. With a personal access token you can also pull artifacts back into the pipeline:

BERYL_TOKEN=$BERYL_PAT npx --yes @beryl-so/cli@latest runs download <run-id> \
  --workspace <workspace-id> --project <project-id> --dir ./beryl-run