Skip to content

windy-bing/ci-code-review

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CI Code Review Agent

gcr 是一个面向 GitLab Merge Request 和 GitHub Pull Request 的代码审查 CLI。它读取 Git diff,运行确定性规则和可选的 LLM review,输出 JSON/Markdown 报告,并将 finding 发布为平台评论。

当前项目不是 GitLab Marketplace 插件,也不需要安装到 GitLab 服务端。实际接入方式是:在 CI Runner 中执行 gcr review,再执行 gcr post

工作流

Merge Request
    |
    v
GitLab Runner checkout 完整 Git 历史
    |
    +--> gcr review --from target --to commit
    |       |
    |       +--> Git diff
    |       +--> 确定性规则
    |       +--> 可选 LLM + Skill + Constraints
    |       +--> review-report.json / review-report.md
    |
    +--> gcr post --platform gitlab
            |
            +--> inline discussion
            +--> summary note

review 本身不访问 GitLab 或 GitHub。平台只影响最后的 post 步骤,因此可以先在本地生成和检查报告,再决定是否发布。

快速开始

1. 构建

需要 Go 1.21 或更高版本:

go build -o gcr ./cmd/gcr

预构建二进制

发布仓库的 v* tag 会通过 .github/workflows/release.yml 构建 Linux amd64、Linux arm64 和 macOS arm64 二进制,并发布到 GitHub Release。也可以本地构建:

VERSION=v0.1.0 ./scripts/build-release.sh
cd dist && shasum -a 256 -c checksums.txt

业务仓库不需要包含 cmd/gcr 时,在 GitLab CI/CD Variables 中设置 GCR_BINARY_URL,CI 会优先下载该 URL;未设置时继续从 ./cmd/gcr 源码构建:

GCR_BINARY_URL=https://github.com/hh/ci-code-review-agent/releases/download/v0.1.0/gcr-v0.1.0-linux-amd64

下载的二进制应来自受信任的 release 或内部制品库。生产环境建议同时增加 checksum 校验,并固定版本,不要使用 latest

2. 本地运行确定性 review

必须显式指定比较基线:

./gcr review \
  --repo . \
  --from main \
  --to HEAD \
  --out review-report.json \
  --markdown review-report.md

默认不会因为 finding 让进程失败。只有传入 --fail-on-p0 时,存在 P0 finding 才会以退出码 2 结束。

3. 本地发布到 GitLab MR

export CI_SERVER_URL=https://gitlab.example.com
export CI_PROJECT_ID=123
export CI_MERGE_REQUEST_IID=45
export GITLAB_API_TOKEN="$GITLAB_REVIEW_TOKEN"

./gcr post --platform gitlab --report review-report.json

Token 不要写进命令行参数、仓库文件或日志。生产环境应使用 GitLab masked/protected CI variable。

GitLab 接入

CI 配置

examples/gitlab/.gitlab-ci.yml 复制到目标业务仓库根目录并提交。GitLab 只会自动识别仓库根目录的 .gitlab-ci.yml,不会自动执行本项目 examples/ 下的示例文件。

示例配置的核心内容如下:

stages:
  - review

code-review:
  stage: review
  image: golang:1.21
  interruptible: true
  resource_group: code-review-$CI_MERGE_REQUEST_IID
  only:
    - merge_requests
  variables:
    GIT_DEPTH: "0"
  script:
    - go build -o gcr ./cmd/gcr
    - |
      ./gcr review \
        --from "origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" \
        --to "${CI_COMMIT_SHA}" \
        --out review-report.json \
        --markdown review-report.md
    - ./gcr post --platform gitlab --report review-report.json
  artifacts:
    when: always
    paths:
      - review-report.json
      - review-report.md

如果业务仓库没有 cmd/gcr 源码,需要在 CI 中先下载预构建的 gcr,或将本项目作为独立 checkout。当前示例假设 gcr 的源码就在业务仓库内。

必需的 GitLab CI 变量

gcr post --platform gitlab 使用以下变量:

变量 用途
CI_SERVER_URL GitLab 地址;GitLab CI 会自动提供
CI_PROJECT_ID 当前项目 ID;GitLab CI 会自动提供
CI_MERGE_REQUEST_IID 当前 MR IID;只在 MR pipeline 中提供
GITLAB_API_TOKEN 推荐使用,Token 需要 api scope
CI_JOB_TOKEN 没有 GITLAB_API_TOKEN 时的备用 Token

推荐在项目的 Settings > CI/CD > Variables 中添加:

Key: GITLAB_API_TOKEN
Value: <project access token or bot user token>
Options: Masked, Protected

使用 CI_JOB_TOKEN 时,要确认 GitLab 版本和项目设置允许 Job Token 调用目标项目的 Merge Request API。遇到 401403 时,优先改用具有 api scope 的 masked token。

