An AI code review CLI that runs as a Job in GitLab Merge Request pipelines.
This project does not run a separate review server. Instead, each GitLab repository runs an MR pipeline job that pulls the Docker image and executes the ai-code-review CLI. The CLI reads MR changes through the GitLab API, requests a review from Gemini, and writes an MR summary note and added-line comments back to GitLab.
Currently using Gemini 3.1 Flash Lite model - offers a generous free token limit, fast speed, and strong performance among lightweight models.
- Reads MR metadata, latest diff version, and changed files via GitLab API.
- Maps only commentable added lines for inline review comments.
- Adds GitLab suggestion blocks to added-line comments when Gemini returns safe replacement code for the current added line.
- Reads bounded local workspace context around changed files to improve diff understanding.
- Generates a structured review result from Gemini with MR-level code summary, actionable line-level findings, and review limitations/notes.
- Creates MR draft notes (summary + line comments).
- Prevents duplicate reviews on the same
head_shaby default, with optional override viaAI_REVIEW_FORCE=true. - Performs stale
head_shachecks before posting/publishing to avoid outdated comments. - Handles large MRs with policy-based behavior: normal line review within limits, summary-only mode on soft-limit overflow, and hard skip on hard-limit overflow.
- Filters non-reviewable files (lock/generated/binary/build outputs, etc.) to reduce noise.
- Supports composable rule packs:
default(always applied),spring,node-express,react-nextjs,python-django-fastapi,nestjs,go-gin-echo,vue-nuxt. - Supports comment-volume controls with
AI_REVIEW_MAX_COMMENTSandAI_REVIEW_MAX_COMMENTS_PER_FILE. - Keeps review non-blocking (
allow_failure: true) so pipeline progress is not blocked by AI outages. - Always writes a run artifact (
ai-review-result.json) for traceability.
GitLab Merge Request
|
v
MR Pipeline
|
v
ai-code-review job
|
v
Docker image: ghcr.io/squatboy/gitlab-code-review:<tag>
|
v
ai-code-review CLI
|
+--> GitLab API: read MR metadata, diff version, and changed files
|
+--> Gemini API: generate structured review result
|
+--> GitLab API: create draft notes and bulk publish
|
+--> save ai-review-result.json artifact
Execution path for script: ai-code-review:
bin.ai-code-reviewinpackage.json/usr/local/bin/ai-code-reviewinside the Docker image- Symlink target
/app/dist/cli.js - Source entrypoint
src/cli.ts - Review execution logic
src/review/runner.ts
Add the following job to .gitlab-ci.yml in the target repository.
ai-code-review:
stage: test
image: ghcr.io/squatboy/gitlab-code-review:v0.1.0-rc3
variables:
AI_REVIEW_ENABLED: "true"
AI_REVIEW_FORCE: "false"
AI_REVIEW_LANGUAGE: "en"
AI_REVIEW_MODEL: "gemini-3.1-flash-lite-preview"
AI_REVIEW_MAX_COMMENTS: "10"
AI_REVIEW_MAX_COMMENTS_PER_FILE: "3"
AI_REVIEW_RULE_PACKS: "default"
AI_REVIEW_RESULT_PATH: "ai-review-result.json"
allow_failure: true
script:
- ai-code-review
artifacts:
when: always
expire_in: 7 days
paths:
- ai-review-result.json
rules:
- if: '$AI_REVIEW_ENABLED == "false"'
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_PROJECT_ID == $CI_PROJECT_ID && $CI_MERGE_REQUEST_TITLE !~ /^(Draft:|WIP:)/'
when: on_success
- when: never
tags:option is required in the environment where the gitlab runner not accepting untagged jobs.
Register these variables as group-level variables by default.
AI_CODE_REVIEW_GITLAB_TOKEN
AI_REVIEW_API_KEY
AI_CODE_REVIEW_GITLAB_TOKEN:
- Recommended: GitLab Group Access Token
- Alternative: Personal Access Token from a dedicated bot account
- Role: Developer or higher in the target project
- Scope:
api - Purpose:
- Read MRs
- Read MR discussions/notes
- Create draft notes
- Bulk publish draft notes
- Do not mark it as a protected variable if it must be available to same-project internal MR pipelines.
AI_REVIEW_API_KEY:
- Raw Gemini API key
- Recommended GitLab CI variable settings: masked/hidden
| Variable | Default | Description |
|---|---|---|
AI_REVIEW_ENABLED |
true |
If false, the job skips the review internally. |
AI_REVIEW_FORCE |
false |
If true, the same head_sha is reviewed again. |
AI_REVIEW_LANGUAGE |
en |
Review result language. |
AI_REVIEW_MODEL |
gemini-3.1-flash-lite-preview |
Gemini model name. |
AI_REVIEW_MAX_COMMENTS |
10 |
Maximum number of line comments per MR. |
AI_REVIEW_MAX_COMMENTS_PER_FILE |
3 |
Maximum number of line comments per file. |
AI_REVIEW_RULE_PACKS |
default |
Rule packs to apply. Comma-separated string. default is always applied. |
AI_REVIEW_RESULT_PATH |
ai-review-result.json |
Result artifact file path. |
Supported rule packs:
default: universal baseline for correctness, security, integrity, and regression risk.spring: Spring/JPA/MyBatis/REST-focused checks.node-express: Node.js/Express API and middleware-focused checks.react-nextjs: React/Next.js rendering, boundary, and API contract checks.python-django-fastapi: Python Django/FastAPI ORM, validation, and auth checks.nestjs: NestJS module/DI boundaries, guard/interceptor/pipe flow, DTO validation, and auth checks.go-gin-echo: Go Gin/Echo context timeout/cancellation, goroutine safety, validation, and API contract checks.vue-nuxt: Vue/Nuxt SSR-CSR boundary, hydration mismatch, route guard/auth flow, and XSS risk checks.
Notes:
- Unknown rule packs are ignored and the review continues.
- Ignored rule packs are reported in the review summary limitations.
- Example:
AI_REVIEW_RULE_PACKS=spring,nestjs,go-gin-echo
- Run only in MR pipelines.
- Exclude fork MRs.
- Exclude MRs with titles starting with
Draft:orWIP:. - If a summary note already exists for the same
head_sha, skip withduplicate_head_sha. - If
AI_REVIEW_FORCE=true, review the samehead_shaagain. - Write line comments only on added lines.
- Attach suggestions only to added-line comments when exact replacement code is available for the current added line.
- Suggestion replacement code may contain multiple lines, but multi-line source range replacement is out of scope.
- Do not comment on deleted lines, context lines, or multi-line ranges.
- Do not block the pipeline when AI review fails.
- Always write the execution result to the
ai-review-result.jsonartifact.
Install dependencies:
pnpm installBuild:
pnpm buildTest:
pnpm testLint:
pnpm lintLocal non-network smoke:
AI_REVIEW_ENABLED=false pnpm devExpected result:
- The CLI runs.
ai-review-result.jsonis created.- The status is recorded as
skipped.
The GitHub Actions workflow publishes multi-arch images to GHCR after quality checks pass.
- Pull requests: run
pnpm build,pnpm test,pnpm lint, and Docker build validation without pushing. mainpush: pushghcr.io/squatboy/gitlab-code-review:sha-<short_sha>.v*tag push: push only the matching version tag, such asghcr.io/squatboy/gitlab-code-review:v0.1.0-rc3.latestis not used.
Build and push the current validation image manually only when needed.
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t ghcr.io/squatboy/gitlab-code-review:v0.1.0-rc3 \
--push .Check the manifest:
docker manifest inspect ghcr.io/squatboy/gitlab-code-review:v0.1.0-rc3