# GitHub Action — review in CI

> CommitBrief/commitbrief-action runs the CLI on every pull request — inline review comments, a severity gate, or a declarative policy gate.

CommitBrief docs · v1.x · Operations

Canonical URL: https://commitbrief.com/docs/1.x/github-action

---

**`CommitBrief/commitbrief-action` is a composite GitHub Action that
runs an LLM code review on every pull request.** Add it to a workflow
with `uses: CommitBrief/commitbrief-action@v1`, give it a provider and
an API key, and it installs the CommitBrief CLI on the runner and
reviews the PR diff in one of three modes: post inline review comments
with a verdict (`comment`), fail the job on finding severity (`gate`),
or evaluate a declarative policy file (`guard`).

It is a thin wrapper — every mode shells out to a CommitBrief command
you can run locally, so a CI failure reproduces on your machine.

## Quick start

Add `.github/workflows/commitbrief.yml` to the repo you want reviewed:

```yaml
name: CommitBrief
on: pull_request

permissions:
  contents: read
  pull-requests: write        # comment mode posts the review

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: CommitBrief/commitbrief-action@v1
        with:
          provider: anthropic
          api-key: ${{ secrets.ANTHROPIC_API_KEY }}
```

That is the whole setup for `comment` mode (the default). The Action
installs Go, runs `go install github.com/CommitBrief/commitbrief/cmd/commitbrief@latest`,
and reviews the pull request.

**The checkout step is not optional.** The Action does not check out
your repository for you, and none of the three modes work without a
git working tree: `comment` mode resolves the target repo from the git
remote, and `gate` / `guard` diff two commits that have to exist
locally.

## Inputs

| Input | Default | Required | What it does |
|-------|---------|----------|--------------|
| `provider` | — | **yes** | `anthropic`, `openai`, `gemini`, `deepseek`, `mistral`, `cohere`, or `ollama`. Anything else fails the step. |
| `api-key` | `""` | no | Provider API key. Pass a repository secret. Not needed for `ollama`. |
| `model` | `""` | no | Model override. Empty uses the provider's default. |
| `mode` | `comment` | no | `comment`, `gate`, or `guard`. |
| `request-changes-on` | `""` | no | `comment` mode: severity at or above which the verdict becomes request-changes (`critical`, `high`, `medium`, `low`). Empty — the default — never requests changes. |
| `fail-on` | `high` | no | `gate` mode: fail the job when a finding meets or exceeds this severity (`critical`, `high`, `medium`, `low`, `info`, `any`). |
| `policy` | `.commitbrief/policy.yml` | no | `guard` mode: policy file path, relative to the repo root. |
| `version` | `latest` | no | The `go install` ref for the CLI, e.g. `v1.16.0`. |

`request-changes-on`, `fail-on`, and `policy` are each read by exactly
one mode and ignored by the other two.

## Permissions

| Mode | Required `permissions:` |
|------|-------------------------|
| `comment` | `contents: read` **and** `pull-requests: write` |
| `gate` | `contents: read` |
| `guard` | `contents: read` |

`comment` mode posts through the workflow's own `GITHUB_TOKEN`, which
is why it needs `pull-requests: write`. Grant that only for the mode
that uses it — a gate-only workflow should stay read-only.

## Mode: `comment`

Runs [`commitbrief remote pr <number>`](/docs/1.x/remote-pr). Each
finding is posted as an inline review comment on the PR, then a
verdict is submitted.

```yaml
      - uses: CommitBrief/commitbrief-action@v1
        with:
          provider: anthropic
          api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          mode: comment              # default
          request-changes-on: high   # opt in to a blocking verdict
```

