Skip to content

Gate a pull request with b8e ci

If you want a pull request to fail when it introduces accessibility violations, run b8e ci as a build step. It scans, reports, and exits non-zero on gating findings, so your CI provider marks the check red.

If you want to gate only the files a pull request changed — not the whole backlog — diff against the base branch with --base:

Terminal window
b8e ci --base origin/main

b8e scans the files changed since that git ref and gates on those alone. Point --base at whatever ref your CI exposes as the merge target.

If the project already has a backlog of findings, changed-file scoping alone will not get you to green — a pull request that touches an old file inherits its old findings. Record the existing ones as a baseline first: see Adopt a baseline on an existing codebase.

By default b8e ci fails on block-level findings and lets warn-level ones through. If you want warnings to fail the build too, raise the enforcement level with --enforce:

Terminal window
b8e ci --base origin/main --enforce warn

To narrow the gate by severity instead — fail only on findings at or above a given impact — use --fail-on:

Terminal window
b8e ci --base origin/main --fail-on serious

block is not a severity — a finding is block-level only when one of its WCAG success criteria is listed in enforcement.block in binclusive.json. b8e init writes that list empty on purpose, so your first run reports the backlog you already had without failing the build.

Until you declare a criterion, the gate has nothing to match. b8e ci says so:

NOT GATING — binclusive.json declares nothing in "enforcement.block", so every finding is
advisory, so this run could not have failed however bad the findings are

The run still exits 0 — a first run is meant to be green. To start gating, add the criteria you want enforced to enforcement.block, or record today’s findings with b8e baseline . first and tighten from there.

Asking for a gate that cannot fire is refused rather than passed: --fail-on against an empty enforcement.block exits 4 and names what to configure, instead of reporting a pass over a critical finding.

b8e ci follows the grep/eslint convention:

  • 0 — nothing gated; the check passes.
  • 1 — gating findings; the check fails.
  • 4 — the invocation asked for a gate that cannot fire (a --fail-on over an empty enforcement.block).
  • 7 — no credential. The step never scanned; see Wire it into a workflow.
  • 2 or higher — the tool failed to run (a bad --base, a broken baseline, a crash).

A crash exits 2, distinct from 1, so a findings failure is never confused with the tool falling over. See the exit-codes reference for the full contract.

In a GitHub Actions job, let the exit code drive the check — do not swallow it:

- run: b8e ci --base origin/${{ github.base_ref }}
env:
BINCLUSIVE_API_KEY: ${{ secrets.BINCLUSIVE_API_KEY }}

b8e ci needs a credential to run, the same as every other b8e command. On a runner there is nobody to b8e auth login, so on that lane the credential is BINCLUSIVE_API_KEY — without it the step exits 7 and never scans.

b8e init --ci scaffolds this workflow for you, that secret included.

If you want a rollup digest in the job summary and a sticky PR comment, add --summary.

The same key that authenticates the run is also what asks for an upload. Add the project to file into, and the findings land in your dashboard:

- run: b8e ci --base origin/${{ github.base_ref }}
env:
BINCLUSIVE_API_KEY: ${{ secrets.BINCLUSIVE_API_KEY }}
BINCLUSIVE_PROJECT_ID: prj_your_project_id

Mint the token in the dashboard under Settings → CI access — it must be a project CI token; a personal API key will not authenticate ingestion.

Setting BINCLUSIVE_API_KEY is treated as “I want these uploaded”, so from then on any failure to send fails the step loudly rather than passing green with nothing filed.

On a workflow that is not pull_request — an on: push or a schedule — also set BINCLUSIVE_ENVIRONMENT to production or staging. Without it the run is stamped unknown and its findings are attributed to no deployment — listed in the dashboard, but absent whenever anyone narrows to Production. See the environment reference.

Needing a credential is not the same as transmitting one byte of your findings. Two ways to keep a gating run local:

Terminal window
b8e ci --base origin/main --no-upload

--no-upload runs the gate exactly as normal and transmits nothing, whether or not a key is set.

The other way is to run under a b8e auth login session with no BINCLUSIVE_API_KEY in the environment. The session is what satisfies the credential requirement; with no key there is nothing asking for an upload, so the scan runs, the exit code is right, and the findings stay on the machine. That is the local path on a laptop, or on a self-hosted runner someone has signed in on.

For every flag b8e ci accepts, see the ci reference.