1. 项目概述:一个真正能落地的开源代码审查 CLI 工具到底长什么样?
“open-code-review”这四个字乍看像一句口号,但在我过去三年深度参与十几个中大型研发团队代码质量体系建设的过程中,它其实指向一个非常具体、非常痛的现实问题:我们每天都在写 PR,却没人真正在看代码。不是不想看,是看不过来——一个中等复杂度的微服务模块,一次合并请求动辄 300+ 行变更,涉及 5 个文件、3 个业务逻辑层、2 个数据模型调整。资深工程师花 20 分钟走查,可能漏掉一个空指针隐患;新人 Reviewer 看半天,最后只敢回一句“看起来没问题”。而所谓“自动化工具”,要么是静态扫描器(只报 syntax error 和 trivial warning),要么是套着 LLM 外壳的玩具——输入一段代码就吐出“建议优化变量命名”,连当前函数所在的上下文都搞不清。
这就是 open-code-review 的真实定位:它不是一个“用 LLM 做代码 review”的概念验证 Demo,而是一个严格遵循 Git 工作流、深度绑定代码变更上下文、默认关闭所有敏感信息外泄通道、可嵌入 CI/CD 流水线的生产级 CLI 工具。它不依赖云端 API 密钥,不上传源码到第三方服务器,不强制你注册账号,甚至不需要联网——核心能力全部基于本地运行的轻量级 LLM 模型(如 Phi-3、TinyLlama 或量化后的 CodeLlama-3B)完成。关键词 “CLI” 决定了它的使用姿势:git diff --cached | open-code-review --rule=security --format=markdown,一条命令,直接输出带行号标注、风险等级标记、修复建议的 Review 报告。它和git commit一样,是你敲完代码后顺手执行的下一步,而不是另开一个网页、登录一个平台、等待 10 秒 API 响应的额外负担。
这个项目解决的不是“有没有 AI 能看代码”的问题,而是“如何让 AI 审查真正成为开发流程里不可跳过的、零摩擦的一环”。它面向三类人:一线开发者(想快速确认自己改的代码有没有低级错误)、Tech Lead(需要批量扫描历史提交找共性缺陷模式)、DevOps 工程师(要把代码质量卡点嵌入 Jenkins/GitLab CI)。它不教你怎么用 LLM,它只做一件事:把 LLM 的推理能力,严丝合缝地焊进你每天敲的git命令里。后面你会看到,从模型选型到 prompt 设计,从 diff 解析到上下文截断策略,每一个决策背后,都是过去踩过的真实坑——比如某次误把.env文件 diff 当普通代码喂给模型,导致密钥被模型缓存并意外输出在日志里;又比如某次没限制 token 长度,模型把整个node_modules的 package.json 都当上下文读了,直接 OOM 崩溃。这些,都会在后续章节里掰开揉碎讲清楚。
2. 整体架构设计与核心思路拆解:为什么必须是 CLI?为什么必须离线?
2.1 CLI 是唯一能穿透开发工作流的形态
很多人一听到“代码审查 + LLM”,第一反应是做个 Web UI 或 VS Code 插件。这看似更友好,实则违背了工程落地的根本逻辑。我带过的两个团队做过对比实验:A 组用 Web 平台,要求每个 PR 必须上传后手动触发 AI Review;B 组用 CLI 工具,集成进 pre-commit hook。结果三个月后,A 组的 AI Review 使用率是 37%,且集中在新功能模块;B 组是 92%,覆盖了包括配置文件修改、SQL 脚本更新、甚至 README.md 编辑在内的所有提交类型。差距在哪?摩擦成本。Web 平台需要你:① 切出 IDE;② 打开浏览器;③ 找到对应 URL;④ 等待页面加载;⑤ 手动粘贴 diff 或选择分支;⑥ 点击按钮;⑦ 等待响应;⑧ 回到 IDE 处理结果。而 CLI 只需:git add . && git commit -m "fix: user auth timeout"—— 提交动作本身已触发审查,结果直接打印在终端,有问题就git commit --amend,没问题就git push。没有额外步骤,没有上下文切换,它就是git的一部分。
提示:open-code-review 的 CLI 设计严格遵循 Unix 哲学——“一个程序只做一件事,并把它做好”。它不处理 Git 仓库初始化,不管理用户权限,不存储历史报告。它只接收标准输入(stdin)的 diff 内容或指定路径的文件列表,输出结构化文本(JSON/Markdown/Plain),其余交给 shell 管道或 CI 脚本组合。这种解耦让它能无缝接入任何现有流程:Jenkins 里加一行
sh 'git diff HEAD~1 | open-code-review --rule=perf';GitLab CI 的before_script里写open-code-review --path=src/main/java --rule=security;甚至 Windows 的批处理文件里也能调用open-code-review.exe --help查看选项。它的存在感应该像grep或jq一样,透明、可靠、无感。
2.2 离线运行是安全底线,不是可选项
热搜词里反复出现的“使用 LLM 时如何防止密钥等鉴权信息泄露”,直指行业最大痛点。去年某金融客户上线类似工具后,因未做 diff 内容清洗,模型将config.yaml中的数据库密码作为上下文学习,后续在生成建议时意外复述——虽未外发,但日志留存已构成合规风险。open-code-review 的离线设计,本质是把安全控制权交还给开发者。它不调用 OpenAI/Claude 的 API,不连接任何外部模型服务,所有推理均在本地完成。这意味着:
- 源码不出内网:diff 内容只存在于内存,处理完即释放,不写临时文件,不上传云端;
- 模型权重可控:你下载的是经过安全审计的 GGUF 格式量化模型(如
codellama-3b.Q4_K_M.gguf),可校验 SHA256 值,可自行替换为内部训练的领域专用模型; - Prompt 逻辑透明:所有提示词(prompt)都放在
templates/目录下,明文可读、可编辑、可审计。例如security.jinja2模板里明确写着{{ diff_content }},但前面加了硬编码过滤规则:“移除所有匹配password:.*|secret_key:.*|api_key:.*的行,替换为password: [REDACTED]”。
这不是技术保守,而是对责任的清醒认知。LLM 不是黑箱神谕,它是你部署在自己机器上的一个计算组件,就像gcc编译器一样——你得知道它吃什么、怎么吃、吃完了吐什么。后面会详细展开,如何用git diff --no-prefix配合正则预处理,确保传给模型的 diff 片段里,连// TODO: fix this这样的注释都不会泄露业务逻辑关键词。
2.3 “Open” 的真实含义:开放模型、开放规则、开放扩展点
“open-code-review”里的 “open”,绝非仅指“开源代码”。它有三层实质含义:
开放模型接口(Open Model Interface):工具底层抽象出
ModelRunner接口,支持多种后端:llama.cpp(CPU/GPU)、Ollama(Docker 封装)、甚至自建 vLLM 服务(需配置--model-url http://localhost:8000/v1/chat/completions)。你不用改一行业务逻辑,就能把 Phi-3 换成 DeepSeek-Coder-1.3B,只需改一个--model-path参数。这种设计源于我们曾遇到的真实需求——某客户因合规要求,必须用国产芯片运行模型,而 llama.cpp 对昇腾 NPU 的支持比 Ollama 更成熟。开放审查规则(Open Rule Engine):所有检查项(rule)都是独立的 YAML 文件,存于
rules/目录。security.yaml定义 SQL 注入、硬编码密钥检测;perf.yaml关注循环嵌套、N+1 查询;readability.yaml检查函数长度、圈复杂度阈值。你可以新增custom-business.yaml,写入“所有支付相关方法必须包含@Transactional注解”的规则,工具会自动解析 Java AST 并匹配。规则引擎不依赖 LLM,而是用成熟的静态分析库(如 tree-sitter)做前置过滤,LLM 只负责对筛选出的高危片段做语义研判——这大幅降低误报率,也避免把简单语法检查也扔给大模型。开放扩展点(Open Extension Points):提供
--hook-pre和--hook-post参数,允许你指定自定义脚本。例如--hook-pre ./scripts/clean-env-diff.sh,该脚本会先执行sed '/^+.*\.env/d'删除所有.env相关变更行;--hook-post ./scripts/send-to-feishu.py,将 Markdown 报告转成飞书卡片推送。这种设计让工具能适应任何企业定制化流程,而不必 fork 仓库改源码。
3. 核心细节解析与实操要点:从 diff 解析到模型喂养的全链路
3.1 Diff 解析:不是简单git diff,而是精准提取“变更意图”
LLM 审查代码的最大陷阱,是把原始 diff 当作文本喂进去。真正的 diff 包含大量噪音:--- a/src/main/java/UserService.java、+++ b/src/main/java/UserService.java、@@ -123,5 +123,7 @@ public class UserService {。这些元信息对人类有用,对模型却是干扰。open-code-review 的 diff 解析器做了三重净化:
第一层:语义化剥离
调用git diff --no-prefix --unified=0(-U0参数生成最小化上下文),再用正则提取+开头的新增行、-开头的删除行,并按文件分组。关键点在于:保留行号偏移,但丢弃绝对路径。例如:
src/main/java/UserService.java + public void updateUser(User user) { + if (user == null) throw new IllegalArgumentException("user cannot be null"); + userRepository.save(user); + }模型看到的不是完整文件路径,而是UserService.java#updateUser,配合后续的 AST 分析,能准确定位到方法级上下文。
第二层:敏感信息动态脱敏
在送入模型前,执行实时正则匹配。内置规则库包含:
- 密钥类:
(?i)(password|secret|key|token|auth|credential).*[:=]\s*["']([^"']+)["']→ 替换为[REDACTED] - 路径类:
/home/[a-z]+/project/.*→ 替换为/path/to/project/... - 业务标识类:
company-[a-z0-9]{8}→ 替换为company-[ID]
注意:脱敏不是简单字符串替换。我们用
re.sub的count=1参数,确保每行最多脱敏一个敏感项,避免误伤passwordResetToken这样的合法变量名。实测下来,某次误配正则导致passwordResetToken被截成passwordReset[REDACTED],引发编译错误——这个坑我们踩过,所以现在所有正则都经过 200+ 个真实 diff 样本测试。
第三层:上下文智能裁剪
LLM 的上下文窗口有限(Phi-3 仅 4K tokens),而一个 PR 可能涉及 20 个文件。工具采用“变更密度”算法:对每个文件计算(新增行数 + 删除行数) / 总行数,优先保留密度 > 0.1 的文件;对单个文件,只取变更行前后各 3 行代码(通过git show :<commit>:<file>获取原始内容),并用 AST 解析器(tree-sitter-java)提取所属类、方法、注解信息,生成结构化上下文描述。例如:
File: UserService.java Method: updateUser Signature: public void updateUser(User user) Annotations: @Transactional, @PreAuthorize("hasRole('ADMIN')") Parent Class: BaseService这段描述只有 87 个 tokens,却比 50 行原始代码更能告诉模型“这段代码在做什么”。
3.2 Prompt 工程:让 LLM 听懂“审查”而不是“聊天”
很多开源 LLM 代码工具失败,根源在于把 Chat Prompt 直接搬过来。You are a helpful coding assistant这类通用指令,在审查场景下会导致模型过度发挥——它可能给你写一段优雅的重构代码,而不是指出if (user != null)没有处理user.getId() == null的空指针风险。open-code-review 的 prompt 设计遵循“三明治结构”:
顶层指令(Role & Constraint)
You are a senior code reviewer with 10+ years of Java/Spring experience. Your task is ONLY to identify security vulnerabilities, performance anti-patterns, and correctness bugs in the provided code changes. DO NOT: - Suggest refactoring or style improvements unless they directly impact security/performance. - Generate code examples unless explicitly requested by the rule. - Explain basic concepts (e.g., what is SQL injection). - Output anything outside the specified JSON schema.中层上下文(Structured Context)
注入上节生成的结构化信息:
Context: - File: UserService.java - Method: updateUser - Framework: Spring Boot 3.2 - Security Annotations: @PreAuthorize("hasRole('ADMIN')"), @Transactional - Related Files: UserRepository.java (interface), User.java (entity)底层任务(Rule-Specific Task)
根据--rule=security动态加载rules/security.yaml中的检查项:
Check for: 1. Hardcoded credentials in new code (e.g., passwords, API keys) 2. SQL string concatenation without parameterized queries 3. Missing input validation on user-controlled data 4. Insecure deserialization (e.g., ObjectInputStream) Output ONLY in JSON format: {"issues": [{"line": 145, "severity": "CRITICAL", "message": "SQL query built via string concatenation, potential injection risk", "suggestion": "Use JdbcTemplate.update() with parameterized query"}]}这种设计让模型角色极度聚焦。我们做过 A/B 测试:用通用 Chat Prompt,模型对 100 个已知漏洞的检出率是 63%;用三明治 Prompt,提升至 91%。关键差异在于,后者强制模型忽略“如何写得更好”,只回答“这里有没有危险”。
3.3 模型选型与本地部署:为什么选 Phi-3 而不是更大更强的模型?
热搜词里充斥着DeepSeek,CodeLlama,Qwen,但 open-code-review 默认推荐Phi-3-mini-4k-instruct(微软发布的 3.8B 参数模型),原因很实在:
| 维度 | Phi-3-mini | CodeLlama-13B | Qwen2-7B |
|---|---|---|---|
| CPU 推理速度 | 12 tokens/sec (i7-11800H) | 3.2 tokens/sec | 5.8 tokens/sec |
| 显存占用 | 2.1GB (int4) | 8.4GB (int4) | 5.3GB (int4) |
| Java 代码理解准确率 | 89.2% (自测 500 题) | 91.7% | 87.5% |
| 首次响应延迟 | < 1.8s | < 4.3s | < 2.9s |
| 模型体积 | 2.4GB | 8.1GB | 4.7GB |
实操心得:别迷信参数量。我们在某电商客户现场测试发现,CodeLlama-13B 在审查其自研 RPC 框架代码时,因训练数据缺乏该框架注解(如
@RpcService),频繁误判为“未知注解,建议删除”;而 Phi-3 虽小,但其训练数据包含大量 GitHub 开源 Java 项目,对 Spring、MyBatis 等主流框架的语义理解更鲁棒。更重要的是,小模型更容易做量化压缩。我们用 llama.cpp 的q4_k_m量化,Phi-3 体积从 3.8GB 压到 2.4GB,推理速度提升 40%,而 CodeLlama-13B 量化后仍需 7.2GB 显存——这对很多只配了 RTX 3060(12GB)的研发笔记本是硬门槛。
部署步骤极简:
# 1. 下载量化模型(官方 GGUF 格式) wget https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-GGUF/resolve/main/Phi-3-mini-4k-instruct.Q4_K_M.gguf # 2. 创建配置文件 config.yaml model_path: "./Phi-3-mini-4k-instruct.Q4_K_M.gguf" n_ctx: 4096 n_threads: 8 temperature: 0.2 # 低温度保证审查结论稳定,避免“可能有风险”这类模糊表述 # 3. 运行审查(以当前暂存区 diff 为例) git diff --cached | open-code-review --config config.yaml --rule security所有参数均可通过 CLI 覆盖,无需改配置文件。例如临时提高温度做探索性分析:--temperature 0.7。
4. 实操过程与核心环节实现:从零开始跑通第一个审查
4.1 环境准备:Windows/macOS/Linux 全平台统一方案
工具用 Rust 编写(兼顾性能与跨平台),但安装对用户完全透明。我们提供三种方式,按推荐顺序排列:
方式一:一键安装脚本(推荐新手)
# macOS/Linux curl -fsSL https://raw.githubusercontent.com/open-code-review/install/main/install.sh | sh # Windows(PowerShell) Invoke-WebRequest -Uri "https://raw.githubusercontent.com/open-code-review/install/main/install.ps1" -OutFile "install.ps1"; .\install.ps1脚本自动检测系统、下载对应二进制、校验 SHA256、添加到 PATH。全程无交互,30 秒完成。它还会检查是否已安装git,若未安装则给出清晰指引(如 macOS 提示brew install git,Windows 提示下载 Git for Windows)。
方式二:包管理器安装(推荐 DevOps)
# macOS (Homebrew) brew tap open-code-review/tap && brew install open-code-review # Ubuntu/Debian echo "deb [arch=amd64] https://apt.open-code-review.dev stable main" | sudo tee /etc/apt/sources.list.d/open-code-review.list curl -fsSL https://apt.open-code-review.dev/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/open-code-review-archive-keyring.gpg sudo apt update && sudo apt install open-code-review # Windows (Scoop) scoop bucket add open-code-review https://github.com/open-code-review/scoop-bucket.git scoop install open-code-review包管理器方案优势在于可版本锁定(brew install open-code-review@1.2.0)和批量部署(Ansible/Puppet 脚本直接调用apt install)。
方式三:源码编译(推荐深度定制者)
git clone https://github.com/open-code-review/core.git cd core && cargo build --release # 生成 ./target/release/open-code-review编译需 Rust 1.75+,但工具本身不依赖复杂 crate,cargo build通常 2 分钟内完成。我们刻意避免使用tokio等异步运行时,保持二进制纯净——最终产物是单个 8MB 的可执行文件,无 DLL 依赖,拷贝即用。
注意:所有安装方式默认不附带模型。这是 deliberate design(有意为之)。模型体积大(2GB+),且不同用户需求不同(有人要 Python,有人要 Java)。安装后首次运行
open-code-review --list-models会列出推荐模型及下载命令,如open-code-review --download-model phi3-mini,下载过程带进度条和断点续传。
4.2 第一个审查实战:用--rule=security检测硬编码密钥
假设你正在开发一个用户登录接口,不小心把测试环境数据库密码写进了代码:
// src/main/java/AuthController.java @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest req) { // ... 认证逻辑 String dbUrl = "jdbc:mysql://test-db:3306/myapp?user=admin&password=TestPass123!"; Connection conn = DriverManager.getConnection(dbUrl); // 危险! return ResponseEntity.ok().build(); }步骤 1:暂存变更
git add src/main/java/AuthController.java步骤 2:执行审查
git diff --cached | open-code-review --rule security --format markdown预期输出(Markdown 格式):
## 🔒 Security Review Report ### ⚠️ CRITICAL Issue Found **File:** `AuthController.java` **Line:** 42 **Description:** Hardcoded database password in JDBC URL. This credential will be exposed in version control and runtime memory. **Suggestion:** Use Spring Boot's `application.yml` with encrypted properties, or inject via environment variable (`System.getenv("DB_PASSWORD")`). ### ✅ No Other Issues Detected Review completed in 1.42s using Phi-3-mini model.步骤 3:修正并验证
按建议修改代码,移除硬编码,改用@Value("${db.password}")注入。再次执行相同命令,输出变为:
## 🔒 Security Review Report ### ✅ No Issues Detected All changes comply with security best practices. Review completed in 0.98s.这个过程耗时不到 5 秒,比你手动 greppassword=还快,且更准确(能识别jdbc:mysql://...?password=xxx这种 URL 形式)。
4.3 进阶用法:集成到 Git Hooks 自动化审查
让审查真正“零摩擦”,需嵌入开发流程。最常用的是 pre-commit hook:
创建.git/hooks/pre-commit文件:
#!/bin/sh # 检查是否有 Java 文件变更 if git diff --cached --name-only | grep -q '\.java$'; then echo "Running open-code-review security check..." if ! git diff --cached | open-code-review --rule security --format plain --quiet; then echo "❌ open-code-review found security issues. Please fix before committing." exit 1 fi fi赋予执行权限:chmod +x .git/hooks/pre-commit。
效果:
当你执行git commit -m "add login endpoint"时,如果代码含硬编码密码,终端立即输出错误并中止提交:
Running open-code-review security check... ❌ open-code-review found security issues. Please fix before committing.实操心得:pre-commit hook 的
--quiet参数至关重要。它抑制正常输出,只在发现问题时返回非零退出码,从而触发 Git 中止。我们曾因忘记加--quiet,导致每次提交都刷屏一堆 JSON,开发者直接禁用了 hook——工具再好,体验烂了就没人用。
4.4 CI/CD 集成:在 GitLab CI 中卡住高危 PR
对于团队协作,需在合并前拦截问题。以下为 GitLab CI 示例(.gitlab-ci.yml):
stages: - review code-review-security: stage: review image: ubuntu:22.04 before_script: - apt-get update && apt-get install -y curl git - curl -fsSL https://raw.githubusercontent.com/open-code-review/install/main/install.sh | sh - open-code-review --download-model phi3-mini script: - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME - git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD | open-code-review --rule security --format json > review-report.json after_script: - cat review-report.json rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event"当 PR 提交时,流水线自动运行审查。若review-report.json中issues数组非空,Job 失败,PR 页面显示红色 ❌,并附带review-report.json内容供 Reviewer 查看。这比人工 Review 更客观——它不会因为今天心情好就放过一个 SQL 注入漏洞。
5. 常见问题与排查技巧实录:那些文档里不会写的坑
5.1 模型加载失败:Error: failed to load model: file not found
现象:
首次运行open-code-review --list-models正常,但执行审查时崩溃,报错failed to load model: file not found。
根因:
工具默认在~/.open-code-review/models/查找模型,但--download-model下载的路径是./models/(当前目录)。这是早期版本的设计缺陷——下载和加载路径不一致。
解决方案:
- 临时修复:运行
open-code-review --download-model phi3-mini --model-dir ~/.open-code-review/models/,强制指定下载目录; - 永久修复:创建符号链接
ln -s $(pwd)/models ~/.open-code-review/models(macOS/Linux)或mklink /D "%USERPROFILE%\.open-code-review\models" ".\models"(Windows); - 最佳实践:在
config.yaml中显式声明model_path: "/full/path/to/Phi-3-mini-4k-instruct.Q4_K_M.gguf",避免路径歧义。
踩坑记录:这个 bug 在 v1.1.0 版本修复,但很多用户升级后仍沿用旧配置。我们的经验是,在
--help输出里加一行醒目提示:“⚠️ 模型路径优先级:CLI 参数 > config.yaml > 默认路径”。
5.2 审查结果为空:No Issues Detected但明显有漏洞
现象:
对一段明显存在 XSS 漏洞的前端代码(如element.innerHTML = userInput)执行--rule security,却返回无问题。
根因:
工具默认只审查*.java,*.py,*.js等后缀,而你的前端代码是*.ts或*.tsx。--rule security的规则集未覆盖 TypeScript,且模型对 TSX 语法理解有限。
解决方案:
- 扩展文件类型:
open-code-review --rule security --include "*.ts,*.tsx"; - 启用 AST 模式:
open-code-review --rule security --ast-parser tree-sitter-typescript(需提前npm install -g tree-sitter-cli); - 自定义规则:在
rules/security.yaml中添加 TSX 专属检查项:- id: xss-tsx description: "Dangerous innerHTML assignment in TypeScript React component" pattern: "innerHTML\s*=\s*.*" severity: CRITICAL
5.3 内存溢出(OOM):进程被系统 kill
现象:
审查一个大型 PR(>50 个文件)时,终端突然中断,dmesg显示Out of memory: Kill process xxx (open-code-review) score xxx or sacrifice child。
根因:
LLM 加载时占用显存/CPU 内存,而工具未做资源限制。Phi-3-mini 在 16GB 内存机器上,峰值内存可达 14GB。
解决方案:
- 降低并发:
open-code-review --threads 2(默认 8); - 限制上下文:
open-code-review --max-files 10 --max-lines-per-file 200,主动裁剪; - 启用 swap:Linux 下
sudo swapon --size=4G --filename /swapfile(临时缓解); - 终极方案:改用 CPU-only 模式
--n-gpu-layers 0,牺牲速度保稳定(实测 CPU 推理慢 3 倍,但内存占用降至 3GB)。
实操心得:我们给客户部署时,总会问一句“你们的 CI 机器内存多大?”。如果 <16GB,直接推荐
--n-gpu-layers 0参数,并写入 CI 脚本模板。宁可慢一点,也不能让流水线因 OOM 频繁失败。
5.4 中文乱码:终端输出方块字符
现象:
在 Windows CMD 或某些 Linux 终端,Markdown 报告中的中文显示为□□□。
根因:
工具输出 UTF-8 编码,但终端未正确设置编码。Windows CMD 默认 GBK,Linux 某些发行版 locale 为C。
解决方案:
- Windows:启动 CMD 后执行
chcp 65001(切换 UTF-8),或改用 Windows Terminal; - Linux:
export LANG=en_US.UTF-8(写入~/.bashrc); - 通用方案:
open-code-review --format plain输出纯文本,规避 Unicode 渲染问题。
5.5 规则不生效:--rule perf未检测到 N+1 查询
现象:
对一段典型的 MyBatis N+1 查询代码(循环内调用userMapper.selectById()),--rule perf无反应。
根因:perf规则依赖 AST 解析识别for循环和Mapper调用,但工具默认未启用 Java AST 解析器(需额外安装tree-sitter-java)。
解决方案:
- 安装解析器:
npm install -g tree-sitter-cli && tree-sitter build-wasm https://github.com/tree-sitter/tree-sitter-java; - 指定解析器路径:
open-code-review --rule perf --ast-parser tree-sitter-java --ast-parser-path ~/.tree-sitter/parsers/java.so; - 验证安装:
tree-sitter parse src/main/java/UserService.java应输出语法树。
注意:AST 解析器是可选依赖,不强制安装。但
--rule perf的文档里会明确标注“需 tree-sitter-java 支持”,避免用户误以为规则失效。
6. 规则引擎深度解析:如何编写自己的审查规则
6.1 规则文件结构:YAML 定义一切
每个规则存为rules/<name>.yaml,例如rules/custom-business.yaml:
name: "Payment Transaction Check" description: "Ensure all payment methods have transaction rollback logic" version: "1.0" language: ["java"] files: ["src/main/java/**/Payment*.java", "src/main/java/**/*Service.java"] checks: - id: "payment-rollback" description: "Payment method must handle transaction rollback on exception" severity: "HIGH" pattern: "public.*void.*processPayment.*\\{[^}]*try\\{[^}]*} catch\\(.*Exception e\\)\\{[^}]*}" suggestion: "Add rollback logic in catch block or use @Transactional(propagation = Propagation.REQUIRED)" ast: true # 启用 AST 模式,需配合 tree-sitter 解析器关键字段说明:
language: 限定适用语言,避免 JS 规则误扫 Java 文件;files: glob 模式匹配文件路径,支持**递归;pattern: 正则表达式,用于快速筛选可疑代码片段;ast: 若为true,工具会调用 tree-sitter 解析 AST,pattern字段将被忽略,改用 AST 节点匹配(如node.type === 'method_definition' && node.childCount > 0);suggestion: 修复建议,支持模板变量{{ filename }},{{ line }}。
6.2 AST 模式实战:精准捕获“缺少事务注解”
正则无法可靠检测@Transactional是否缺失(因注解位置多变),AST 模式是唯一解。以下为rules/transaction.yaml片段:
checks: - id: "missing-transactional" description: "Service method lacks @Transactional annotation" severity: "MEDIUM" ast: true ast_query: | (method_declaration (modifiers (annotation (name) @annotation-name)) . (identifier) @method-name) ast_filter: | let hasTransactional = false; for (const node of captureNodes['annotation-name']) { if (node.text === '@Transactional') hasTransactional = true; } return !hasTransactional; suggestion: "Add @Transactional to {{ method-name }} method"AST Query 语法(tree-sitter):
(method_declaration ...)匹配方法声明节点;(modifiers (annotation (name) @annotation-name))捕获所有注解名称;@method-name为方法名节点打标签,供suggestion引用。
AST Filter(JavaScript):
在匹配到的节点上执行 JS 逻辑,返回true则触发告警。此处检查是否@Transactional存在,不存在则告警。
实操心得:AST 模式调试困难。我们提供
open-code-review --debug-ast src/main/java/PaymentService.java命令,输出完整语法树 JSON,方便你定位节点类型。别试图手写复杂 AST Query,先用tree-sitter parse看真实树结构,再逐步构建。