1. 从一次线上事故说起为什么Gerrit权限配置不是小事那天下午团队正准备发布一个重要的版本突然发现一个本应只有核心开发人员才能访问的私有分支被一个刚入职两周的实习生推送了代码。更糟糕的是推送的代码包含了一个未经充分测试的、可能导致服务崩溃的改动。虽然最终在合并前被拦截但整个发布流程被打断团队花了数小时进行代码审查和回滚操作项目进度严重受阻。事后复盘根源直指Gerrit上一个看似不起眼的权限配置错误一个用于简化新成员初始权限的“通配符”引用refs/for/*被错误地赋予了Push权限而非安全的Push Merge权限。这个案例绝非孤例。在我十多年的代码托管和协作平台使用经验里Gerrit以其强大的代码评审流程和精细的权限控制模型著称但它的权限系统Access Control也因其复杂性和灵活性成为了许多团队“踩坑”的重灾区。很多人把Gerrit权限配置看作是一次性的“安装后步骤”草草了事却不知它实际上是保障代码库安全、维护开发流程秩序的“基石工程”。一个配置不当的Gerrit轻则导致代码混乱、评审流程形同虚设重则可能引发安全漏洞或数据泄露。因此今天我们不谈高深的架构就聚焦于Gerrit权限配置这个“脏活累活”把它掰开了、揉碎了讲清楚。我会基于一个典型的中大型研发团队场景带你从零开始构建一套既安全又高效、既能严格管控又能灵活适配的Gerrit权限体系。无论你是刚开始接触Gerrit的运维还是负责团队流程规范的Tech Lead这篇文章都能为你提供一份可直接落地的“配置地图”和避坑指南。2. 理解Gerrit权限模型的核心项目、组、规则与继承在动手配置之前必须彻底理解Gerrit权限系统的几个核心概念。它不像GitLab或GitHub那样主要依赖仓库的成员角色Maintainer, Developer而是构建了一个更为抽象和强大的三层模型项目Project、组Group、访问规则Access Section并通过继承Inheritance机制实现复用。2.1 项目Project权限的承载主体在Gerrit中一切权限都依附于项目。一个项目通常对应一个Git仓库。权限配置的核心文件是project.config它存储在项目的refs/meta/config分支中。这意味着对权限的修改本身也是一次需要提交和评审的代码变更完美契合了“Infrastructure as Code”的理念。注意直接通过Gerrit Web界面或SSH命令进行权限修改本质上也是在操作这个分支。我强烈建议通过克隆refs/meta/config分支到本地修改project.config文件后再推送到Gerrit进行评审的方式来进行权限变更。这留下了可追溯的变更历史便于审计和回滚。2.2 组Group权限的授予对象组是用户的集合。Gerrit权限从不直接授予单个用户而是先授予组再将用户加入对应的组。这是实现权限规模化管理的基石。系统内置组如Anonymous Users所有用户包括未登录、Registered Users所有登录用户、Project Owners项目所有者。通常不建议直接给Anonymous Users任何写权限。自定义组这是你发挥的主要舞台。例如team-frontend-core前端核心组、team-backend后端全体、role-release-manager发布经理角色组。组的规划至关重要。我的经验是按“角色”和“团队”两个维度交叉创建组。角色组如code-reviewer,integrator定义能力团队组如team-a,team-b定义归属。一个用户可能同时属于team-a和code-reviewer两个组。2.3 访问规则Access Section权限的具体内容这是最复杂的部分。访问规则定义了“谁”Group在“哪里”Ref能“做什么”Permission。它由三要素构成引用Ref/Ref Pattern权限生效的Git引用范围。可以是具体分支refs/heads/master标签refs/tags/v*或是通配符refs/heads/*。特殊引用refs/for/*代表推送到评审的引用refs/meta/config代表项目的配置分支。权限Permission具体的操作权利。Gerrit权限非常细致主要分几类读权限Read可克隆、拉取。写权限Push直接推送、Push Merge只能推送合并提交即通过评审的提交、Push Signed-Off只能推送带Signed-off-by的提交、Forge Author允许推送作者信息与提交者不同的提交。评审流程权限Label Code-Review打Code-Review分数如2, 1, -1、Label Verified打Verified分数通常用于CI集成、Submit将评审通过的变更合入分支。管理权限Create Reference创建分支/标签、Delete Reference删除分支/标签、Edit Topic Name编辑评审主题等。组或用户被授予权限的对象必须是组。一条完整的规则在project.config中看起来是这样的[access refs/heads/*] push group team-backend pushMerge group Registered Users label-Code-Review -2..2 group code-reviewers submit group integrators2.4 继承Inheritance实现权限的层次化管理Gerrit项目可以有一个父项目。子项目会继承父项目的所有权限配置。这是实现权限模板化的关键。通常我们会创建一个名为All-Projects或Base-Permission-Template的顶级父项目在其中定义全公司或全部门通用的基础权限如所有登录用户可克隆所有代码。然后每个具体的业务项目如project-frontend,project-backend都继承自这个父项目并只配置自己特有的、差异化的权限。这种结构极大地减少了配置冗余和出错概率。修改父项目的权限所有子项目会自动生效除非子项目显式覆盖。3. 实战为中型研发团队设计一套权限方案假设我们有一个团队分为前端组、后端组、测试组并设有架构师和发布经理角色。我们将一步步构建权限体系。3.1 第一步规划与创建组首先在Gerrit Web界面的“People” - “List Groups”中创建以下组团队组team-frontendteam-backendteam-qa角色组role-core-reviewer核心评审员可给2/-2role-integrator集成员可执行Submitrole-release-manager发布经理可操作发布分支和标签特殊组project-owners每个项目的管理员可从Project Owners继承并添加特定成员将相应成员加入这些组。例如资深开发人员同时加入team-backend和role-core-reviewer。3.2 第二步配置基础父项目 (All-Projects)克隆All-Projects的配置分支git clone ssh://gerrit-host:29418/All-Projects cd All-Projects git fetch origin refs/meta/config:config git checkout config编辑project.config文件# 基础读权限所有登录用户可读所有分支 [access refs/heads/*] read group Registered Users [access refs/tags/*] read group Registered Users # 基础推送权限任何人可向 refs/for/* 推送进行评审这是Gerrit工作流的基础 [access refs/for/*] push group Registered Users # 配置分支保护只有Project Owners能修改权限 [access refs/meta/config] read group Registered Users push group Project Owners # 定义Code-Review标签的含义-2 阻止 -1 不喜欢 0 无意见 1 看起来不错 2 批准 [label Code-Review] function MaxWithBlock value -2 Block value -1 Do not submit value 0 No score value 1 Looks good to me, but someone else must approve value 2 Looks good to me, approved copyAllScoresIfNoChange true # 如果新补丁集无实质更改则复制所有分数 # 定义Verified标签通常由CI系统自动打标 [label Verified] function MaxWithBlock value -1 Failed value 0 No score value 1 Verified defaultValue 0提交并推送这个变更。这为所有项目建立了安全基线人人可读、人人可发起评审、配置受保护。3.3 第三步为具体项目例如project-service-api配置权限现在为后端API服务项目配置具体权限。假设该项目使用master为主干分支release/*为发布分支feature/*为功能分支。创建项目并继承在Gerrit上创建project-service-api设置其父项目为All-Projects。克隆配置分支同上克隆该项目的refs/meta/config分支。编辑project.config添加以下段落# --- 主干分支 (master) 保护策略 --- [access refs/heads/master] # 读所有人可读 read group Registered Users # 推送禁止直接Push必须通过评审 push block group Registered Users # 推送至评审后端团队成员可以推送变更进行评审 push group team-backend # 评审权限核心评审员可打Code-Review分 label-Code-Review -2..2 group role-core-reviewer # 验证权限测试团队和CI系统可打Verified分 label-Verified -1..1 group team-qa label-Verified -1..1 group Continuous-Integration-Group # 假设CI系统属于此组 # 合入权限只有集成员可以Submit submit rule label:Code-Review2,label:Verified1 submit group role-integrator # --- 发布分支 (release/*) 更严格的保护 --- [access refs/heads/release/*] read group Registered Users push block group Registered Users # 只有发布经理和特定集成员可以创建发布分支并推送至评审 push group role-release-manager push group role-integrator label-Code-Review -2..2 group role-core-reviewer label-Verified -1..1 group team-qa label-Verified -1..1 group Continuous-Integration-Group # 合入要求可能更高例如需要额外的QA签名 submit rule label:Code-Review2,label:Verified1 submit group role-release-manager # 发布分支仅限发布经理合入 # --- 功能分支 (feature/*) 相对宽松 --- [access refs/heads/feature/*] # 允许后端团队成员创建和直接推送强制推送除外到自己的功能分支便于早期快速迭代 create group team-backend push force group team-backend # 允许强制推送用于rebase # 但合并到主干仍需走评审流程这通常通过分支合并时的Pull Request在Gerrit中仍是提交来保证 # --- 标签保护 --- [access refs/tags/*] read group Registered Users create group role-release-manager # 只有发布经理可以打标签 pushTag group role-release-manager pushSignedTag group role-release-manager解读关键配置点push block group Registered Users这是一条拒绝规则。在Gerrit中权限是顺序匹配的后面的规则可以覆盖前面的。这里先拒绝所有注册用户的直接Push再在后面开放特定组的Push到refs/for/*从而实现了“禁止直接Push必须走评审流程”的效果。submit rule label:Code-Review2,label:Verified1这是一条提交规则。它定义了一个合入条件只有当该变更的Code-Review标签最高分达到2并且Verified标签最高分达到1时submit权限才会生效。这强制要求了代码必须经过核心评审员批准且通过CI验证。create group team-backendCreate Reference权限控制创建新引用的能力。对于功能分支我们开放给团队方便他们自主创建。3.4 第四步处理权限冲突与规则匹配顺序Gerrit按照project.config文件中[access]部分的顺序从上到下进行匹配。第一条匹配的规则生效。这要求我们必须把最特殊、最具体的引用路径放在前面把最通用的放在后面。例如如果你把[access “refs/heads/*”]放在[access “refs/heads/master”]前面那么针对master分支的特殊配置就可能被通用的heads/*规则覆盖而失效。正确的顺序是[access refs/heads/master] # 具体分支 [access refs/heads/release/*] # 发布分支模式 [access refs/heads/feature/*] # 功能分支模式 [access refs/heads/*] # 通用分支如有 [access refs/for/*] # 评审引用 [access refs/tags/*] # 标签 [access refs/meta/config] # 配置分支4. 高级场景与避坑指南配置完成后真正的挑战在于应对各种边界情况和历史遗留问题。4.1 场景一如何优雅地处理“强制推送”Force Push强制推送在团队协作中是危险的因为它会重写历史。但在某些场景下又必不可少比如本地分支rebase后推送。Gerrit通过Push权限的force修饰符来控制。push force group senior-developers最佳实践不要全局开放force push。仅将其授予可信的、经验丰富的开发者组如senior-developers并且最好限制在功能分支refs/heads/feature/*或开发者个人分支refs/heads/dev/*上。对于master、release/*等共享分支绝对禁止强制推送。4.2 场景二集成CI系统如Jenkins的权限CI系统需要自动验证代码并打Verified标签。为此你需要创建一个专门的服务账户如jenkins-bot和一个对应的组如ci-bots。为该组授予对refs/for/*的Push权限用于获取待验证的变更。授予label-Verified权限到目标分支如refs/heads/master。在CI系统的Gerrit插件中使用该服务账户的HTTP密码或SSH密钥进行认证。避坑点确保CI系统的label-Verified权限范围是精确的避免它给无关分支打标。同时服务账户不应拥有Submit或Code-Review等核心人权权限。4.3 场景三处理遗留分支或特殊分支对于历史遗留的、已不再活跃但需要保护的分支例如old-stable可以单独为其设置一条严格的规则只允许少数人读取禁止任何写入。[access refs/heads/old-stable] read group archive-maintainers push block group Registered Users # 完全禁止推送4.4 常见“坑”与排查技巧坑1权限不生效。首先检查用户是否加入了正确的组。其次使用Gerrit自带的审核查询功能。在项目的“Access”标签页点击“Check Access”输入用户名和引用如refs/heads/masterGerrit会详细列出该用户在此引用上的所有有效权限及其来源来自哪个组、哪条规则。这是最强大的调试工具。坑2Submit按钮灰色。99%的原因是提交规则不满足。检查变更的标签状态Code-Review是否达到2Verified是否达到1规则中的标签名是否拼写正确大小写敏感坑3通配符匹配意外。记住refs/heads/feature-*不会匹配refs/heads/feature/xxx。路径分隔符很重要。refs/heads/feature/*才能匹配feature/下的所有分支。坑4继承覆盖混乱。子项目想覆盖父项目的某条权限时需要在子项目的project.config中重新定义整个[access]段落。Gerrit的继承是“全部或全部不”不能只覆盖一条权限中的部分设置。修改后务必在子项目中使用“Check Access”验证覆盖效果。5. 权限审计与持续维护权限配置并非一劳永逸。团队结构调整、项目演进都需要更新权限。定期审计每季度或每半年导出所有项目的project.config文件进行审查。重点关注是否还存在通用写权限特殊权限组如force push的成员是否合理离职员工的账号是否已从所有组中移除变更流程将权限变更纳入正式的流程。任何权限修改都必须通过克隆refs/meta/config、提交变更集、发起评审、至少获得一位其他核心成员如Tech Lead的2批准后才能合入。这提供了制衡和记录。文档化维护一个内部文档记录每个自定义组的职责、每个项目的权限策略概要例如“master分支需Code-Review2和Verified1仅integrators可合入”。新成员入职时这份文档能帮助他们快速理解协作规范。回到开头的故事如果当时团队遵循了上述实践——为实习生设置了仅能推送至refs/for/*的权限并对refs/heads/*设置了push block那么那次事故根本不会发生。Gerrit的权限系统像一套精密的齿轮理解它、善用它它能成为保障代码质量和开发效率的利器忽视它、误用它它也可能成为团队协作中隐藏的绊马索。花时间把它配置好磨刀不误砍柴工。