ARTICLE DETAIL

资讯详情

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

MongoDB 仓库 OWNERS.yml 代码所有权机制详解:格式规范、过滤解析与 CODEOWNERS 自动生成

MongoDB 仓库 OWNERS.yml 代码所有权机制详解:格式规范、过滤解析与 CODEOWNERS 自动生成 MongoDB 仓库 OWNERS.yml 代码所有权机制详解格式规范、过滤解析与 CODEOWNERS 自动生成【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo导读本文基于 MongoDB 服务器仓库中的 docs/owners/owners_format.md 官方规范完整讲解仓库的代码所有权Code Ownership体系如何通过分散在各目录的OWNERS.yml文件声明文件归属与审批人approver、filter 的匹配与向上解析规则、aliases 别名文件的定义以及修改OWNERS.yml后如何通过bazel run codeowners重新生成全局的.github/CODEOWNERS数据库。读完本文你将掌握在 MongoDB 仓库中编写、校验和提交OWNERS.yml的完整实战技能并理解这套规范背后的生成器与校验器源码实现。OWNERS 体系总览从 OWNERS.yml 到 CODEOWNERSMongoDB 仓库采用一种分散声明、集中生成的代码所有权模式仓库中大量目录各自维护一份OWNERS.yml当前仓库中共有 672 份声明该目录下文件的审批人顶层工具 codeowners_generate.py 扫描全仓库的OWNERS.yml按目录层级把它们翻译成 GitHub 认识的.github/CODEOWNERS文件任何对OWNERS.yml的修改之后都必须重新生成.github/CODEOWNERS这是仓库的硬性要求。官方文档明确指出After modifying any OWNERS files, the overall ownership database (.github/CODEOWNERS) must be rebuilt. This is done by runningbazel run codeowners.仓库的构建系统还提供了配套校验当 CI 提供带凭据的 expansions 文件时会额外调用 GitHub 解析后的 CODEOWNERS 结果做远程校验remote validation本地没有凭据时该步骤会被跳过但生成generation与本地校验local validation始终照常执行。生成器的实现事实从 codeowners_generate.py 的源码可以看到完整工作流通过evergreen_git.get_files_to_lint()拿到仓库全部文件列表build_tree()按目录构建一棵树每个目录节点最多挂一个OWNERS.yml或OWNERS.yaml文件名由OWNERS_FILE_NAMES (OWNERS.yml, OWNERS.yaml)定义同目录出现两份会直接断言报错process_dir()按根目录优先、逐层向外的顺序深度优先处理每个目录——生成器注释特别强调顺序必须如此以保证根目录内容永远最先写入CODEOWNERS依据OWNERS.yml里的version字段选择解析器parsers {1.0.0: owners_v1.OwnersParserV1(), 2.0.0: owners_v2.OwnersParserV2()}输出文件头部写死一段注释提示该文件由bazel run codeowners生成、请勿手工修改最终写入.github/CODEOWNERS并依次执行三类后置检查详见下文生成后的自动校验。生成器还支持--check参数此时不会重写文件而是用difflib.unified_diff对比新旧内容一旦不一致就以非零退出码报错并打印醒目提示# ACTION REQUIRED: If you are seeing this in CI you need to run bazel run codeowners——这正是 CI 中拦截改了 OWNERS.yml 却忘了重新生成的手段。OWNERS.yml 文件格式规范OWNERS.yml的格式松散地参考了 Kubernetes 与 Chromium 的 OWNERS 文件约定具体实现细节以本仓库为准。一个文件由四大顶级字段组成version、aliases、filters、options。version格式版本version是当前OWNERS.yml文件格式的版本号最新版本为2.0.0。较早版本的差异见文末OWNERS Changelog一节。生成器会校验该字段必须存在且受支持否则直接断言失败。aliases别名导入aliases指向一组 YAML 文件这些文件里定义了可在本OWNERS.yml中使用的别名alias。从源码看别名文件路径必须以//开头、相对于仓库根目录解析例如//buildscripts/.../xxx_aliases.yml每个别名文件内部必须声明version: 1.0.0和aliases两个字段否则解析报错见 owners_v1.py 的process_alias_import。filters过滤器列表filters是一个 glob 模式匹配 gitignore 语法列表每个过滤规则必须满足两个约束至少匹配一个文件模式相对该文件唯一。每条 filter 必须带一个approvers审批人列表任一审批人批准即可让代码合并。也可以指定NOOWNER来把该过滤器标记为无主。每条 filter 还可以携带可选的metadata标签用户可以在里面放任意自定义标签。目前官方预留了两个有意义的标签emeritus_approvers曾经是审批人、现已不再拥有审批权限的人。保留他们是为了在关键情况下能联系到对该代码积累了知识库的前辈。approvers与emeritus_approvers都应当是 GitHub 用户名、邮箱或别名owning_team拥有这些文件的团队但该团队本身没有审批权限而是作为答疑对象。这个元数据也可以被程序化使用例如生成某个团队实际拥有哪些文件的报告——即便该团队已提名具体工程师作为审批人。官方文档说明这两个标签并非穷举列表以后可以继续增加更多有文档或未文档化的选项metadata标签不会做 lint 检查。options文件级选项options为可选项目前支持两个选项默认值作用no_parent_ownersfalse设为true时停止向上的 OWNERS 解析详见Filter Resolutionno_auto_approverfalse设为true时阻止该OWNERS.yml生成的CODEOWNERS条目自动带上svc-auto-approve-bot从 owners_v1.py 的parse()实现可以看到当no_parent_owners为真时解析器会先为目录生成一行无 owner的通配条目确保该目录下没有任何文件继承上层 owner除非匹配到本文件后续的模式no_auto_approver则控制是否调用process_owner(svc-auto-approve-bot)追加机器人审批人该行为还受环境变量ADD_AUTO_APPROVE_USERtrue控制见should_add_auto_approver。完整示例文件文档给出的完整OWNERS.yml示例含逐行注释version: 2.0.0 # corresponds to the owners.yml version you are using aliases: # Contains the markdown-approvers alias - //buildscripts/resmokelib/devprod_test_infrastructure_aliases.yml filters: # List of all filters - *: # Select all files (will apply recursively) approvers: # Anyone on this list can approve PRs - devprod-test-infrastructure # alias for a group of users - IamXander # github username - user.namemongodb.com # email address metadata: emeritus_approvers: # This list is just for historical reference - userB owning_team: 10gen/devprod-test-infrastructure # The team which owns the matching files. These folks are not required approvers that will block a PR. - /*: # Select all files in the current directory (not recursive) approvers: # Anyone on this list can approve PRs - devprod-test-infrastructure # alias for a group of users - *.md: # Select all markdown files in the current directory (not recursive) approvers: - markdown-approvers - **/*.py: # Select all python files (will apply recursively) approvers: - python-approvers - config.txt: # Select the config.txt file in the current directory (not recursive) approvers: - config-approvers - **/BUILD.bazel: # Select all BUILD.bazel files (will apply recursively) approvers: - bazel-approvers options: # All options for this file no_parent_owners: false # See above for no_parent_owners. Defaulted to false so this line is not needed. no_auto_approver: false # Prevents auto-adding svc-auto-approve-bot for this OWNERS file.关于 approvers 中的三种身份写法解析器owners_v1.py 的process_owner会做归一化处理含的条目按邮箱原样加入且强制要求是mongodb.com邮箱否则抛错其余条目视为GitHub 用户名自动补前缀条目若命中已导入的别名则展开为该别名下的成员列表NOOWNER必须独占整个 approvers 列表否则断言失败。真实仓库示例根目录 OWNERS.yml仓库根目录的 OWNERS.yml 是格式 1.0.0 的真实范例展示了团队别名10gen/...形式、metadata.emeritus_approvers含 TODO 注释说明某人暂被移出 approvers以及大量针对构建/格式化配置文件的精细过滤器version: 1.0.0 filters: - *: approvers: - 10gen/mongo-default-approvers - OWNERS.yml: approvers: - 10gen/server-root-ownership metadata: emeritus_approvers: - visemet # TODO SERVER-122669: add back to approvers once project work is finished - /BUILD.bazel: approvers: - 10gen/devprod-build - .bazelrc*: approvers: - 10gen/devprod-build - .clang-format: approvers: - 10gen/server-programmability ...再如 buildscripts/cost_model/OWNERS.yml展示了团队级授权模式目录默认归10gen/query-optimization而OWNERS.yml文件本身的修改需要10gen/query-optimization-staff-leads审批version: 1.0.0 filters: - *: approvers: - 10gen/query-optimization - OWNERS.yml: approvers: - 10gen/query-optimization-staff-leadsFilter Resolution过滤器解析规则解析规则是这套体系的核心语义官方文档用两条原则概括自底向上从被修改文件所在的最深目录OWNERS.yml开始逐级向仓库根目录走。最深一层中能匹配上的 filter 胜出——更上层的OWNERS.yml只有在更深的文件中没有任何 filter 匹配时才会被参考。no_parent_owners: true会彻底终止向上遍历。同文件内有序同一文件里的所有 filter 按顺序逐一求值最后一个匹配的 filter 胜出。这与 GitHub 原生CODEOWNERS的最后匹配模式优先语义完全一致。模式书写速查文档给出了常见模式及其匹配范围均相对于该OWNERS.yml所在目录模式匹配范围*当前目录及其所有子目录下的全部文件/*仅当前目录下的全部文件不含子目录*.py当前目录下的全部 Python 文件不含子目录/*.py同上显式写出当前目录**/*.py当前目录及其子目录下的全部 Python 文件/**/*.py同上显式写出当前目录BUILD.bazel当前目录下的BUILD.bazel文件不含子目录**/BUILD.bazel当前目录及其子目录下的BUILD.bazel文件两种版本解析器的差异源码佐证对比 owners_v1.py 与 owners_v2.py 的get_owner_line可以精确定位 v1/v2 的语义差别v1模式中不含/时会被自动扩展成/{directory}/**/{pattern}即隐式递归v2只有pattern *或空模式才扩展为/{directory}/即 v2 将*解析为目录名保证默认递归其余模式一律原样拼接为/{directory}/{pattern}不再隐式加**/是否递归完全由作者显式书写决定。这正是 v2.0.0 变更日志的核心内容详见OWNERS Changelog。另外两个版本都会对最终模式做test_pattern校验用glob.iglob(..., recursiveTrue)确认至少能匹配到一个真实文件否则抛出RuntimeError(fCan not find any files that match pattern: ...)杜绝写了一个永远匹配不上的孤儿规则。Aliases 别名文件格式别名文件同样是 YAML格式要求如下version恒为1.0.0aliases是组名列表每个组名必须包含一个或多个reviewer评审人评审人应为 GitHub 用户名。官方示例version: 1.0.0 aliases: devprod-build: - IamXander # github username - user.namemongodb.com # email address结合源码补充两点约束别名文件路径必须以//开头仓库根相对导入时由process_alias_import校验别名展开发生在 approvers 归一化之前——别名成员可以是 GitHub 用户名也可以是mongodb.com邮箱展开后走与普通 approver 相同的处理逻辑。Filter Resolution 示例三份文件逐层演练文档用a/b/c/三层目录结构完整演示了解析过程这里原样继承a/b/c/OWNERS.ymlversion: 2.0.0 aliases: - //aliases.yml filters: - **/*.py: approvers: - teamC - **/*.md: approvers: - teamMDa/b/OWNERS.ymlversion: 2.0.0 aliases: - //aliases.yml filters: - **/*.json: approvers: - teamB - **/*.py: approvers: - teamPY options: no_parent_owners: truea/OWNERS.ymlversion: 2.0.0 aliases: - //aliases.yml filters: - *: approvers: - teamA - **/*.yaml: approvers: - teamYAMLExample 1修改a/b/c/file.py解析从最深的a/b/c/OWNERS.yml开始先比较file.py是否匹配**/*.md否再检查是否匹配**/*.py是于是选中teamC作为评审团队。最深层命中即终止不会向上查询。Example 2修改a/b/c/file.yaml依然从a/b/c/OWNERS.yml开始没有任何 filter 匹配.yaml。向上走到a/b/OWNERS.yml**/*.json与**/*.py都不匹配.yaml。由于该文件设置了no_parent_owners: true向上遍历到此为止——即使a/OWNERS.yml中明明有**/*.yaml规则也不会被使用。最终该文件没有评审团队。这就是no_parent_owners的隔离语义它把本目录及子树从全局所有权体系中断开。生成后的自动校验新文件、孤儿文件与禁用 ownerbazel run codeowners生成.github/CODEOWNERS后生成器还会自动执行三类检查见 codeowners_generate.py 的post_generation_checks校验生成的 CODEOWNERSrun_validator调用独立下载的 codeowners 校验二进制对结果做本地校验该校验二进制按平台/架构由 codeowners_binary.bzl 中的 repository rule 下载当前锁定 v1.2.3覆盖 linux/macos 的 amd64/arm64。新文件必须有人负责check_new_files结合 expansions 文件与目标分支找出本次 patch 新增的文件若新文件无主(unowned)或仅由默认 owner 兜底CODEOWNERS_DEFAULT_OWNER环境变量指定则报错提示新文件必须声明非默认的代码 owner。不得制造孤儿文件check_orphaned_files拿新生成的 CODEOWNERS 与基线修订版本中的旧 CODEOWNERS 对比检查是否有文件因本次修改失去了所有权或回落到默认所有权防止改着改着把文件改没了归属。禁用 owner 黑名单check_banned_codeowners若设置了BANNED_CODEOWNERS_FILE_PATH仓库中对应 .github/BANNED_CODEOWNERS.txt会逐行扫描 CODEOWNERS命中黑名单中的 owner 即报错。此外还有一套允许无主文件的白名单机制配置在 .github/ALLOWED_UNOWNED_FILES.yml。从 codeowners_generate.py 的get_allowed_unowned_files实现看其格式要求version必须为1.0.0每个 filter 必须包含justification理由与filter模式两个字段模式必须以/开头、禁止通配符、必须真实存在且不能是目录。仓库当前允许无主的文件只有.github/CODEOWNERS它本身就是生成物、modules_poc/modules.yaml、.agents/skills/OWNERS.yml与cspell.json等少量特殊文件每个都附带了明确理由例如生成文件非生产代码类似 linter 配置所有团队都应能编辑。OWNERS Changelogv1.0.0 → v2.0.0 的破坏性变更v2.0.0 相对 v1.0.0 引入了两条不兼容变更升级到 v2.0.0 格式时必须注意不含斜杠的模式不再自动加**/前缀v1 中写*.py会隐式变成递归匹配v2 中它只匹配当前目录。若需要递归必须自己显式写**/*.py。*模式现在解析为目录名v2 保证*默认递归匹配当前目录及子目录若只想匹配当前目录内请使用/*。这两条规则与上文两种版本解析器的差异中的源码实现完全对应。当前仓库内大量OWNERS.yml仍是version: 1.0.0例如根目录 OWNERS.yml 与 buildscripts/cost_model/OWNERS.yml二者在生成器中同时受支持仓库处于两种格式并存的过渡期新增或改写的文件按官方文档建议使用最新的2.0.0。实战总结修改 OWNERS.yml 的标准流程综合文档与源码在 MongoDB 仓库中调整代码所有权的推荐流程是找到目标目录下的OWNERS.yml不存在则新建按上述格式规范书写version/aliases/filters/options模式务必先用文档的速查表确认匹配范围避免 v1/v2 语义混淆模式必须能匹配到真实文件否则生成时会被test_pattern拒绝需要团队别名时先在对应的//路径别名文件中定义再在approvers中引用运行bazel run codeowners重新生成.github/CODEOWNERS观察生成器的本地校验输出确认没有触发新文件无主文件失去所有权禁用 owner等检查告警将OWNERS.yml与重新生成的.github/CODEOWNERS一并提交——CI 中的--check模式会验证 CODEOWNERS 是否与 OWNERS.yml 同步若不同步将直接拦截合并。这套机制让谁负责哪些代码的声明下沉到每个目录、贴近实际维护者又通过集中生成与自动化校验保证全局所有权数据库的一致性是大规模 C 仓库中保障代码评审质量与责任归属的关键基础设施。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表