从 0 学习 Alibaba Open Code Review(二):ocr review 命令入口分析

从 0 学习 Alibaba Open Code Review(二):ocr review 命令入口分析 前言上一篇文章中从使用者角度跑通了 Alibaba Open Code Review 的基础流程包括安装配置、ocr llm test、ocr review --preview、ocr review、JSON 输出、自定义规则配置以及工具调用日志分析。通过第一篇学习已经知道ocr review 可以识别 Git diff ocr review 可以对变更代码生成审查意见 ocr review --preview 可以预览本次会审查哪些文件 ocr review --format json 可以输出结构化结果 .opencodereview/rule.json 可以影响审查重点。不过到目前为止只是停留在“会使用”的阶段。从这一篇开始进入源码阅读阶段。本文先从最外层的 CLI 命令入口开始解决一个最基础的问题当执行 ocr review 时OpenCodeReview 的源码是从哪里开始执行的这篇文章的目标不是一次性读懂全部源码而是先找到ocr review的入口建立第一张简单的源码地图。一、本文基于的源码环境本文基于本地open-code-review-main源码阅读重点分析下面三个文件cmd/opencodereview/main.go cmd/opencodereview/review_cmd.go cmd/opencodereview/flags.go如果你的本地目录是 Git 仓库可以用下面的命令记录当前源码版本git rev-parse--short HEAD这样做的好处是后续如果 OpenCodeReview 升级导致源码结构变化可以知道本文对应的是哪个版本的代码。二、本篇学习目标本文主要解决下面几个问题1. OpenCodeReview 的项目目录结构大概是什么样的 2. ocr review 命令在哪里被分发 3. runReview 函数在哪里定义 4. ocr review 的参数是如何被解析的 5. reviewOptions 是什么 6. parseReviewFlags 除了解析参数还做了哪些校验 7. 如何画出 ocr review 的第一层调用链通过本文完成从会使用 ocr review到能找到 ocr review 的源码入口的过渡。三、为什么先从命令入口开始读源码对于 OpenCodeReview 这种 CLI 工具来说最自然的源码阅读方式就是从命令入口开始。因为用户真正执行的是ocr review所以源码阅读的第一步应该是找到ocr review 这个命令在哪里被识别然后再继续看它进入了哪个函数 这个函数第一步做了什么 命令参数是怎么解析的如果一开始就直接看 Agent、LLM、Git diff 或工具调用很容易迷路。所以这一篇只做一件事找到 ocr review 的启动入口。四、准备源码阅读环境首先进入 OpenCodeReview 源码目录cd D:\agent\open-code-review-main查看项目根目录dir可以看到项目中包含很多目录和文件例如cmd internal examples plugins skills pages npm README.md README.zh-CN.md go.mod go.sum Makefile这一篇不需要理解所有目录只需要先关注两个目录目录作用cmdCLI 命令入口本文重点关注internal项目内部核心逻辑后续文章再深入对于第二篇来说最重要的是cmd因为要先找到ocr review 命令是从哪里进入的五、查看cmd目录进入cmd目录查看dir.\cmd继续查看cmd/opencodereviewdir.\cmd\opencodereview这里可以看到一些和 CLI 命令相关的 Go 文件例如main.go flags.go review_cmd.go shared.go output.go这一篇先重点关注三个文件文件本文关注点main.goCLI 总入口负责分发命令review_cmd.goocr review对应的入口函数flags.goocr review参数解析逻辑暂时不深入shared.go output.go internal/agent internal/tool这些内容后续再拆。六、搜索review命令为了定位ocr review相关代码可以在项目根目录执行Select-String-Path.\cmd\**\*.go-Patternreview这个命令会在cmd目录下搜索所有 Go 文件中包含review的代码。如果本地安装了ripgrep也可以使用rgrunReview|parseReviewFlags|reviewOptions.\cmd从搜索结果中可以重点关注几个文件cmd\opencodereview\main.go cmd\opencodereview\flags.go cmd\opencodereview\review_cmd.go这三个文件基本可以帮助回答本文最核心的问题ocr review 是如何从命令行进入源码逻辑的七、分析main.goCLI 总入口先查看main.go的前面部分Get-Content.\cmd\opencodereview\main.go|Select-Object-First 80在main.go中可以看到main()会调用一个命令分发函数iferr:dispatch();err!nil{fmt.Fprintf(os.Stderr,Error: %v\n,err)os.Exit(1)}继续看dispatch()其中和review相关的逻辑是casereview,r:returnrunReview(args[1:])这行代码非常关键。它说明当用户执行ocr review或者使用简写ocr r程序都会进入runReview(args[1:])也就是说main.go 并不直接完成代码审查 它只是根据用户输入的子命令把流程分发给对应函数。所以目前可以得到第一段调用链用户执行 ocr review ↓ cmd/opencodereview/main.go ↓ main() ↓ dispatch() ↓ case review, r ↓ runReview(args[1:])这一步解决了第一个问题ocr review 命令是在哪里被分发的答案是cmd/opencodereview/main.go八、理解args[1:]是什么意思这里有一个容易困惑的地方runReview(args[1:])为什么不是直接传args而是传args[1:]可以简单理解为args[0] 是子命令 review args[1:] 是 review 后面的参数例如用户执行ocr review--preview那么命令参数大概可以理解为args[0] review args[1] --preview当程序已经通过args[0]判断出这是review命令后后面传给runReview的就只需要是--preview所以runReview(args[1:])就是把review后面的参数传进去。再举一个例子ocr review--formatjson可以理解为review 是子命令 --format json 是 review 命令自己的参数所以runReview只需要关心--format json而不需要再关心review这个单词本身。九、分析review_cmd.go找到runReview接下来查看review_cmd.goGet-Content.\cmd\opencodereview\review_cmd.go|Select-Object-First 80可以看到runReview函数是这样开始的funcrunReview(args[]string)error{opts,err:parseReviewFlags(args)iferr!nil{returnerr}ifopts.showHelp{printReviewUsage()returnnil}...}这一段说明runReview 是 ocr review 命令的核心入口函数。而它做的第一件事是parseReviewFlags(args)也就是解析用户传入的命令行参数。例如ocr review--preview里面的--preview就会在这里被解析。因此第二段调用链可以补充为用户执行 ocr review ↓ main.go ↓ runReview(args[1:]) ↓ parseReviewFlags(args)到这里已经知道ocr review 不是直接开始审查代码 而是先进入 runReview 然后先解析参数。十、先看一眼runReview后续流程这一篇不深入分析runReview后面的完整审查流程但可以先建立一个简化地图。在parseReviewFlags之后runReview后续大概会继续做这些事情parseReviewFlags ↓ loadCommonContext ↓ applyCLIExcludes ↓ validateReviewRefs ↓ runPreview 或 loadLLMRuntime ↓ buildToolRegistry ↓ agent.New ↓ ag.Run ↓ emitRunResult可以先简单理解为1. 解析参数 2. 加载仓库、规则、文件过滤器等公共上下文 3. 校验 Git ref 参数是否合法 4. 如果是 preview 模式只生成预览结果 5. 如果不是 preview 模式就加载 LLM Runtime 6. 构建 Agent 可用工具 7. 创建 Agent 8. 执行审查 9. 输出结果。这一篇只重点理解最前面的入口main.go ↓ runReview ↓ parseReviewFlags后面的Git diff、规则匹配、Agent 工具调用、JSON 输出会在后续文章中逐步拆开。十一、分析flags.go参数解析入口接下来查看flags.go中和review相关的部分Get-Content.\cmd\opencodereview\flags.go|Select-Object-Skip 90-First 90可以看到有一个结构体typereviewOptionsstruct{toolConfigPathstringrulePathstringrepoDirstringfromstringtostringcommitstringexcludesstringoutputFormatstringaudiencestringbackgroundstringmodelstringconcurrencyintperFileTimeoutintmaxToolsintmaxGitProcsintpreviewboolshowHelpbool}这个结构体可以简单理解为ocr review 命令的参数集合。也就是说用户在命令行输入的参数最后会被保存到这个结构体里。例如用户执行ocr review--preview程序内部就会得到preview true用户执行ocr review--formatjson程序内部就会得到outputFormat json用户执行ocr review--commitabc123程序内部就会得到commit abc123所以reviewOptions是连接命令行输入和内部审查流程的中间对象。十二、什么是parseReviewFlags在flags.go中还可以看到funcparseReviewFlags(args[]string)(reviewOptions,error){...}这个函数的核心作用是把用户输入的命令行参数解析成 reviewOptions。比如ocr review--preview经过parseReviewFlags之后大概可以理解为reviewOptions{ preview: true, }再比如ocr review--formatjson解析之后大概可以理解为reviewOptions{ outputFormat: json, }不过parseReviewFlags不只是解析参数它还会做一部分参数合法性校验。例如源码中可以看到类似逻辑--from 和 --to 必须成对出现 --commit 不能和 --from / --to 同时使用 --audience 只能是 human 或 agent --max-tools 不能是负数并且非 0 时会有最小值限制 --max-git-procs 不能是负数。因此parseReviewFlags做的是两件事1. 把字符串形式的命令行参数变成程序内部能使用的结构体字段 2. 在真正进入审查流程之前先拦截明显非法的参数组合。这一点很重要因为它说明 CLI 层不仅负责“接收输入”也承担了第一层输入校验职责。十三、先理解几个常用参数ocr review支持很多参数先重点理解几个已经用过和后面马上会用到的参数。1.--preview预览审查范围源码中可以看到类似代码a.BoolVarP(opts.preview,preview,p,false,preview which files will be reviewed without running the LLM)这说明--preview 是一个布尔参数 -p 是它的短参数 默认值是 false。所以ocr review--preview和ocr review-p是等价的。它的作用是只预览本次会审查哪些文件不真正调用 LLM。第一篇中执行过ocr review--preview输出Preview: 1 file(s) changed | 7 -1 Will review (1): [M] src/user.js 7 -1现在结合源码可以理解用户传入 --preview ↓ parseReviewFlags 解析 preview true ↓ runReview 后续会根据 preview 判断是否进入预览模式2.--format控制输出格式源码中可以看到类似代码a.StringVarP(opts.outputFormat,format,f,text,output format: text or json)这说明--format 用于控制输出格式 -f 是短参数 默认值是 text。默认执行ocr review输出的是普通文本结果。如果执行ocr review--formatjson或者ocr review-fjson就会输出 JSON 格式结果。第一篇中已经用过ocr review--format json ocr-review-result.json这个 JSON 输出后面非常重要因为可以基于它继续做Markdown 报告生成 审查历史保存 GitHub Actions 集成 Agent Workflow3.--commit审查指定提交源码中可以看到类似代码a.StringVarP(opts.commit,commit,c,,single commit hash or tag to review (vs its parent))这说明--commit 用于审查某一个指定 commit -c 是短参数。例如ocr review--commitabc123可以理解为审查 abc123 这个提交相对于它父提交带来的代码变更。这和默认的工作区审查不同。默认执行ocr review通常审查当前工作区中的变更。而ocr review--commitabc123审查的是某一次提交。4.--from和--to审查两个引用之间的差异源码中可以看到类似代码a.StringVar(opts.from,from,,source ref to start diff from (e.g., main))a.StringVar(opts.to,to,,target ref to end diff at (e.g., feature-branch))这说明--from 和 --to 用于指定 diff 的起点和终点。例如ocr review--frommain--tofeature-branch可以理解为审查 main 到 feature-branch 之间的代码差异。注意--from和--to必须成对出现。如果只写ocr review--frommain而不写--toparseReviewFlags会返回错误。十四、参数解析小结到这里对ocr review参数解析有了一个初步理解用户输入命令 ↓ main.go 分发到 runReview ↓ runReview 调用 parseReviewFlags ↓ parseReviewFlags 把参数保存到 reviewOptions ↓ parseReviewFlags 校验参数组合是否合法可以画成ocr review --preview ↓ args [--preview] ↓ parseReviewFlags(args) ↓ reviewOptions.preview true再比如ocr review --format json ↓ args [--format, json] ↓ parseReviewFlags(args) ↓ reviewOptions.outputFormat json所以reviewOptions可以理解为一次 ocr review 命令的配置对象。十五、查看ocr review -h除了看源码还可以通过帮助命令确认参数ocr review-h这个命令会显示ocr review支持的参数。源码中的参数定义最终会体现在帮助信息里。例如--preview --format --commit --from --to --rule --repo --concurrency --timeout --model对于新手来说看源码时可以配合帮助信息一起理解源码中定义了什么参数 ↓ ocr review -h 中显示什么参数 ↓ 实际命令怎么使用这个参数这样会比单独看 Go 代码更容易理解。十六、第二篇的完整简化流程图根据本文阅读到的内容现在可以整理出ocr review的第一层调用链用户执行 ocr review ↓ cmd/opencodereview/main.go ↓ main() ↓ dispatch() ↓ case review, r ↓ runReview(args[1:]) ↓ cmd/opencodereview/review_cmd.go ↓ parseReviewFlags(args) ↓ cmd/opencodereview/flags.go ↓ 生成 reviewOptions如果用户执行ocr review--preview可以理解为ocr review --preview ↓ main.go 分发 review 命令 ↓ runReview([--preview]) ↓ parseReviewFlags ↓ reviewOptions.preview true如果用户执行ocr review--formatjson可以理解为ocr review --format json ↓ main.go 分发 review 命令 ↓ runReview([--format, json]) ↓ parseReviewFlags ↓ reviewOptions.outputFormat json这就是本文最核心的收获。十七、目前暂时没有深入的内容这一篇只是源码阅读的第一步。目前还没有深入分析Git diff 是如何获取的 Preview 中的 [M] 是怎么来的 7 -1 是如何统计的 rule.json 是如何匹配文件的 Agent 工具是如何注册的 file_read / code_search 是如何调用的 LLM Provider 是如何加载的 JSON 输出是如何组装的。所以本文只建立一个最基础的入口认知ocr review 是从 main.go 分发到 runReview 再由 runReview 调用 parseReviewFlags 解析并校验参数。十八、本文使用的 PowerShell 命令记录为了方便后续复盘这里整理一下本文用到的主要命令。1. 进入源码目录cd D:\agent\open-code-review-main2. 查看项目根目录dir3. 查看cmd目录dir.\cmd4. 查看cmd/opencodereview目录dir.\cmd\opencodereview5. 搜索review相关代码Select-String-Path.\cmd\**\*.go-Patternreview或者rgrunReview|parseReviewFlags|reviewOptions.\cmd6. 查看main.goGet-Content.\cmd\opencodereview\main.go|Select-Object-First 807. 查看review_cmd.goGet-Content.\cmd\opencodereview\review_cmd.go|Select-Object-First 808. 查看flags.go中的 review 参数Get-Content.\cmd\opencodereview\flags.go|Select-Object-Skip 90-First 909. 查看帮助信息ocr review-h十九、总结1.ocr review从哪里进入答案是cmd/opencodereview/main.go在这里程序会根据用户输入的子命令进行分发。2.ocr review会进入哪个函数答案是runReview(args[1:])也就是说ocr review和ocr r最终都会进入runReview。3.runReview第一步做什么答案是parseReviewFlags(args)也就是先解析命令行参数。4. 参数解析结果保存到哪里答案是reviewOptionsreviewOptions可以理解为一次 ocr review 命令的参数集合。5.parseReviewFlags除了解析参数还做了什么它还会做基础参数校验例如--from / --to 必须成对出现 --commit 不能和 --from / --to 同时使用 --audience 只能是 human 或 agent 部分数值参数不能为非法值。6. 本文建立的源码地图是什么可以总结为main.go ↓ dispatch() ↓ runReview() ↓ parseReviewFlags() ↓ reviewOptions这就是对ocr review的第一层源码理解。二十、下一篇计划Git Diff 解析流程通过本文已经知道ocr review 是如何从 CLI 入口进入 runReview 的。下一篇继续分析从 0 学 OpenCodeReview 源码Git Diff 解析流程下一篇主要解决这些问题1. ocr review --preview 为什么能识别 src/user.js 2. Preview 中的 [M] 是怎么来的 3. 7 -1 是如何统计出来的 4. OpenCodeReview 如何判断哪些文件需要审查 5. rule.json 中的 exclude 如何影响审查范围第一篇解决的是ocr review 能做什么第二篇解决的是ocr review 是从哪里启动的第三篇继续解决ocr review 的输入 Git diff 是怎么生成的