资讯动态

基于GitLab与Gerrit的开源代码审查流程搭建指南

发布时间:2026/10/19 14:02:09 来源:尧图企业网站定制
1. 整体设计思路为什么我会选择自建一套代码审查流程代码审查这件事团队里永远有人觉得它是形式主义也有人觉得它是质量保障的最后一道防线。我个人的看法是代码审查本身绝对有用但很多团队之所以觉得它没用是因为审查流程设计得太粗糙要么是走过场式地点个赞要么是等代码合并之后才有人认真看发现问题已经晚了。open-code-review这个项目说白了就是一套基于开源组件自建的代码审查基础设施。它不是某个单一软件而是一整套流程和工具链的组合覆盖了从提交代码、触发审查、人工评审、自动检查到最终合并的完整链路。我在实际落地的时候参考过GitHub的Pull Request流程也研究过Gerrit的集中式审查模型最后结合团队规模和技术栈做了一套适合自己的方案。先说清楚这套东西解决了什么问题。在没有规范化审查流程的时候团队里最常见的场景是开发者在功能分支上写完代码直接推到主干然后测试环境开始报错或者Code Review变成了事后诸葛亮代码已经上线了Reviewer才在群里说这个写法有问题。而有了open-code-review这套流程之后每次合入必须经过两部分检查一是机器自动跑的静态检查、单元测试、构建验证二是人工Reviewer的明确批准。任何一步不通过代码就合不进去这就从机制上堵住了先上线再补票的漏洞。那这套东西适合谁来用呢我觉得主要适合三类人一是刚起步的中小型研发团队想在GitHub或GitLab上建立规范的代码合入流程但又不想花钱买企业版二是已经受够了开发流程混乱、想用开源方案快速搭建内部研发基础设施的技术负责人三是对Gerrit这类集中式审查工具有兴趣、想在自己的服务器上跑一套完整环境来学习的开发者。不管你是哪种情况下面这些内容都是我这几个月踩坑踩出来的经验可以直接照着用。可能有人会问为什么不直接用GitHub或GitLab官方提供的代码审查功能这个问题的答案其实也是我当初决定做这个项目的核心原因。先说GitHub免费版对私有仓库的审查支持其实够用但有些团队代码必须放在内网不允许推到公网服务这就不得不自建。再说GitLab社区版确实自带Merge Request和代码审查功能但它对强制审查的约束比较弱比如管理员可以绕过审批直接合并自动检查的集成配置也需要一定经验。Gerrit则正好相反它的审查模型非常严格每一行代码都可以被逐行评论没有Reviewer的打分合入就永远无法进行但它也有问题就是对Git工作流做了比较大的改动开发者需要额外学习。所以这套open-code-review的设计哲学是不要盲目追求工具的功能多强大而是根据团队的实际协作模式选择最合适的方案。在我这套流程里我同时兼容了两种主流的代码审查模型一种是基于Merge或Pull Request的轻量级审查适合快速迭代的互联网团队另一种是基于Gerrit的集中式审查适合对代码质量要求极高、甚至需要审计记录的团队。下面我会把这两种方案的搭建过程和踩坑经验都写出来。2. 核心细节解析与方案选型考量2.1 代码审查的两种核心模型到底怎么选代码审查工具虽然多但底层的协作模型基本只有两种。理解这两种模型的差异是在做选型之前最重要的事。第一种是基于分支合并的审查模型代表是GitHub Flow和GitLab Flow。它的工作方式是开发者从主干拉出一个功能分支在功能分支上提交代码然后把功能分支推送到远端发起一个Merge Request或者Pull Request。审查者在这个Request里查看代码差异留下评论开发者可以针对评论继续添加提交。所有人都同意之后把功能分支合并回主干。这种模型的优点是贴近大多数开发者的日常习惯分支即隔离冲突处理相对简单适合每天多次发布、追求迭代速度的产品团队。缺点也很明显因为合并之后通常不会对每个提交做严格的线性化处理历史记录里会有比较多的合并节点而且逐行审查的精度不如Gerrit那么细。第二种是集中式的审查模型代表是Gerrit。Gerrit的核心思想是没有审查就没有提交。开发者的提交不会直接进入仓库主干而是先进入Gerrit的一个暂存区Gerrit为每个提交生成一个Change审查者在网页界面上逐行评论打分分数达到要求后Gerrit才把这个提交真正合入目标分支。这种模型的好处是审查粒度极细、强制力极强每一行代码都有人看过而且任何绕过审查的动作几乎不可能发生。缺点是学习成本高开发者需要改变推送方式使用git push origin HEAD:refs/for/master这样的命令而不是普通的git push新手很容易在这里被卡住。我在实际项目中是这样取舍的如果团队人数在20人以下产品迭代速度很快那么用GitLab或Gitea的Merge Request就足够了不要引入Gerrit增加团队负担。如果团队做的是底层基础设施、支付系统、安全相关的高风险项目或者在合规审计上有硬性要求那么直接上Gerrit它能提供的审计记录和强制流程是GitLab很难替代的。2.2 开源方案横评GitLab、Gitea、Gerrit、Review Board不卖关子我直接把对比结果放在下面这张表格里这是我搭建open-code-review时做了大量调研之后总结出来的。工具部署难度审查模型学习成本适合场景我的评价GitLab Community Edition中等Merge Request低中小团队日常迭代功能最均衡推荐首选Gitea低Pull Request低极简场景、个人项目轻量但审查能力较弱Gerrit较高集中式Change高高合规要求团队审查最强对流程改造大Review Board中等Pre-commit Review中等老牌团队已有SVN现在用得少了不推荐新项目GitLab CE是我个人最推荐的首选。它的社区版虽然砍掉了一些企业功能但代码审查核心链路全部保留比如Merge Request、Inline Comment、Approval Rules部分版本通过配置文件支持、CI集成等。部署方式也友好有官方Omnibus安装包也有Docker镜像。我在生产环境用的就是Docker版的GitLab CE搭配Runner跑自动化检查。Gitea的优势是极轻量一台1核2G的服务器就能跑得很流畅界面响应速度比GitLab快很多。但它的问题在于代码审查功能比较弱虽然支持Pull Request和评论但在审查规则、权限约束、CI集成方面差得比较远适合个人项目或者极小的团队不适合作为企业级审查基础设施。Gerrit的部署和配置确实是四者里最复杂的它依赖Java环境底层用JGit实现数据库默认用H2实际生产建议换成MySQL或PostgreSQL。但它也是唯一一个能把强制审查做到极致的开源方案。如果你需要的是没有批准绝对无法合入这种硬约束Gerrit是唯一的选择。2.3 审查流程设计中的三个关键原则工具选好了流程设计更重要。我在open-code-review里遵循了三个核心原则这三个原则也是我踩过坑之后总结出来的。第一个原则是机器能干的活绝对不让人工干。代码风格检查、重复代码检测、单元测试、编译构建这些完全可以自动化的检查一定要在代码进入人工审查之前就全部跑完。人工Reviewer的时间和精力是最宝贵的资源应该只用来关注逻辑正确性、代码可读性、架构合理性这些机器难以判断的问题。我在CI流水线里配置了静态检查、单元测试和代码覆盖率采集任何一个环节失败Merge Request的合入按钮就会自动置灰。第二个原则是审查结果必须有明确的状态和记录。审查不能只是口头说说所有的批准、驳回、评论意见都必须有系统记录。这不仅是审计的需要也是防止我明明说过了但他没改这种扯皮事件的关键。Gerrit天然具备这个能力GitLab则需要通过配置Approval Rules来实现比如要求至少两名指定Reviewer批准、代码所有者必须参与审查等。第三个原则是流程要有弹性不能一刀切。我见过一些团队把审查流程搞得极其严格改一行注释也需要两位高级工程师批准结果就是大家为了绕过流程开始投机取巧。合理的做法是区分变更等级比如普通Bug修复只需要1个Reviewer批准涉及公共接口变更、数据库结构变更的高风险MR则必须2个Reviewer批准并且要求架构组相关成员参与。这种弹性设计能让团队的审查效率和质量达到一个最佳的平衡点。3. 实操过程从零搭建基于GitLab的开源代码审查环境3.1 快速部署GitLab我的Docker Compose配置这一节我把最常用的方案写出来就是基于GitLab CE自建代码仓库和审查环境。如果你已经有现成的GitLab可以直接跳到3.2节看审查规则配置。我这边生产环境用的是Docker Compose部署配置文件可以直接抄作业。先建立一个docker-compose.yml内容如下version: 3.8 services: gitlab: image: gitlab/gitlab-ce:16.10.2-ce.0 container_name: gitlab restart: always hostname: gitlab.example.com environment: GITLAB_OMNIBUS_CONFIG: | external_url https://gitlab.example.com gitlab_rails[gitlab_shell_ssh_port] 2222 gitlab_rails[initial_root_password] YourStrongRootPassword gitlab_rails[smtp_enable] true gitlab_rails[smtp_address] smtp.example.com gitlab_rails[smtp_port] 465 gitlab_rails[smtp_user_name] gitlabexample.com gitlab_rails[smtp_password] smtp_password gitlab_rails[smtp_domain] example.com gitlab_rails[smtp_authentication] login gitlab_rails[smtp_tls] true ports: - 80:80 - 443:443 - 2222:22 volumes: - /srv/gitlab/config:/etc/gitlab - /srv/gitlab/logs:/var/log/gitlab - /srv/gitlab/data:/var/opt/gitlab shm_size: 256m几个关键点说一下。initial_root_password只在首次初始化时生效如果容器已经运行过再修改这个环境变量是没用的必须在启动后登录后台改或者直接在/etc/gitlab/gitlab.rb里改。GITLAB_OMNIBUS_CONFIG这个环境变量里的配置会被Omnibus在容器启动时自动读取和应用比进容器敲命令方便得多。shm_size一定要显式配置GitLab的PostgreSQL和Redis对共享内存有一定需求默认值太小会导致启动时随机出现异常。启动命令很简单docker compose up -d docker compose logs -f gitlab等待日志中出现类似gitlab Reconfigured!的提示就可以通过浏览器访问了。首次启动可能需要三到五分钟不要急着刷新页面GitLab在后台做初始化期间页面返回502是正常现象。3.2 配置Merge Request审批规则让强制审查真正生效GitLab CE版本的Approval Rules在Web界面里默认不可见因为GitLab把可视化的审批规则配置做进了付费版。但有个隐藏的能力CE版的审批规则可以通过项目配置文件.gitlab/merge_request_approvals.yml来管理或者通过API调用开启。我在部署时就通过API把审批规则配置好了。先拿API开启一个项目的审批功能这里的PROJECT_ID是项目主页URL里的数字IDPRIVATE_TOKEN需要在用户设置里生成一个有api权限的Personal Access Tokencurl --request POST \ --header PRIVATE-TOKEN: your_private_token \ --header Content-Type: application/json \ --data {approvals_required: 1, reset_approvals_on_push: true} \ https://gitlab.example.com/api/v4/projects/PROJECT_ID/approvals这个接口的作用是设置该项目合并请求需要的最少审批人数。approvals_required设成1意味着至少有一名有权限的成员点击Approve之后才能合并。reset_approvals_on_push设为true意思是如果开发者新推送了提交之前的审批自动失效必须重新审批。这个配置非常关键能防止我先批准了开发者又偷偷改代码比如加了一行删库逻辑然后直接合并这种安全漏洞。如果你不想用API也可以用Web界面操作。进入项目后点击Settings Merge requests在Approval settings里勾选Enable approval rules然后添加规则。比如我常用的配置是规则设置值普通MR最少审批人数1修改db/schema.rb的MR最少审批人数2修改.gitlab-ci.yml的MR最少审批人数2审批重置条件代码推送后自动重置这个配置的原理是大多数业务代码的MR有一个人认真看就够了但数据库结构变更和CI配置变更影响面很大必须至少两个人同时确认。GitLab的审批规则支持按文件路径匹配所以我能做到这种精细化控制。3.3 接入GitLab CI让自动化检查成为合入的前置条件审批规则只解决了人的约束还差机器这道关卡。GitLab CI可以让你在MR创建和代码更新时自动执行检查任务检查结果会直接显示在MR页面上如果检查失败Merge按钮同样是灰的。我把CI配置简化成下面这个例子stages: - lint - test - coverage variables: IMAGE_BASE: registry.example.com/ci-images lint: stage: lint image: ${IMAGE_BASE}/node:20-alpine script: - npm ci - npm run lint only: - merge_requests test: stage: test image: ${IMAGE_BASE}/node:20-alpine services: - postgres:15 variables: POSTGRES_DB: app_test POSTGRES_USER: tester POSTGRES_PASSWORD: testpass DATABASE_URL: postgresql://tester:testpasspostgres/app_test script: - npm ci - npm run test:ci artifacts: when: always reports: junit: junit.xml only: - merge_requests coverage: stage: coverage image: ${IMAGE_BASE}/node:20-alpine script: - npm ci - npx jest --coverage --coverageReporterscobertura after_script: - curl -X POST -F token$COVERAGE_TOKEN -F commit$CI_COMMIT_SHA https://coverage.example.com/api/upload only: - merge_requests这里有个特别重要的细节only: [merge_requests]这个字段限定了这些任务只在MR存在时才执行。如果你不写这个限制每次push代码到分支都会跑一遍完整流水线对服务器资源是个不小的浪费。另外only和rules建议用rules代替因为only/except在新版GitLab中已被标记为弱化语法不过它是最好理解的这里就不展开绕了。对于没有CI/CD系统的团队你完全可以把这一整套open-code-review简化成GitLab Shell脚本的组合。在GitLab服务器上挂一个crontab脚本每五分钟轮询一次处于open状态的MR把每个MR对应的分支拉到临时目录执行语法检查和单元测试然后把结果通过GitLab API写到MR的讨论区。这种方案虽然简陋但能解决很多小团队没有CI基础设施的尴尬。4. 常见问题与排查技巧实录4.1 GitLab部署和权限相关的问题问题1GitLab启动后一直502或者过几分钟就挂一次。这个最常见的原因有两个。第一服务器内存不足GitLab全家桶加上PostgreSQL、Redis、Prometheus4G内存是最低配置8G才比较舒适。如果你只有2G内存建议把Prometheus和Grafana关掉在gitlab.rb里设置prometheus_monitoring[enable] false。第二shm_size太小导致PostgreSQL崩溃Docker Compose里显式设置shm_size: 256m就能解决。问题2用户无法通过SSH协议推拉代码。GitLab的SSH默认监听22端口但主机的SSH服务通常也占用了22端口。看我的Compose配置把容器内的22端口映射到了宿主机的2222端口这时候需要在GitLab的gitlab.rb里修改gitlab_shell_ssh_port为2222否则用户克隆代码时生成的SSH URL端口是错的。还要注意所有用户的克隆地址需要手动带上端口号比如ssh://gitgitlab.example.com:2222/group/project.git。问题3MR提交后没有触发CI任务。先检查.gitlab-ci.yml是不是放在项目根目录文件名是否正确再检查Runner是否在线在Settings CI/CD Runners里查看有没有可用的Runner最后检查你的only或rules条件是不是漏掉了merge_requests。4.2 审查流程落地中的协调问题工具层面的问题好解决人在流程中的问题更难搞。我在open-code-review落地的过程中遇到最大的挑战不是技术而是组织行为。一开始我们把审批人数设为2人结果发现很多MR因为等第二个Reviewer而阻塞一阻塞就是一整天。原因很简单团队里不同成员负责的模块不一样别人不敢随便批准自己不了解的代码。后来我把审批规则改成了按风险分级普通代码1人批准即可高风险变更才需要2人整个流程的效率提升非常明显。还有一个细节值得分享给Reviewer配置邮件通知和待办提醒。GitLab支持在MR被分配给某个用户时给该用户发送邮件同时该MR会自动出现在他个人的To Do列表中。我要求团队里每个人都养成看到待办就处理的习惯不管是通过还是驳回至少要在当天给出反馈。代码审查最大的敌人是拖延一旦拖上两天开发者可能已经切到别的任务再回来改就是额外的上下文切换成本。5. 高阶玩法基于Gerrit搭建更严格的集中式审查流程如果你觉得GitLab的MR审查还不够硬核那可以试试Gerrit。我在open-code-review项目里同时也部署了一套Gerrit环境专门用于那些对代码质量要求极高的核心仓库。下面我把部署和基本使用流程写出来方便有兴趣的读者直接照着操作。5.1 用Docker快速启动GerritGerrit的官方Docker镜像在gerritcodereview/gerrit下但直接跑一个单独的Gerrit容器会遇到一个问题它默认使用H2数据库性能一般而且数据备份麻烦。我建议用Docker Compose把Gerrit和PostgreSQL放在一起version: 3.8 services: postgres: image: postgres:15 container_name: gerrit-postgres environment: POSTGRES_DB: reviewdb POSTGRES_USER: gerrit POSTGRES_PASSWORD: gerritpass volumes: - pg_data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U gerrit] interval: 10s timeout: 5s retries: 5 gerrit: image: gerritcodereview/gerrit:3.9 container_name: gerrit ports: - 8080:8080 - 29418:29418 depends_on: postgres: condition: service_healthy environment: CANONICAL_WEB_URL: http://gerrit.example.com:8080 GERRIT_INIT_ARGS: --install-pluginreviewnotes --install-plugindelete-project volumes: - git_data:/var/gerrit/git - etc_data:/var/gerrit/etc - db_data:/var/gerrit/db command: init volumes: pg_data: git_data: etc_data: db_data:这里有个坑需要提醒Gerrit容器首次启动时用init命令初始化它会自动生成/var/gerrit/etc/gerrit.config文件然后退出。你需要在宿主机上修改生成的配置文件把数据库相关配置指向PostgreSQL然后再重启容器。具体来说编辑etc_data卷里etc/gerrit.config把[database]部分改成[database] type POSTGRESQL hostname postgres database reviewdb username gerrit改完配置文件后把容器命令从init改成空或者daemon再docker compose up -dGerrit就会以正常模式启动。5.2 完成一次Gerrit审查从创建项目到Change合并Gerrit中首次使用需要完成几个步骤。登录后点击New Project填一个仓库名设计权限组时要注意项目分为两个层级refs/heads/*是目标分支的引用refs/for/*是开发者提交代码时推送的特殊引用路径。在权限配置里把refs/for/*的Push权限授予普通开发者把refs/heads/*的Push权限只授予管理员或集成负责人。这样开发者的代码永远不能直接推入主干只能推送到审查暂存区。完成项目配置后克隆代码的命令与GitLab有所不同git clone http://gerrit.example.com:8080/YourProject cd YourProject git config remote.origin.push refs/heads/*:refs/for/* git config remote.origin.receivepack git receive-pack这里把push refspec配置成refs/heads/*:refs/for/*以后执行git push时代码会自动进Gerrit的Change队列。如果是第一次推送某个新的changeGerrit会返回一个类似http://gerrit.example.com:8080/c/YourProject//123的URL这就是需要分享给Reviewer的Change页面地址。在Reviewer端打开Change页面后会给每个Commit生成一个Patch Set。Reviewer点击Reply可以对Response进行打分分数规则是2表示可以合入1表示我没意见但不算批准-1表示有意见但不反对-2表示强烈反对。只有得到某个具备Code Review 2权限的用户打出2Change才满足合入条件。任何一个在Gerrit上审查过的开发者都会对它的审查精度印象深刻。它对每一行代码都能单独评论支持在提交的间隙继续追加修正change的每一次更新都会产生新的Patch Set同时保留之前的版本方便对比。这种体验是GitLab MR难以做到的也因此我建议所有追求代码质量的团队都可以在核心仓库上尝试引入Gerrit哪怕只做试点也行。6. 常见问题与排查技巧实录Gerrit篇与通用篇6.1 我在Gerrit上遇到的三个典型问题问题1普通开发者直接把代码push到了refs/heads/master绕过了审查。排查后发现是权限没配好。我在Gerrit的refs/heads/*权限里只允许Administrators组Push但开发者使用的是同一个账号所以问题不在账号而在于权限继承。解决方法是在项目配置里取消继承自父项目的权限选项针对refs/heads/*明确拒绝非管理员组的Push权限。配置完成后普通开发者push到主干时会被拒收并收到一条Push to refs/heads/master denied的提示。问题2开发者提交了新的Patch Set后原来的2审批变成了1或消失。这是Gerrit的默认安全机制新的Patch Set意味着代码内容发生了变化旧的审批意见不再适用。Gerrit会根据新Patch Set与上一版的差异逐行重算涉及改动的区域会重新进入未审批状态。这个特性安全设计是对的但容易让开发者困惑尤其是当一位Reviewer已经打完2、开发者又修了一个小小的拼写错误时。我的建议是在团队规范里约定有新Patch Set推送后原Reviewer应重新审查如果只是无关紧要的改动应该顺手重新打分不要因此卡住流程。问题3Gerrit服务器重启后某些Change消失了或者项目列表为空。这是H2数据库的常见问题容器异常退出时H2可能损坏。解决办法就是从一开始就不用H2而是配置外部PostgreSQL。我已经在5.1节里给出了完整的Compose配置照抄即可。6.2 审查流程的ROI我算过一笔账做代码审查基础设施最大的顾虑往往是投入产出比。我也被问过很多次搞这么复杂到底值不值这里我分享一组自己在团队里记录的数据。在open-code-review落地之前我们线上故障平均每月3次其中约40%的故障根因是低质量代码合入比如空指针、错误的分支合并、缺少异常处理等。落地审查流程之后的六个月线上故障平均每月降到1次左右而且再也没有出过因为代码合并顺序问题导致的事故。当然任何数据都有幸存者偏差代码审查的核心价值在于预防而预防的效果很难量化。但有一点我很确定当团队中每个人都知道自己写的代码一定会被另一个人认真看一遍的时候提交之前的自测和自审程度会有明显提升这才是审查流程带来的最大隐性收益。6.3 把open-code-review继续扩展下去如果你的团队已经跑通了基础的审查流程下面这几个方向是下一步可以考虑的第一个方向是接入更多自动化能力。比如增加一个机器人在MR创建时自动分配Reviewer根据文件变更路径匹配模块负责人比如在CI里增加SonarQube扫描自动把重复代码、坏味道和严重漏洞报告贴到MR评论中。我和团队目前在做的是给CI增加一个容器镜像安全扫描步骤镜像构建完成后自动执行漏洞扫描有问题就不允许合入这能在代码进入生产环境前更早发现供应链风险。第二个方向是做好审查数据度量。GitLab和Gerrit都提供了API可以定期拉取审查相关的数据比如MR的平均审查时长、每个开发者的MR被驳回率、单笔MR的平均评论数、每个Reviewer的响应时间。通过这些指标你可以发现流程中的瓶颈。比如某个模块的MR平均审查时长特别长大概率是该模块只有一个人能审这时就该考虑备份Reviewer了。第三个方向是沉淀审查清单。我见过最好的做法是把团队在历史事故中总结出的常见问题做成一份检查清单挂在项目Wiki里同时让机器人把这份清单直接放到每个MR的描述模板里。审查者照着清单一项项对既不容易漏项也能让新加入的开发者尽快掌握团队关注的重点。就我的实际感受来说无论用GitLab还是Gerrit审查工具都只是辅助真正的核心是人的习惯和团队的文化。如果工具能创造一个环境让每次代码提交都被认真对待让每个问题都能在合入前被发现和解决那这套基础设施就值回了全部搭建成本。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