配置 GitLab Runner

GitLab CE 容器只提供 GitLab 服务端,不会自动提供 Runner。没有 Runner 时,Pipeline 会被创建,但长期处于 pending

在一台能访问 GitLab 的机器上安装 GitLab Runner,然后:

  1. 进入项目 Settings > CI/CD > Runners,创建 project runner。
  2. 记录 runner authentication token。
  3. 使用 Docker executor 注册 Runner:
docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

docker exec -it gitlab-runner gitlab-runner register \
  --non-interactive \
  --url "http://192.168.1.11" \
  --token "<runner-authentication-token>" \
  --executor "docker" \
  --docker-image "golang:1.21" \
  --description "gcr-review-runner" \
  --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"
  1. 确认 Runner 的 tags、protected 设置和项目可见范围允许该 MR pipeline 使用它。
  2. 重新推送一个 commit 或在 Pipeline 页面重试任务。

Runner 的 Docker executor 需要能够拉取 golang:1.21。如果 Runner 所在机器不能访问 Docker Hub,应配置镜像代理或使用内部镜像,并把 CI 文件中的 image 改成可访问的镜像。

GitLab 发布行为

  • path 和新增行号的 finding 会尝试发布为 inline discussion。
  • 无法定位到新增行的 finding 会进入 summary note。
  • 最后总会发布一条 summary note,包含通过状态、各级别 finding 数量和 inline/summary 数量。
  • GitLab API 请求超时时间为 20 秒,CLI 的 post 总超时时间为 30 秒。

一次验证后的 MR 页面效果如下,包含 inline finding 和 review summary:

GitLab MR code review result

LLM 模型配置

LLM review 只有传入 --llm 才会启用。未启用时仍会运行确定性规则。

Ollama

Ollama 使用原生 API 地址,不要在 URL 后面追加 /v1

export GCR_LLM_PROVIDER=ollama
export GCR_LLM_BASE_URL=http://localhost:11434
export GCR_LLM_MODEL=Qwen2.5-14B-Instruct-Q5_K_M:latest

./gcr review --from main --to HEAD --llm

在 GitLab Runner 中使用 Ollama 时,localhost 指向 Runner job 容器,而不是宿主机。应使用 Runner 能访问的地址,例如宿主机网关、局域网地址或内部 Ollama 服务地址。

OpenAI-compatible API

export GCR_LLM_PROVIDER=openai
export GCR_LLM_BASE_URL=https://api.openai.com
export GCR_LLM_MODEL=gpt-4o-mini
export GCR_LLM_API_KEY="$OPENAI_API_KEY"

./gcr review --from main --to HEAD --llm

也支持其他 OpenAI-compatible gateway,只需将 GCR_LLM_BASE_URL 设置为该 gateway 的地址。

配置优先级

命令行参数优先于环境变量,环境变量优先于默认值:

配置 命令行参数 环境变量 默认值
Provider --llm-provider GCR_LLM_PROVIDER ollama
Base URL --base-url GCR_LLM_BASE_URL http://localhost:11434
Model --model GCR_LLM_MODEL Qwen2.5-14B-Instruct-Q5_K_M:latest
API key GCR_LLM_API_KEY,其次 OPENAI_API_KEY
Diff 上限 --max-diff-bytes 60000
Review 超时 --timeout 10m

模型输出必须是 JSON。若模型不可用、路由结果无法解析或结果无法定位到实际 diff,程序会记录 warning,并跳过对应 LLM finding;确定性规则仍会保留。

Skill 配置

Skill 是 Markdown 文件,文件名必须是 SKILL.md。默认目录是当前工作目录下的 skills/,也可以通过 --skills-dirGCR_SKILLS_DIR 指定一个目录。

Skill 文件格式

---
name: database
category: performance
languages: [go, java]
description: 数据库查询、连接池和事务边界。
---

审查新增代码中的 N+1 查询、无界结果集、连接泄漏、事务范围错误和缺少超时。
只报告能从当前 diff 定位的问题。

字段说明:

  • name:Skill 名称,LLM 路由返回的名称必须匹配它。
  • category:用于向模型描述 Skill 类型。
  • languages:语言列表;* 表示所有语言。
  • description:Skill 索引中的简短描述。
  • front matter 后的正文:真正发送给 review 模型的审查指导。

Skill 如何被选择

启用 --llm 后,流程是:

  1. 按优先级加载各层目录中的所有 SKILL.md
  2. 根据 diff 中的语言和 Skill 索引,让模型返回相关 Skill 名称。
  3. 只将模型选中的 Skill 正文传给 review 模型。
  4. 将约束文本、选中的 Skill 和 diff 一起发送给 review 模型。

没有可用 Skill、Skill 路由失败或模型返回非法 JSON 时,会退回通用 senior-engineer review guidance,不会自动假设某个 Skill 一定相关。

