~/codigoagente
AI agents

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

Open Code Review flow in GitHub Actions: trigger, full-history checkout, install, range-mode review and comment posting
The pattern every CI integration in the project follows (diagram in Spanish) · codigoagente

Every recipe (GitHub, GitLab, Gerrit, Bitbucket, GitFlic, Codeup) follows the same outline:

  1. A PR or MR event triggers the job.
  2. ocr is installed with npm (runners are ephemeral).
  3. The LLM is configured with ocr config set from CI secrets.
  4. 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 ``

  1. 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

terminalsh
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.yml

2. Create secrets and variables

Under Settings → Secrets and variables → Actions:

NameTypeExamplePurpose
`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):

.github/workflows/ocr-review.ymlyaml
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-review or @open-code-review. Comments from bots and strangers don't spend your quota.
  • pull_request_target is 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: under pull_request_target they would have access to your secrets.
  • concurrency cancels the previous review of the same PR when a new push arrives.
  • The composite action alibaba/open-code-review@main installs ocr, 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:

snippetyaml
      - 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'
InputDefaultWhat it does
`language``English`Comment language
`effort`(medium)`low`, `medium` or `high`: 1, 2 or 3 passes
`max_tokens_budget`unlimitedToken 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`8Parallel 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:

snippetyaml
      - 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.json

5. Findings in Security → Code scanning (SARIF)

If you'd rather see findings in GitHub's security tab than as comments:

snippetyaml
      - 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.sarif

fetch-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

terminalsh
curl -o .gitlab-ci.yml \
  https://raw.githubusercontent.com/alibaba/open-code-review/main/examples/gitlab_ci/.gitlab-ci.yml

If 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:

VariableRequiredMaskedDescription
`OCR_LLM_URL`YesNoModel endpoint
`OCR_LLM_AUTH_TOKEN`YesYesProvider key
`OCR_LLM_MODEL`YesNoModel
`GITLAB_API_TOKEN`RecommendedYesToken with `api` scope to post comments; falls back to `CI_JOB_TOKEN`

The core of the job:

.gitlab-ci.ymlyaml
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.json

The 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_anthropic is hardcoded to false in 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:

  1. max_tokens_budget (or --max-tokens-budget): a hard cap per run.
  2. effort: low: one pass per group, the biggest saver.
  3. incremental or checkpoint_range: don't re-run the full review on every push.
  4. exclude in rule.json: drop migrations, generated code and fixtures.
  5. On-demand trigger: remove synchronize from the events and let the team comment /open-code-review when they want a fresh review.
  6. Lower review_concurrency if 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

Repository in this article
The code this article covers is public: browse it, star it or clone it from here.

// implementation

Need this automation running in your business?
Don't spend weeks wiring servers, APIs and agents. Safe Build Labs, the agency that sponsors codigoagente, builds it for you, production-ready.
We tested mcp-builder, Anthropic's official skill for building MCP servers: one for a small shop in 99 seconds for 0.25 USD, plus two version bugs to avoid.
  • Skills for your agents
  • Claude Code
read article
We tested webapp-testing, Anthropic's official skill that tests your local web app with Playwright: setup, with_server.py, two real pitfalls and what it costs.
  • Skills for your agents
  • Claude Code
read article
video-shotcraft is an Apache-2.0 skill with 150 shots that lets Claude Code build your product's promo video in Remotion. How it works and what it costs.
  • Skills for your agents
  • Claude Code
read article