AI code review on every pull request: Open Code Review with GitHub Actions and GitLab CI
Open Code Review pays off most when nobody has to remember to run it: every pull request gets a first review with comments on the exact lines before a human looks at it. In this last part of the guide we set that up with the official GitHub Action and with GitLab CI, explain every secret and option, and look at how to control cost without opening a security hole.
We build on the earlier parts: installing and configuring ocr and writing rules with rule.json. Everything here comes from the v1.13.2 workflow, action and docs. We did not run it on a repository with a paid API key, so where we describe behavior it is the behavior the project documents.
How CI review works

Every recipe (GitHub, GitLab, Gerrit, Bitbucket, GitFlic, Codeup) follows the same outline:
- A PR or MR event triggers the job.
ocris installed with npm (runners are ephemeral).- The LLM is configured with
ocr config setfrom CI secrets. - The review runs in range mode with machine-readable output:
``bash ocr review --from "origin/<base-branch>" --to "origin/<pr-branch>" --format json --audience agent ``
- A script reads
comments[]and posts each finding as an inline comment. Findings without a line (start_line: 0) go into a summary comment.
There are always two credentials: the LLM one, to generate the review, and a token with write access to the PR, to comment. On GitHub the second is the automatic GITHUB_TOKEN.
GitHub Actions step by step
1. Copy the workflow
mkdir -p .github/workflows
curl -o .github/workflows/ocr-review.yml \
https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/github_actions/ocr-review.yml2. Create secrets and variables
Under Settings → Secrets and variables → Actions:
| Name | Type | Example | Purpose |
|---|---|---|---|
| `OCR_LLM_URL` | Secret | `https://api.anthropic.com` or `https://api.openai.com/v1/chat/completions` | Model endpoint |
| `OCR_LLM_AUTH_TOKEN` | Secret | `sk-…` | Provider key |
| `OCR_LLM_MODEL` | Variable | `claude-sonnet-4-6` | Model. No default. |
| `OCR_LLM_USE_ANTHROPIC` | Variable | `true` | `true` for Anthropic, `false` for OpenAI-compatible APIs |
3. What the official workflow does
This is the core of the file (simplified; the original repeats the same filter in concurrency):
on:
pull_request_target:
types: [opened, synchronize, reopened]
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
jobs:
code-review:
runs-on: ubuntu-latest
timeout-minutes: 30
if: |
github.event_name == 'pull_request_target'
|| (
github.event_name == 'issue_comment'
&& github.event.issue.pull_request
&& github.event.comment.user.type != 'Bot'
&& contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association)
&& (startsWith(github.event.comment.body, '/open-code-review')
|| startsWith(github.event.comment.body, '@open-code-review'))
)
steps:
- name: Get PR context
id: pr-context
if: github.event_name == 'issue_comment'
uses: actions/github-script@v7
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner, repo: context.repo.repo,
pull_number: context.issue.number });
core.setOutput('base_ref', pr.base.ref);
core.setOutput('head_sha', pr.head.sha);
- name: Run OpenCodeReview
uses: alibaba/open-code-review@main
with:
llm_url: ${{ secrets.OCR_LLM_URL }}
llm_auth_token: ${{ secrets.OCR_LLM_AUTH_TOKEN }}
llm_model: ${{ vars.OCR_LLM_MODEL }}
llm_use_anthropic: ${{ vars.OCR_LLM_USE_ANTHROPIC }}
base_ref: ${{ steps.pr-context.outputs.base_ref }}
head_sha: ${{ steps.pr-context.outputs.head_sha }}What to understand:
- It triggers when a PR is opened, updated or reopened, and when a member, owner or collaborator comments
/open-code-reviewor@open-code-review. Comments from bots and strangers don't spend your quota. pull_request_targetis used so secrets are available for PRs from forks too. The project considers this safe because the action only reads the diff and never executes PR code. Don't add steps that run PR code (npm install, tests, builds) to this same workflow: underpull_request_targetthey would have access to your secrets.concurrencycancels the previous review of the same PR when a new push arrives.- The composite action
alibaba/open-code-review@maininstallsocr, configures it, runs the review and posts the comments. If the batch fails, it posts them one by one, and leaves a summary comment with statistics.
4. The action inputs you'll use most
All under with: on the Run OpenCodeReview step:
- name: Run OpenCodeReview
uses: alibaba/open-code-review@main
with:
llm_url: ${{ secrets.OCR_LLM_URL }}
llm_auth_token: ${{ secrets.OCR_LLM_AUTH_TOKEN }}
llm_model: ${{ vars.OCR_LLM_MODEL }}
llm_use_anthropic: ${{ vars.OCR_LLM_USE_ANTHROPIC }}
language: English
effort: medium
max_tokens_budget: '3000000'
rule: .opencodereview/rule.json
background: ${{ github.event.pull_request.title }}
ocr_version: '1.13.2'
incremental: 'true'
resolve_outdated: 'true'
stream_progress: 'true'| Input | Default | What it does |
|---|---|---|
| `language` | `English` | Comment language |
| `effort` | (medium) | `low`, `medium` or `high`: 1, 2 or 3 passes |
| `max_tokens_budget` | unlimited | Token cap per run; publishes what it reviewed and marks the rest `failed(budget)` |
| `rule` | — | Rules file passed to `--rule` |
| `background` | — | Context passed to `--background` (the PR title works great with Conventional Commits) |
| `review_concurrency` | 8 | Parallel sub-agents; lower it if your provider throttles you |
| `ocr_version` | `latest` | Pin a version for reproducible builds |
| `sticky_summary` | `true` | Updates the same summary comment on every run |
| `incremental` | `false` | Only adds comments that don't overlap ones it already posted |
| `resolve_outdated` | `false` | Resolves its threads GitHub has marked outdated |
| `checkpoint_range` | `false` | On each push, reviews only what is new since the last reviewed commit |
| `route_severity_below` / `route_categories` | — | Sends minor findings to the summary instead of inline |
| `llm_extra_body` | `{"thinking":{"type":"disabled"}}` | Extra provider fields (disables thinking by default) |
| `llm_reasoning_effort` | — | `minimal` to `max` for OpenAI-compatible reasoning models |
On background: if you write your own run: steps instead of using the action, pass the PR title through env: and never with ${{ }} inside the script. GitHub substitutes ${{ }} as text before the shell runs, and a title with special characters could execute commands on your runner:
- name: Run OCR review
env:
PR_TITLE: ${{ github.event.pull_request.title }}
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
run: |
ocr review --background "$PR_TITLE" \
--from "origin/$BASE_REF" --to "origin/$HEAD_REF" \
--format json --audience agent > ocr-result.json5. Findings in Security → Code scanning (SARIF)
If you'd rather see findings in GitHub's security tab than as comments:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: npm install -g @alibaba-group/open-code-review
- name: Run OCR review
env:
OCR_LLM_URL: ${{ secrets.OCR_LLM_URL }}
OCR_LLM_TOKEN: ${{ secrets.OCR_LLM_AUTH_TOKEN }}
OCR_LLM_MODEL: ${{ vars.OCR_LLM_MODEL }}
BASE_REF: ${{ github.base_ref }}
HEAD_REF: ${{ github.head_ref }}
run: |
ocr review --from "origin/$BASE_REF" --to "origin/$HEAD_REF" \
--format sarif --audience agent > results.sarif
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sariffetch-depth: 0 is mandatory: without full history there is no merge-base. Uploading SARIF requires the security-events: write permission on the job.
6. Comment as a named bot
By default comments come from github-actions[bot]. To post as "OpenCodeReview Bot", create a GitHub App (Pull requests read and write, Contents read-only), install it on the repo, store GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY as secrets and mint the token with actions/create-github-app-token@v1. Then pass it to the action's github_token input.
GitLab CI
curl -o .gitlab-ci.yml \
https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/gitlab_ci/.gitlab-ci.ymlIf you already have a .gitlab-ci.yml, save the recipe elsewhere and pull it in with include: - local: 'ci/ocr-review.gitlab-ci.yml'.
Variables under Settings → CI/CD → Variables:
| Variable | Required | Masked | Description |
|---|---|---|---|
| `OCR_LLM_URL` | Yes | No | Model endpoint |
| `OCR_LLM_AUTH_TOKEN` | Yes | Yes | Provider key |
| `OCR_LLM_MODEL` | Yes | No | Model |
| `GITLAB_API_TOKEN` | Recommended | Yes | Token with `api` scope to post comments; falls back to `CI_JOB_TOKEN` |
The core of the job:
script:
- npm install -g @alibaba-group/open-code-review
- |
ocr config set llm.url "$OCR_LLM_URL"
ocr config set llm.auth_token "$OCR_LLM_AUTH_TOKEN"
ocr config set llm.model "$OCR_LLM_MODEL"
ocr config set llm.use_anthropic false
ocr config set llm.extra_body '{"thinking": {"type": "disabled"}}'
- mkdir -p .ocr
- |
ocr review --background "$CI_MERGE_REQUEST_TITLE" \
--from "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" \
--to "${CI_COMMIT_SHA}" \
--format json --audience agent > .ocr/ocr-result.json
- python3 post_review.py .ocr/ocr-result.jsonThe posting script uses the MR versions endpoint to compute base_sha, start_sha and head_sha so each comment lands on the right line as a discussion, falls back to plain notes for anything it can't anchor and ends with a summary. It also keeps artifacts (.ocr/ocr-result.json, .ocr/ocr-stderr.log) for a week and writes a dotenv report with counters.
Two GitLab details:
- GitLab rejects variables shorter than 8 characters, so
llm.use_anthropicis hardcoded tofalsein the script. For Claude, edit it by hand. - With
only: [merge_requests]it reviews on every push. The docs include a Python wrapper that checks the MR notes and skips the review if an OpenCodeReview one already exists. If you name the project access token "OpenCodeReview Bot", that is the name shown on the comments.
Self-hosted GitLab needs no changes: it uses CI_SERVER_URL.
Other CI systems
The repo's examples/ folder has ready-made recipes for Gerrit (Jenkinsfile + posting script), Bitbucket Pipelines, GitFlic CI and Codeup (Alibaba Cloud DevOps). They all follow the same five-step pattern.
Keeping cost under control
Nobody watches the meter in CI, so cap it from the start:
max_tokens_budget(or--max-tokens-budget): a hard cap per run.effort: low: one pass per group, the biggest saver.incrementalorcheckpoint_range: don't re-run the full review on every push.excludeinrule.json: drop migrations, generated code and fixtures.- On-demand trigger: remove
synchronizefrom the events and let the team comment/open-code-reviewwhen they want a fresh review. - Lower
review_concurrencyif your provider throttles you: retries under throttling make the job slower.
Common errors
Cannot find merge-base The checkout was shallow. Set fetch-depth: 0 on actions/checkout. The official workflow already does; keep it if you edit the file.
Failed to parse OCR output OCR_LLM_URL or OCR_LLM_AUTH_TOKEN is missing or wrong. Check the secrets and look at the artifacts (ocr-result.json and ocr-stderr.log).
Comments on the wrong lines The diff changed between review and posting (another push in between). The script detects it and posts a plain comment instead.
The job is green but files are missing A sub-agent failed (timeout, provider limit). ocr exits 0 if at least one group succeeded; failed ones appear in the JSON warnings and in the summary.
OCR_DEBUG: "1" does nothing It isn't implemented. To debug, download the artifacts or run ocr review locally with the same refs.
FAQ
Can it block the merge if it finds something serious? The action doesn't fail on findings: it comments. To block, use SARIF with Code Scanning and a branch protection rule, or read the action's comments_total output in a later step and exit 1 by your own criteria.
Is it safe with PRs from forks? The official workflow is, because it only reads the diff. The risk appears if you add steps that execute PR code in the same pull_request_target job.
Can I use a local model in CI? Yes, if the runner can reach your Ollama or gateway (LiteLLM, for example): point OCR_LLM_URL at it and set OCR_LLM_USE_ANTHROPIC to false.
How long does it take? According to the project's benchmark, 1 to 8 minutes per real PR depending on the model. The workflow has timeout-minutes: 30.
Conclusion
With one YAML file, two secrets and two variables, every pull request gets a line-anchored AI review, in your language, with your rules and a capped cost. That is the point where Open Code Review stops being a tool you run and becomes part of the team's process.
That wraps up the guide. If you skipped ahead, start with what Open Code Review is and how it works inside, and for more tools for your agent, see the Claude Code skills guide.
// source code
// implementation
More on AI agents

- Skills for your agents
- Claude Code

- Skills for your agents
- Claude Code

- Skills for your agents
- Claude Code