全局、项目、个人 Skill 的配置建议

当前实现会自动合并多层 Skill,同名 Skill 由后面的高优先级层覆盖前面的定义。优先级从低到高为:仓库内置 skills/、全局 ~/.gcr/skills/、企业共享目录、个人 ~/.gcr/personal-skills/、企业项目目录、项目 .gcr/skills/

全局共享 Skill:

export GCR_SKILLS_DIR=/opt/gcr/skills
./gcr review --llm --from main --to HEAD

企业项目 Skill 放在:

/opt/gcr/skills/projects/<CI_PROJECT_PATH>/SKILL.md

例如 CI_PROJECT_PATH=group/service-a 时,目录为 /opt/gcr/skills/projects/group/service-a/GCR_SKILLS_DIR 也可以通过 review 参数 --skills-dir 指定,命令行参数优先于环境变量。

项目专用 Skill:在项目中维护 .gcr/skills/,并在 CI 中显式指定:

./gcr review \
  --llm \
  --skills-dir .gcr/skills \
  --from "origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" \
  --to "${CI_COMMIT_SHA}"

如果项目 Skill 还需要内置 Skill,应将需要的内置文件复制到项目目录,或者在 CI 阶段组装一个合并后的临时目录。不要依赖本地开发机的 ~/ 内容影响 CI。

个人本地 Skill:

mkdir -p ~/.gcr/personal-skills/my-team
cp my-personal-skill/SKILL.md ~/.gcr/personal-skills/my-team/SKILL.md

个人 Skill 会参与本地自动合并,但不会进入没有同样 HOME 目录的团队 CI。建议把团队规则放进仓库或受控的共享目录,把个人偏好只用于本地 review。

Constraints 配置

Constraints 与 Skill 不同:Constraints 是 review 的总体规则和组织要求,当前实现支持多层加载,并且后加载层覆盖先加载层。

优先级从低到高如下:

  1. 内置规则:程序内置 senior-engineer guidance
  2. 全局:~/.gcr/constraints.md
  3. 企业全局:<constraints-dir>/global.md
  4. 个人:~/.gcr/personal.md
  5. 企业项目:<constraints-dir>/projects/<project>/constraints.md
  6. 项目:<repo>/.gcr/constraints.md
  7. 一次性自定义:--constraints-file <path>

--constraints-file 是最高优先级且必须存在;文件不存在会直接报错。constraints-dir 可通过 --constraints-dirGCR_CONSTRAINTS_DIR 设置。项目名优先取 CI_PROJECT_PATH,其次 GITHUB_REPOSITORY,最后取仓库目录名。

推荐目录结构

/opt/gcr/constraints/
├── global.md
└── projects/
    └── group/
        └── service-a/
            └── constraints.md

~/.gcr/
├── constraints.md
└── personal.md

project/
└── .gcr/
    └── constraints.md

示例

~/.gcr/constraints.md 或企业全局约束:

- 所有网络调用必须有超时,并且错误必须可观测。
- 不允许在日志中输出 token、密码或完整用户隐私数据。

项目约束 .gcr/constraints.md

- Public API 行为必须保持向后兼容。
- 数据库 schema 变更必须提供回滚或兼容迁移方案。
- 新增并发代码必须说明生命周期和取消路径。

CI 中使用企业约束:

./gcr review \
  --llm \
  --constraints-dir /opt/gcr/constraints \
  --from "origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}" \
  --to "${CI_COMMIT_SHA}"

重要:约束是追加文本,不是结构化规则引擎。发生冲突时,模型会收到“后面的高优先级约束覆盖前面的约束”的说明,但最终仍依赖模型遵循文本。高风险安全规则应同时使用确定性规则或 CI gate,不要只依赖 LLM。

规则配置与确定性扫描

当前内置规则只检查新增行:

