Protobuf 的 Bazel Central Registry 自动发布机制:.bcr 配置目录全解析
本文基于 protobuf 仓库的 .bcr/README.md 展开:它说明了 protobuf 每次发版时如何自动发布到 Bazel Central Registry(下称 BCR,即 registry.bazel.build),并给出了自动化发布所依赖的三个配置文件。读完本文,你将理解 metadata.template.json、source.template.json 与 presubmit.yml 各自承载的信息、它们的字段含义与模板占位符规则,以及 BCR 发布前测试(presubmit)与日常 Bazel CI 测试之间的联动关系,从而能够为自己的 Bazel 模块搭建一套同样严谨的 BCR 自动发布流程。
1. 背景:为什么要为 protobuf 配置 BCR 自动发布
BCR 是 Bazel 官方的中央注册表,集中托管采用 Bzlmod(MODULE.bazel)模块系统的开源项目版本。对于 protobuf 这样的基础依赖,发布到 BCR 意味着下游项目只需在 MODULE.bazel 中声明一行 bazel_dep 就能拉取指定版本,而不必自行维护源码归档地址与校验和。
README 的核心表述只有一句话:
When protobuf is released, we want it to be published to the Bazel Central Registry automatically.
This folder contains configuration files to automate the publish step.
即:.bcr/ 目录是"发布自动化"的驱动目录。发布时使用的工具链是 bazel-contrib/publish-to-bcr(README 中指向其 templates/README.md 作为这些文件格式的权威文档),它会读取本目录下的三个文件,按模板渲染后向 BCR 仓库发起版本发布。
2. 目录结构与三个关键文件
当前 .bcr/ 目录包含四个文件:
| 文件 | 作用 |
|---|---|
| .bcr/README.md | 说明本目录用途,指向官方模板文档 |
| .bcr/metadata.template.json | 渲染为 BCR 的 metadata.json:主页、维护者、仓库、已发布版本、撤销版本 |
| .bcr/source.template.json | 渲染为 BCR 每个版本的 source.json:源码下载 URL 与 strip 规则 |
| .bcr/presubmit.yml | 定义发布到 BCR 之前运行的测试矩阵与构建目标 |
三者分工明确:metadata 描述"这个模块是谁维护的",source 描述"每个版本的源码从哪里下载",presubmit 描述"发布前必须通过哪些构建验证"。
3. metadata.template.json:模块元数据
.bcr/metadata.template.json 是发布时 metadata.json 的模板,其字段在当前仓库中的实际取值如下。
3.1 顶层字段
homepage:固定为https://github.com/protocolbuffers/protobuf,是 BCR 页面上展示的项目主页。repository:数组形式,当前为["github:protocolbuffers/protobuf"]。这个github:前缀的简写让 BCR 能够识别模块来源仓库,用于变更监控等场景。versions:当前为空数组[]。该字段记录已发布进 BCR 的版本列表,发布流程会自动更新。yanked_versions:当前为空对象{}。用于记录被"撤回"的版本及原因,下游在版本解析时会跳过这些版本。
3.2 maintainers 数组
maintainers 列出模块维护者,每个条目包含 name、email、github、github_user_id 四个必填字段,以及可选的 do_not_notify。当前配置中包含 Protobuf Team(机器人账号 protobuf-team-bot)以及多名 Google 成员。其中两类维护者值得注意:
- 前几位维护者(如 Protobuf Team、Sandy Zhang 等)没有
do_not_notify字段,默认会在 BCR 相关通知中被提及; - 多数 Google 维护者显式设置了
"do_not_notify": true,表示仅作为归属记录、不参与通知。
这种"维护者全集 + 通知开关"的写法,是团队型项目控制 BCR 通知噪音的常见做法。
4. source.template.json:源码归档的定位规则
.bcr/source.template.json 全文仅三个字段,但正是发布自动化的核心:
{
"integrity": "**leave this alone**",
"strip_prefix": "{REPO}-{VERSION}",
"url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{VERSION}.bazel.tar.gz"
}
逐字段说明:
url:发布产物地址模板。{OWNER}、{REPO}、{TAG}、{VERSION}是publish-to-bcr在渲染时替换的占位符,最终指向 GitHub Release 中的protobuf-<version>.bazel.tar.gz归档。也就是说,protobuf 的每次 release 都会额外产出一个.bazel.tar.gz归档专门供 BCR 使用,而不是直接抓取源码仓库。strip_prefix:{REPO}-{VERSION},即解包后去掉protobuf-<version>/前缀,使归档内容对齐模块根目录。integrity:模板中写死的**leave this alone**是刻意保留的占位文案——真实的 sha256 完整性校验和必须在发布时由工具自动计算并填入,人工保留此占位符可防止手工填写出错的校验和进入 BCR。
这套"归档 URL + strip_prefix + 自动 integrity"的组合,保证下游在 Bzlmod 下载模块时既能验证归档完整性,又能获得确定性的目录结构。
5. presubmit.yml:发布前测试矩阵
.bcr/presubmit.yml 定义了 BCR 在接收 protobuf 新版本时必须通过的预检任务。当前完整配置如下:
# LINT.IfChange(bcr_presubmit)
bcr_test_module:
module_path: examples
matrix:
platform: ["debian12", "macos_arm64", "ubuntu2404", "windows"]
bazel: [8.x, 9.x]
tasks:
verify_targets:
name: "Verify build targets"
platform: ${{ platform }}
bazel: ${{ bazel }}
build_targets:
- '//...'
- '@com_google_protobuf//:protobuf'
- '@com_google_protobuf//:protobuf_lite'
- '@com_google_protobuf//:protobuf_python'
- '@com_google_protobuf//:protoc'
- '@com_google_protobuf//:test_messages_proto2_cc_proto'
- '@com_google_protobuf//:test_messages_proto3_cc_proto'
# LINT.ThenChange(<ROOT_DIR>/.bazelci/presubmit.yml)
各配置项的含义:
bcr_test_module:BCR 预检以"独立模块"为单位运行——它会克隆被测试的模块并执行构建,而不是在 protobuf 仓库自身里跑测试。module_path: examples:指定用仓库中的 examples 子模块作为测试入口。选择 examples 的原因在于它正是一个"下游消费者"视角的模块:examples/MODULE.bazel 中通过bazel_dep(name = "protobuf", version = "0.0.0", repo_name = "com_google_protobuf")声明对 protobuf 模块的依赖(开发期用local_path_override指向仓库根,BCR 预检时则会解析为待发布的真实版本)。换言之,BCR presubmit 验证的是"下游项目依赖 protobuf 后能否正常构建",而非 protobuf 自身测试套件。matrix:平台与 Bazel 版本的双重矩阵——4 个平台(debian12、macos_arm64、ubuntu2404、windows)× 2 个 Bazel 大版本(8.x、9.x),共 8 个组合,每个组合都执行verify_targets任务。build_targets:在//...(examples 模块内全部目标)之外,显式列出 6 个关键外部目标:C++ 全量库:protobuf、轻量版:protobuf_lite、Python 绑定:protobuf_python、Java 绑定:protobuf_java、编译器:protoc,以及 proto2/proto3 两条测试消息代码生成目标:test_messages_proto2_cc_proto、:test_messages_proto3_cc_proto。这与根模块 MODULE.bazel 中声明的repo_name = "com_google_protobuf"、bazel_compatibility = [">=8.0.0"]相互印证:presubmit 矩阵覆盖的 Bazel 8.x/9.x 正是根模块声明的兼容性下限之上的范围。
6. 与 Bazel CI 的联动:LINT.IfChange 双向约定
一个容易忽视但非常重要的细节是 presubmit.yml 首尾的 LINT.IfChange / LINT.ThenChange 注释,它们与 .bazelci/presubmit.yml 构成双向同步约定:
- .bcr/presubmit.yml 开头为
# LINT.IfChange(bcr_presubmit),结尾为# LINT.ThenChange(<ROOT_DIR>/.bazelci/presubmit.yml); - .bazelci/presubmit.yml 开头为
# LINT.IfChange(bazelci_presubmit),结尾为# LINT.ThenChange(<ROOT_DIR>/.bcr/presubmit.yml)。
这意味着修改任何一个文件都会强制要求同时修改另一个文件(否则提交无法通过 lint 检查),迫使两个矩阵保持同步。而两个文件为什么要保持"同一套测试",.bazelci/README.md 给出了明确解释:.bazelci/presubmit.yml 是 Bazel CI 日常跑的任务集,.bcr/presubmit.yml 是发布到 BCR 前跑的任务集,两者应包含相同的测试集合——这保证了"CI 天天验证通过的构建"与"发布到 BCR 时验证的构建"是同一件事,避免出现"日常构建通过、发布预检失败"的断层。对比两个文件可以看到:BCR 版本以 bcr_test_module 包装并指定 module_path: examples,Bazel CI 版本则是同一组 build_targets 加上 working_directory: examples 的等价写法。
7. 从 examples 模块看下游消费视角
BCR 预检选用 examples 作为测试模块并非随意。examples/MODULE.bazel 展示了一个最小可运行的消费者模块:
module(
name = "com_google_protobuf_examples",
version = "0.0.0",
compatibility_level = 1,
)
bazel_dep(name = "protobuf", version = "0.0.0", repo_name = "com_google_protobuf")
local_path_override(
module_name = "protobuf",
path = "..",
)
其中 repo_name = "com_google_protobuf" 使 @com_google_protobuf// 前缀在所有 BUILD 文件中可用,presubmit 里列出的构建目标正是以此为前缀;local_path_override 只在本地开发时生效,BCR 预检环境下依赖会解析为待发布的 protobuf 真实版本,从而完成端到端验证。此外 examples 还通过 dev_dependency 引入了 examples/examples_with_hyphen 子模块,用于覆盖模块名包含连字符这类 Bzlmod 边界场景。
8. 小结:可复用的 BCR 发布配置范式
从 protobuf 的 .bcr/ 目录可以提炼出一套可直接迁移到自有项目的发布自动化范式:
metadata.template.json:维护homepage、repository、maintainers(含do_not_notify控制通知面),versions与yanked_versions交给发布工具维护;source.template.json:指向 release 产出的专用 Bazel 归档,strip_prefix与归档目录名对齐,integrity占位符保持**leave this alone**由工具计算;presubmit.yml:用"下游消费模块"(如 examples)作为module_path,在"平台 × Bazel 版本"矩阵上构建核心库、编译器与绑定目标,保证发布前的验证视角与用户一致;- 与 CI 文件双向 LINT 锁定:通过
LINT.IfChange/LINT.ThenChange强制 BCR presubmit 与 Bazel CI presubmit 的测试集同步演化。
以上即 .bcr/README.md 所述"automate the publish step"的完整落地:三个模板文件定义了"发布什么、从哪取源码、发布前验证什么",配合根目录的 MODULE.bazel 模块声明,protobuf 的每次 release 都能以可复现、可验证、跨平台的方式自动进入 Bazel Central Registry。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00