Without `request-changes-on` the verdict is only ever `approve` or
`comment` — the review never blocks a merge. Setting it to `high`
turns any high or critical finding into a `request-changes` review.
The full verdict table, and the comment-volume caps that come with
each threshold, are in
[Review a GitHub PR](/docs/1.x/remote-pr#the-verdict).

The step exits `0` whenever the review submits successfully, verdict
included. `comment` mode reports through GitHub, not through the exit
code; use `gate` or `guard` if you want a red check.

## Mode: `gate`

Runs [`commitbrief diff <base>...<head> --fail-on=<severity>`](/docs/1.x/severity).
No comments, no PR write access — just a pass/fail check.

```yaml
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0             # both PR endpoints must be local
      - uses: CommitBrief/commitbrief-action@v1
        with:
          provider: openai
          api-key: ${{ secrets.OPENAI_API_KEY }}
          mode: gate
          fail-on: high
```

`fetch-depth: 0` matters here. On a `pull_request` event the default
shallow checkout does not contain the base and head commits the Action
diffs, and `commitbrief diff` fails on the missing objects. What the
severities mean, and which exit code each produces, is in
[Severity and CI gating](/docs/1.x/severity).

## Mode: `guard`

Runs [`commitbrief guard --diff <base>...<head> --policy <path>`](/docs/1.x/policy-gate).
A per-severity budget rather than a single threshold — useful when a
repo takes a high volume of AI-authored pull requests.

```yaml
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: CommitBrief/commitbrief-action@v1
        with:
          provider: anthropic
          api-key: ${{ secrets.ANTHROPIC_API_KEY }}
          mode: guard
          policy: .commitbrief/policy.yml   # default
```

Requires **CLI v1.10.0 or newer** — pin `version:` accordingly if you
are not on `latest`. The policy file has to be committed, and
CommitBrief adds `.commitbrief/` to your `.gitignore` on its first
cache write, so un-ignore the one file explicitly:

```gitignore
.commitbrief/
!.commitbrief/policy.yml
```

A missing or malformed policy **blocks** rather than passes — see
[Policy gate](/docs/1.x/policy-gate#exit-codes).

## Secrets

`api-key` is the only secret the Action takes. Pass it from a
repository (or organization) secret and let the Action map it to the
environment variable the CLI reads:

| `provider` | Environment variable set for the CLI |
|-----------|--------------------------------------|
| `anthropic` | `ANTHROPIC_API_KEY` |
| `openai` | `OPENAI_API_KEY` |
| `gemini` | `GEMINI_API_KEY` |
| `deepseek` | `DEEPSEEK_API_KEY` |
| `mistral` | `MISTRAL_API_KEY` |
| `cohere` | `COHERE_API_KEY` |
| `ollama` | none — leave `api-key` empty |

`ollama` needs no key but does need a reachable server, so it is
practical only on a self-hosted runner or with a service container;
point the CLI at it with `OLLAMA_HOST` in the workflow's `env:`.

The three [CLI-backed providers](/docs/1.x/providers#claude-cli-gemini-cli-and-codex-cli)
— `claude-cli`, `gemini-cli`, `codex-cli` — are rejected outright by
the `provider` input. They drive a locally authenticated host CLI,
which a CI runner does not have.

## Pinning versions

Two versions are in play, and they are pinned separately.

- **The Action** — `uses: CommitBrief/commitbrief-action@v1`. `v1` is
  a moving tag: it is repointed as the Action changes, so a
  behavioral fix reaches you without a workflow edit. Pin a commit SHA
  instead if your organization requires immutable third-party actions.
- **The CLI** — the `version:` input, default `latest`. Pin a released
  tag (`version: v1.16.0`) for reproducible CI; `latest` follows the
  newest release and can change what your gate flags from one run to
  the next.

Each mode has a CLI floor:

| Mode | Needs CLI |
|------|-----------|
| `gate` | v0.9.0+ (the `diff` subcommand) |
| `comment` | v1.1.0+ (`remote pr`) |
| `guard` | v1.10.0+ (`commitbrief guard`) |

## Notes for CI

- **Pull requests only.** The Action reads the PR number, base SHA,
  and head SHA from the `pull_request` event payload and fails
  immediately without one. It is not a `push`-event tool.
- **Large diffs can abort on the cost preflight.** CommitBrief aborts
  rather than prompts when it is not on a TTY and the estimated spend
  exceeds `cost.warn_threshold_usd` (default `$0.50`). The Action
  exposes no `--no-cost-check` input; raise or disable the ceiling in a
  committed `.commitbrief/config.yml` — the cost key only, never an
  `api_key` — un-ignored the same way as the policy file. See
  [Safety, cost, and cache](/docs/1.x/safety-and-cost).
- **The cache never survives a run.** The response cache lives at
  `.commitbrief/cache` inside the checkout, so every job starts cold
  and every review is a paid call.
- **`COMMITBRIEF.md` is honored.** The Action reviews a real checkout,
  so your committed [review rules](/docs/1.x/review-rules) apply in CI
  exactly as they do locally.

## See also

- [Review a GitHub PR — remote pr](/docs/1.x/remote-pr) — the command
  behind `comment` mode, and how to run it from your own terminal.
- [Severity and CI gating](/docs/1.x/severity) — the severity scale and
  `--fail-on`, behind `gate` mode.
- [Policy gate — guard](/docs/1.x/policy-gate) — the policy file format,
  behind `guard` mode.
- [Providers](/docs/1.x/providers) — which provider to pick, and what a
  review costs.