ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

开源代码审查工具链从零搭建:Gitea、Gerrit与Reviewdog实战

开源代码审查工具链从零搭建:Gitea、Gerrit与Reviewdog实战 代码审查这件事很多团队不是不想做是做着做着就变味了。有的团队把审查当形式PR 挂着三天没人理最后合并按钮是领导点的有的团队干脆跳过审查出了线上事故才想起来当初要是有人看一眼就好了。我自己在几个不同规模的项目里折腾过代码审查的落地从纯靠人肉盯 Git diff到搭起一套完整的开源审查工具链中间踩了不少坑也总结出一些能直接用的经验。这篇文章就把这套 open code review 的搭建过程、工具选型和落地心得完整梳理一遍希望能给正在这个方向上纠结的团队一个参考。1. 先搞清楚代码审查到底解决的是哪些问题很多团队引入代码审查工具以为装一个系统就能让代码质量自动变好这个预期从一开始就是错的。代码审查工具本身不会写高质量代码但它能解决几个非常实际的问题谁来审、审什么、改没改干净、记录在哪、卡没卡住合并。没有工具支撑时代码审查完全依赖人与人之间的口头沟通或者临时消息。开发把分支推到远程然后在群里喊一声“帮我看看”运气好有人回了运气不好消息被刷过去代码就一直躺在那里。等要上线时才突然发现分支还没合并赶紧点个通过审查名存实亡。整个过程没有留痕也没有强制规则质量完全靠自觉。有了一套数据库记录审查动作的开源工具之后审查动作就从“随缘”变成了“流程”。代码的每一次变更、每一条评论、每一个 approve 和 reject 都被记录在仓库里任何项目成员随时可以回溯“这段代码当时为什么那么写”。同时可以通过分支保护规则强制要求 PR 必须被指定数量的人批准才能合并这个能力在团队协作里非常关键——审批不再靠人情而是靠规则卡住入口。另一个经常被忽略的价值是知识传递。代码审查本质上是一种异步的、基于内容的知识分享。新入职的工程师通过审查老同事的代码能快速理解项目的分层习惯和边界约定反过来老同事在审查新人的代码时也能发现哪些约定文档里没写清楚从而迭代内部规范。这些隐性收益往往比“提前发现 bug”更值钱。所以我的建议是在搭建工具之前先跟团队把目标对齐。是希望提高代码正确性是希望统一代码风格是希望强制多人知情还是单纯为了留痕目标不同工具侧重点就不同。想清楚这一点再往下选型就不会跑偏。2. 开源审查工具的三种流派按团队规模和风格选择开源生态里代码审查工具并没有“唯一解”主流方案基本可以分成三个流派仓库平台内置的 Merge Request 工作流、独立审查服务器 Gerrit 的 push-review 模型、以及静态分析工具驱动的自动化审查。我分别说一下它们的特点和适用场景。2.1 仓库内置 MR/PR 工作流最稳妥的入门选择这一派的代表是 Gitea、GitLab Community Edition、以及早年的 Gogs。它们把代码仓库、Issue、MR/PR、分支保护、Webhook 全部集成在一起开箱即用。最适合大多数团队尤其是想把“管代码”和“审代码”放在同一个地方做的场景。从用户感知角度讲内置 MR/PR 的工作流对工程师最友好——平时怎么用 GitHub 就怎么用这套。本地开分支、写代码、提交、push 到远程然后在网页上发起合并请求指派给若干个 reviewer。Reviewer 收到通知打开在线 diff逐行评论或者整体评论最后给出 Approve 或 Request changes。整个流程和日常开发习惯是完全一致的不需要额外学习一套专门工具。这类工具的另一个优势是权限模型完整。可以精确控制谁能创建仓库、谁能推送分支、谁能合并 PR甚至可以按目录来约束哪些人有权修改特定路径下的文件。对中型团队来说这个自由度已经绰绰有余。2.2 Gerrit 的 push-review 模型适合对审查质量有执念的团队Gerrit 是一套历史悠久的开源代码审查系统它被很多大型项目采用核心思路和普通 MR 流程不太一样。在普通 MR 流程里开发先 push 到远端分支再发起合并申请Gerrit 则要求开发 push 到一个特殊引用refs/for/branch然后由 Gerrit 生成一个独立的 Change。这个 Change 不直接落到目标分支上而是在审查全部通过后由系统自动变基并入主干或由具备权限的人手动提交。这种设计的好处是审查粒度更细——它能基于每个 commit 单独审查审查完一个 commit 后再继续看下一个。对那种一个功能拆成多个带依赖关系的提交的团队来说体验比普通 MR 好很多。缺点同样明显开发流程与传统 git 工作流差异大新成员学习成本高而且要单独维护一套服务。我个人的判断是Gerrit 更适合对工程严谨度要求极高的底层基础设施团队、或者一个仓库有几十上百人协作的大型项目。如果你的团队只有三五个人、十个人以内用 Gerrit 会显得过重反而拖慢迭代速度。2.3 自动化审查机器先审一遍人再查漏补缺第三类工具不替代人工审查而是把机械性的检查全部交给机器。最典型的是 SonarQube、Reviewdog、以及各类 linter 工具在 CI 里的集成。它们的共同点是在 PR/MR 阶段自动跑静态扫描、代码风格检查、测试覆盖率统计把结果以评论形式直接贴到对应的代码行上。这套思路非常有用因为人工审查最大的成本不是“审”而是“待办项太多不知道该看什么”。如果每次 PR 里有一堆缩进、命名、明显的逻辑漏洞检查项混在一起评审者很快就会疲劳。把这些机械化问题交给机器让评审者只聚焦于架构、设计、业务正确性审查效率和体验都会显著提升。我在实际落地里是这么分工的机器负责 90% 的客观问题人负责 10% 需要大量上下文才能发现的主观问题。这样既控制了质量又不会让团队觉得“审查是个痛苦的事”。3. 从零落地一套开源审查环境以 Gitea 为例接下来说实操。我自己用下来最顺手的组合是 Gitea PostgreSQL Woodpecker CI再加一个 Reviewdog 做自动化审查。这套组合的优点是全部开源、资源占用低、一台低配服务器就能跑起来、搬迁也方便。下面按步骤讲完整个搭建过程。3.1 部署 Gitea别图省事用 SQLite直接上 PostgreSQLGitea 官方提供 Docker 镜像部署本身不复杂但有个细节我建议一开始就踩对数据库直接用 PostgreSQL不要因为图省事用 SQLite。SQLite 在单机小规模场景下确实能用但当仓库数量上来了、Webhook 频繁触发、多人同时打开页面时SQLite 的锁竞争会非常明显页面会出现卡顿。我用的 docker-compose 配置如下包含了 Gitea 和 PostgreSQL 两个服务version: 3 services: db: image: postgres:16-alpine restart: always environment: - POSTGRES_USERgitea - POSTGRES_PASSWORDgitea_db_password - POSTGRES_DBgitea volumes: - ./postgres:/var/lib/postgresql/data gitea: image: gitea/gitea:1.21 restart: always environment: - USER_UID1000 - USER_GID1000 - GITEA__database__DB_TYPEpostgres - GITEA__database__HOSTdb:5432 - GITEA__database__NAMEgitea - GITEA__database__USERgitea - GITEA__database__PASSWDgitea_db_password ports: - 3000:3000 - 2222:22 depends_on: - db volumes: - ./gitea:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro在服务器上执行docker compose up -d之后访问http://服务器IP:3000就能进入安装页面。这里要注意一点安装表单里的 SSH 端口和 HTTP 端口填的是容器映射后的宿主机端口也就是 SSH 填 2222、HTTP 填 3000否则之后 clone 仓库时生成的 SSH 地址会不对。这是我第一次部署时踩过的坑花了不少时间才排查出来。3.2 配置分支保护把审查做成硬性门槛服务启动后创建组织或仓库之前先在“管理后台”里把邮件通知、仓库默认分支这些基础项设置好。真正核心的一步是配置分支保护规则。位置在仓库的 “Settings - Branches - Branch Protection” 里针对默认分支一般叫 main 或 master添加一条规则。我个人习惯开启这几项禁止直接推送到该分支强制通过 PR 合并。需要审查且至少 1 个指定角色比如仓库管理员或项目维护者批准。新提交推送后旧的批准自动失效。禁止强制推送防止历史被改写。设置好以后开发就不能再像以前一样直接git push origin main把代码推上去了远程端会直接拒绝。所有变更都必须走 PR 流程审查、评论、修改、重新审批都在这条轨道里进行。这个规则比在团队群里强调一百遍“记得走审查”都管用。3.3 一次完整的 PR 审查流程演示以一次功能开发为例完整流程大概是这样的# 在本地创建功能分支 git checkout -b feature/user-login # 提交代码 git add . git commit -m feat: add user login api # 推送到远程 git push origin feature/user-login推送完成后打开 Gitea 仓库页面会看到一条醒目的提示条“Compare Pull Request”。点击进入填写标题和描述把审查者指派给相关同事然后提交 PR。审查这时候扮演的角色很关键他把 PR 页面当成一次“异步讨论场”在 diff 里逐点提意见。作者收到评论后继续在该分支上提交新 commit 并推送PR 会自动更新。等所有评论都被解决、审查者点了批准PR 才被允许合并。合并前如果 CI 集成已经接好还会等检查结果跑完才能点合并按钮。整个流程跑完注意观察一点全过程的讨论记录都留在 PR 时间线上谁批的、谁被 过、谁 diss 过哪行代码后面随时能翻出来。这就是“留痕”带来的长期价值。4. 机器先审人再进场接入静态检查和 Reviewdog人工审查最怕的不是“审得慢”而是把宝贵的精力花在本来机器就能搞定的事情上。这部分我讲一下怎么用开源工具把机械劳动剥掉让审查者一进来就直击要害。4.1 在 CI 里跑 Lint 和质量门禁先把最基础的一层搭起来代码推送到远端或 PR 创建时自动触发静态检查。我这里用的是 Woodpecker CI它本身也是开源项目资源占用极低天然适合挂在 Gitea 边上。配置一个流水线文件.woodpecker.yml示例pipeline: lint: image: golang:1.22 commands: - gofmt -l . - go vet ./... test: image: golang:1.22 commands: - go test ./... -race -coverprofilecoverage.outCI 一旦接上每个 PR 的页面上都会显示流水线状态。测试挂了、格式不对一眼就能看到审查者在看代码内容前就有了第一印象。这一步非常值得做它能拦住大量低级问题。4.2 Reviewdog让检查结果直接“长在”对应代码行上Lint 通过与否只是结果但要是能把具体问题以评论形式出现在 diff 上审查体验会提升一个档次。这个需求正好是 Reviewdog 解决的。这里给一个简化但能跑通的 Reviewdog 接入方式。在 CI 里安装 reviewdog然后让它解析 linter 的输出并请求 Gitea 的 API 将结果作为评论发布到对应文件和对应行pipeline: review: image: golang:1.22 commands: - go vet ./... 21 | reviewdog -namego vet -reportergitea -levelwarning environment: - REVIEWDOG_INSECURE_SKIP_VERIFYtrue - REVIEWDOG_GITEA_API_URLhttps://git.example.com/api/v1/实际用的时候要在 CI 的密钥管理里配好 GITEA_TOKEN。这个 token 的权限只需要write:repository就够了不建议给太大范围。Reviewdog 会智能判断如果问题位于 PR 变更的行上就当作普通评论发出来如果问题跟 PR 无关就不打扰人。这样审查者打开一个 PR 时机器已经把该说的都说完了剩下的讨论都聚焦在真正的逻辑问题上。4.3 一个必要提醒不要被自动化评论淹没这里必须泼一盆冷水。自动化工具不是接得越多越好。我见过一些团队在 PR 里同时挂了五六个机器人一会儿提示“这里复杂度太高”一会儿提示“此处缺少版权头”一条 PR 底下几十条机器评论真正有价值的代码讨论反而被淹没。我的做法是对机器评论做分级能阻止合并的比如单元测试失败、安全漏洞设为硬性门禁在 CI 里直接 fail。只是风格建议但项目规范有明确要求的以普通评论发出但限制数量。纯偏好类的建议比如“这里是不是可以用 switch 替代”不入门禁也不发评论留着负责人自己复盘。机器评论宁缺毋滥审查者才会有耐心读每一条。把质量门禁定在“只有真问题才阻断”的粒度上团队接受度会高很多。5. 工具搭好之后真正难的是让“人”愿意好好审工具链齐了分支保护开了PR 也能正确卡人了但很多团队到了这一步会发现另一个更棘手的问题——代码走完流程了可审查质量还是水得很。有人直接点 Approve 不看内容有人丢一句“LGTM”就算完成任务有人为了合代码而合代码。这一节聊聊我见过的团队都卡在哪以及怎么化解。5.1 为什么 PR 越小审查质量越高审查质量的第一杀手是 PR 太大。一个 PR 里面塞了十几个文件、两千多行改动任何人打开看都会头皮发麻。人的注意力是有限的看前两三百行可能还认真到后面就是划屏幕求眼熟评论区也只会集中在前面几个文件里。所以我在团队里一直强推“小 PR”原则一个 PR 只做一件事改动量控制在两百到四五百行以内。如果一个功能实在拆不开至少要求提交历史是分段清晰的每个 commit 是一个可独立审查的逻辑单元。审查者按照 commit 顺序逐个看理解成本会大大降低。这个原则在工具层面也能推一把。Gitea 的分支保护可以设置“PR 必须基于最新主分支”这能减少大量由过期代码引发的冲突。团队约定俗成每天开工前先 rebase 一次而不是攒到快合并时才处理。这个过程看起来费时间实际比最后解冲突省得多。5.2 作者也要学会“带着路标请人审”还有一个很少人提但影响巨大的点代码作者自己要先学会怎么发起审查。很多人 push 完分支后PR 描述就写一个“fix bug”审查者根本不知道改动背景、影响范围、重点看哪里只能从头到尾硬读。更好的做法是PR 描述里带上背景说明、改动清单、测试情况、风险点以及一句“我在 xxx 处的处理逻辑不太确定请重点看看”。这一句话能很有效降低审查者的认知负担。别小看这件事它把审查从“帮你检查作业”变成“咱俩一起没把握的地方商量着来”。我甚至见过一些团队在仓库里放了 PR 描述模板用 Gitea 的 issue/pull request 模板功能强制填充这些信息。这种做法对新人特别友好能让他们快速习得规范的提交习惯。5.3 审查文化客观标准、提前对齐、去掉权威包袱最后必须承认代码审查也是一种沟通而沟通里最容易出问题的是“面子”和“权威”。有的团队里组长审过的代码别人不敢说三道四审查流于形式有的团队则经常在 PR 评论区吵起来为了一个变量命名能争论半天。这两种极端都需要靠规则纠正。我倾向于在团队内部像这样对齐几条约定审查者对事不对人评论尽量给“建议 理由”少给命令式语气作者不要把评论当成批评当成一次共同排查有明显争议的问题先小范围私聊确认不要在评论区公开拉锯建立仓库级约定文档CODING STANDARD大部分风格问题答案都查得到不用每次新讨论。这些约定不花一分钱买工具但实际比任何审查系统都重要。过去的经验让我越来越确信工具只能保证“每一次都过了流程”而文化才能决定“过完流程之后代码是不是真的变好了”。6. 落地过程中最容易踩的坑完整排查链路再讲几个我在真实环境里遇到的坑。这些问题不大但每个都足以让团队在推审查流程的时候产生“这套东西是不是不行”的怀疑。我把排查链路写出来方便大家遇到了能快速定位。6.1 分支保护设了却推不上去先检查权限和路径匹配有次一个同事在 Gitea 里设置了分支保护但发现自己仍然可以直接 push 到 main。排查过程分了四步最后发现是规则路径写错了——保护规则里的分支名写的是master而默认分支是main规则根本没匹配上。Gitea 的分支保护规则支持通配符和路径匹配如果你有多个代码目录需要差异化保护一定要确认规则的正则写法。另外一个常被忽略的点是仓库管理员owner默认是可以绕过某些保护规则的这是设计如此不是 bug。如果希望任何人都不许绕行就不能给那个人分配 Maintenance 或更高角色。6.2 Reviewdog 评论不出现大概率是 Token 权限或 API 地址问题Reviewdog 接入时最容易出的问题是CI 跑完了状态也是绿的但 PR 里就是看不到任何评论。我排查这个问题时按这个顺序走第一在 CI 日志里确认 reviewdog 是否真的输出了内容和 reporter 类型如果日志里显示posting results说明程序本身是跑的第二检查 GITEA_API_URL 是否正确很多自建实例不是走 /api/v1/ 的默认路径第三检查 token 的有效范围reviewdog 需要write:repository或write:issue权限纯只读 token 不会报明显错误但发不出评论。这三个环节走完绝大多数“没反应”的问题都能定位。6.3 多人协作时 PR 频繁冲突救命的不是工具是节奏当团队开始严格执行 PR 合并后突发问题也会变多典型的就是 PR 开了一天main 分支被别的分支合入了几次导致这个 PR 里到处是冲突。开发者一边解冲突一边抱怨审查流程拖慢了进度。这个问题工具帮不上太多忙得靠流程节奏解决。我给团队定的节奏是功能分支存活时间不超过两个工作日到了时间哪怕没全部做完也要把已有代码拆成更小的 PR 先合一部分同时每天上午统一做一次 rebase保证所有人都是从最新 main 上拉分支开发的。运行了两周后冲突出现频率肉眼可见地下降。6.4 一个隐藏的坑把自动化检查当成“出口豁免权”接着第 4 节说一个最容易在团队里滋生的问题。自动化检查接入后一些开发会形成一种心态“CI 都过了、reviewdog 也没意见那就说明我代码没问题合吧。”这就是把自动化检查当成了免责声明。我见过最典型的一个事故是某仓库静态检查全绿但一个非常隐蔽的并发原子性问题没有被任何 linter 识别生产环境偶发抖动查了两周才发现。工具能拦截的是那些能被形式化描述的问题而业务逻辑对不对、边界条件全不全、并发模型符不符合场景这些永远都要靠人。所以我在多个场合反复强调自动化是帮你筛掉重复的、确定性的问题好让你把脑力留给那些 machine 不擅长的判断。如果想在团队里真正用好这套东西可以这样来设置角色边界机器人负责“客观违规”人负责“主观风险”。每次 PR 都明确留出至少一条“审查者思考型评论”的空间避免团队过渡依赖机器。7. 落到自己的项目上两种最小可用的起步方案如果你看完上面这些觉得直接上一整套太重也没有问题。我把这套体系拆成两个量级的起步方案按需选择即可。最小方案适用于个人项目或 2~3 人小团队只需要一个 Gitea 容器 一个 CI 容器连数据库都用 SQLite 先顶着。把分支保护打开要求自己或同伴至少批准一次再合然后在 CI 里塞几个 linter 脚本。这套跑起来的成本一个下午就够。它不完美但已经能让你看到“强制走流程”带来的变化。标准方案适用于 5~15 人的产品团队上面基础上把数据库换成 PostgreSQL接入 Reviewdog按目录维度配置分层保护和代码所有者把 PR 描述模板和仓库级编码约定文档一起推进。这个方案要求团队成员都认可审查的价值并且愿意花 1~2 周适应节奏。见效期大概在一个月左右这时候你会看到代码冲突减少、回溯历史更方便、线上低级 bug 明显变少。如果团队已经到几十人规模还要考虑代码所有权、跨仓依赖、权限矩阵是不是需要更复杂的方案屆时可以根据实际情况决定要不要引入 Gerrit。但无论规模大小有一点是完全一样的工具始终只是放大器它放大的是你对审查重不重视的态度。你有心把审查做好哪怕只有 Gitea 一个 CI 脚本也能做得像模像样你没有这个心接再多的工具也只是多了几个让流程变慢的理由。最后分享一个我自己的习惯转变以前我审查代码时总是盯着“哪里错了”现在我会分两步看——先问“这段代码的作者想解决什么问题”再去看他有没有解决到位。这个视角切换之后代码审查的阻力小了很多也更接近它本来的意义。希望这套 open code review 的完整落地思路也能让你和团队在这个过程中感受到同样的变化。
返回列表