Rule 级别 说明
secret-detection P0 疑似 token、password、API key 或 secret
private-key P0 私钥头部
dynamic-execution P2 新增 eval(exec(
todo-fixme P4 新增 TODO/FIXME
long-line P4 新增行超过 180 字符
large-change P3 单文件新增超过 400 行

可以使用 JSON 文件启停内置规则、调整严重级别和修改长行阈值:

{
  "rules": [
    {"id": "todo-fixme", "enabled": false},
    {"id": "long-line", "severity": "P3", "max_length": 160},
    {"id": "dynamic-execution", "severity": "P1"}
  ]
}

运行时指定:

./gcr review \
  --rules-file .gcr/rules.json \
  --from main \
  --to HEAD

未知 rule ID、非法 severity 或负数 max_length 会直接报错。规则文件只覆盖当前内置规则,不会动态执行 JSON 中的代码。

外部命令通过参数接入:

./gcr review \
  --from main \
  --to HEAD \
  --lint-cmd "go vet ./..." \
  --test-cmd "go test ./..." \
  --security-cmd "semgrep --config=auto ."

Semgrep 和 npm audit 可以输出结构化 finding。命令必须显式输出 JSON:

./gcr review \
  --from main \
  --to HEAD \
  --security-cmd "semgrep --config=auto --json ."

./gcr review \
  --from main \
  --to HEAD \
  --security-cmd "npm audit --json"

Semgrep finding 会使用结果中的 rule ID、path、start/end line、message、snippet 和 severity;npm audit finding 会定位到 package.json,并保留依赖名、漏洞等级和 advisory 标题/URL。npm audit --json 因发现漏洞而返回非零退出码时,不会额外生成通用 P0;扫描器无法解析或命令本身失败时才会生成检查失败 finding。

普通外部命令失败会被写成 P0 finding,但只有传入 --fail-on-p0 才会让 review 进程以退出码 2 失败。这样可以先以 artifact/comment 形式观察,再逐步启用阻断。

GitHub

GitHub 适配器使用 Pull Request Reviews API 发布 summary 和行级 review comment。需要 token 具备 pull_requests: write 权限:

export GITHUB_TOKEN="$GITHUB_TOKEN"
export GITHUB_REPOSITORY=owner/repo
export GITHUB_PR_NUMBER=123
export GITHUB_SHA=<pull-request-head-sha>

./gcr post --platform github --report review-report.json

GitHub 所需变量:GITHUB_TOKENGITHUB_REPOSITORYGITHUB_PR_NUMBERPR_NUMBERGITHUB_SHA 用于行级 comment 的 commit_id;缺失时程序会读取 PR head SHA。没有 path/line 的 finding 会保留在 summary 中。如果 Reviews API 失败,程序会回退为普通 PR summary comment。

安全与运行建议

  • 不要把 GCR_LLM_API_KEYOPENAI_API_KEYGITLAB_API_TOKENGITHUB_TOKEN 写入仓库。
  • Token 使用 masked/protected CI variables,并限制 project、scope 和有效期。
  • Runner job 使用完整 Git 历史,必须保留 GIT_DEPTH: "0",否则 origin/<target-branch> 可能不存在。
  • LLM review 只处理 diff,且默认最多发送 60000 bytes;大改动会被截断。
  • review-report.jsonreview-report.md 作为 always artifact,方便排查 comment 发布失败。
  • P0/P1 gate、密钥扫描和依赖扫描等强约束应使用可复现的确定性工具;LLM 适合作为补充审查。

故障排查

Pipeline 一直是 pending

项目没有可用 Runner,或 Runner tag/protected 设置不匹配。先在 Settings > CI/CD > Runners 确认 Runner 在线,并确保它允许执行该 MR pipeline。

missing GitLab configuration

确认运行在 MR pipeline 中,并检查 CI_PROJECT_IDCI_MERGE_REQUEST_IIDGITLAB_API_TOKEN。本地运行时需要手动设置这些变量。

no MR versions 或 inline comment 失败

GitLab 需要 MR diff version 的 base/start/head SHA。确认 MR 有有效 diff,并且 Runner checkout 不是 shallow clone。无法定位的 finding 会退回 summary note。

LLM review 没有 finding

检查 --llm 是否传入、Runner 能否访问 GCR_LLM_BASE_URL、模型是否支持返回 JSON,以及 --skills-dir 下的 SKILL.md front matter 是否完整。LLM 失败不会阻止确定性 review 继续运行,但会在报告 warnings 中记录。

目录说明

cmd/gcr/main.go                 CLI 入口
internal/review                 确定性规则和 LLM review
internal/skill                  Skill 解析、语言识别和路由结果过滤
internal/rules                  确定性规则配置
internal/constraints            多层约束加载
internal/platform/gitlab        GitLab inline/summary 发布
internal/platform/github        GitHub summary 发布
internal/report                 JSON/Markdown 报告
examples/gitlab/.gitlab-ci.yml  GitLab CI 示例
examples/github/code-review.yml GitHub Actions 示例
scripts/build-release.sh        跨平台二进制构建和 checksum
.github/workflows/release.yml   tag release workflow

当前限制与后续方向

  • Skill 已支持仓库、全局、企业、个人、企业项目和项目层级合并;同名 Skill 由高优先级层覆盖。
  • GitLab CI 支持通过 GCR_BINARY_URL 下载独立二进制,也兼容源码构建。
  • GitHub 已支持 Pull Request Reviews API 的行级 comment,并在失败时回退 summary。
  • 当前规则配置使用 JSON;Semgrep 和 npm audit 支持 JSON 输出解析。其他扫描器和平台仍需增加对应适配器。

About

review agent

Resources

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors