Thin per-platform wrappers that run the same code-review-agent worker
container against a pull/merge request diff. Nothing platform-specific lives in
the agent — each CI integration is just the container plus the right env vars and
one selected reporter.
| File | Platform | Default reporter (auto) |
Durable output |
|---|---|---|---|
github-action/action.yml |
GitHub Actions | github + terminal |
idempotent PR comment |
gitlab-ci/.gitlab-ci.yml |
GitLab CI | gitlab + terminal |
idempotent MR note |
jenkins/Jenkinsfile |
Jenkins | terminal + file |
archived artifact |
Jenkins has no native PR comments, so it relies on the file reporter + artifact
archiving. GitHub/GitLab additionally archive the file artifact in these
examples.
Every example runs the same worker image built from this repo's
Dockerfile. Get it into the job one of two ways:
-
Build inline (no registry needed). The GitHub Actions example (
example-workflow.yml) checks out the agent source anddocker builds it in the job, then passes the local tag as theimage:input — nothing has to be published first. -
Pull a published image. Build once, push to a registry, then reference that tag (used by the GitLab/Jenkins examples; the GitHub one can do this too by dropping its build steps):
docker build -t ghcr.io/your-org/code-review-agent:latest . docker push ghcr.io/your-org/code-review-agent:latestReplace
ghcr.io/your-org/code-review-agent:latesteverywhere with your image.
The image bundles the trusted skills/ and review.toml and ships git
(the CLI shells out to git diff/git show).
The entrypoint is the CLI:
code-review [RANGE] --repo PATH --reporter ... --fail-on ...
For a CI review the RANGE is <base>...HEAD (three-dot). A two-/three-dot range
makes the agent read new-side file content with git show <head>:<path> — correct
regardless of checkout state — and treats <base> as the diff base.
These are not optional knobs — they are the trust boundary (see
PLAN.md "Security & trust model" and CLAUDE.md §6/§13). Getting
one wrong silently degrades to default config or leaks PR-controlled input into
the reviewer prompt.
-
Run with the container's working directory set to the checkout.
config.pyreads the trustedreview.tomlwith a baregit show <ref>:review.toml(no-C), so it discovers the repo from the current directory. If cwd is not inside the checkout the read fails and silently falls back to default config. GitHub:docker run -w /workspace. GitLab/Jenkins: the job already runs with cwd = the cloned project dir. -
Set
TRUSTED_CONFIG_REFto the base ref, never the PR/MR head.review.tomlis read fromgit show <base>:review.toml— a PR cannot rewrite its own review rules (fail_on, ignore globs, enabled skills). With no trusted ref a CI run fails closed (UntrustedConfigError). The base sha must be in local history: GitHubactions/checkoutwithfetch-depth: 0, GitLabGIT_DEPTH: "0", Jenkinsgit fetch origin <target>. -
Pin
SKILLS_PATHandREVIEW_CONFIGto the bundled absolute paths (/app/skills,/app/review.toml) via real env vars. The defaults are cwd-relative; since cwd is the PR checkout (rule 1), unpinned defaults would resolve./skills/./review.tomlinto PR-controlled content. The image already sets these; the examples re-assert them for clarity. -
Make a CI marker visible inside the container (
CI,GITHUB_ACTIONS,GITLAB_CI, orJENKINS_URL). It does two things: (a) the.envfile is not loaded under CI, so a PR-supplied checkout.envcan't set operator-only fields; (b)--reporter autoresolves to the platform reporter. Note:docker rundoes not inherit the runner's env, so GitHub's composite action passes-e CI=true -e GITHUB_ACTIONS=trueexplicitly. -
Repo-local extra skills stay off.
ALLOW_REPO_SKILLSis unset (false). Only flip it on (--allow-repo-skills) if you trust the reviewed repo'sreview.toml [skills].extra_paths— it is a prompt-injection vector in CI. -
Mount the checkout read-only and write artifacts elsewhere. The agent only reads diffs and reports; the read-only mount enforces that. The
filereporter writes to a separate writableREPORT_DIR. -
git safe.directory. When the checkout is owned by a different uid than the container user, git refuses with "detected dubious ownership". The examples passGIT_CONFIG_COUNT=1 / GIT_CONFIG_KEY_0=safe.directory / GIT_CONFIG_VALUE_0=<path>so every internal git call trusts the mounted checkout.
| Concern | Vars | Notes |
|---|---|---|
| LLM | OPENAI_API_KEY | ANTHROPIC_API_KEY | GOOGLE_API_KEY, DEFAULT_LLM_PROVIDER, DEFAULT_LLM_MODEL |
one key must match the provider; store as a masked secret |
| Trust | TRUSTED_CONFIG_REF (base ref), TRUSTED_CONFIG_PATH (repo-relative, default review.toml), SKILLS_PATH, REVIEW_CONFIG, ALLOW_REPO_SKILLS |
TRUSTED_CONFIG_PATH is fed to git show — never an absolute path |
| GitHub reporter | GITHUB_TOKEN (needs pull-requests: write), GITHUB_REPOSITORY, GITHUB_REF/GITHUB_EVENT_PATH, GITHUB_API_URL |
PR number comes from the event payload or refs/pull/N/merge |
| GitLab reporter | GITLAB_TOKEN (project/personal token with api), CI_PROJECT_ID, CI_MERGE_REQUEST_IID, CI_API_V4_URL |
CI_JOB_TOKEN usually cannot post notes — use GITLAB_TOKEN |
| Output | REPORT_DIR (writable), REPORTER (or --reporter) |
reporters are composable: terminal,file,github,gitlab |
The run exits non-zero when a finding meets --fail-on (default high), failing
the job — a quality gate. Set --fail-on off for advisory-only reviews that post a
comment/artifact but never block the pipeline. Reporter failures are non-fatal and
do not change the exit code.