首页
/ ripgrep 发布工作流详解:基于 RELEASE-CHECKLIST 的多 Crate 发布、CI 协同与产物校验全流程

ripgrep 发布工作流详解:基于 RELEASE-CHECKLIST 的多 Crate 发布、CI 协同与产物校验全流程

2026-09-03 16:55:52作者:齐冠琰

ripgrep 采用“一个根包 + 多个独立发布 crate”的 workspace 结构,每次新版本发布都需要按固定顺序完成依赖审计、逐 crate 发版、版本打标、CI 构建多平台产物与 Homebrew formula 更新等动作。本文以 RELEASE-CHECKLIST.md 为骨架,结合仓库中的根 Cargo.tomlCHANGELOG.mdci/sha256-releasespkg/brew/ripgrep-bin.rb 等文件,完整还原 ripgrep 从 master 到正式 release 的每一步操作、其背后的工程原因以及可落地的自动化提示。

1. 发布前必须理解的仓库结构

理解发布清单的前提,是先理解 ripgrep 的包组织方式。根 Cargo.toml 中,ripgrep 包本身就是命令行工具 rg 的宿主包,其 [[bin]] 指向 crates/core/main.rs

[[bin]]
bench = false
path = "crates/core/main.rs"
name = "rg"

同一个文件还声明了 workspace 成员:

[workspace]
members = [
  "crates/globset",
  "crates/grep",
  "crates/cli",
  "crates/index",
  "crates/matcher",
  "crates/pcre2",
  "crates/printer",
  "crates/regex",
  "crates/searcher",
  "crates/ignore",
]

这意味着一次 ripgrep 发布通常牵动十几份 Cargo.toml:根包只发布 ripgrep 这一个 crate 到 crates.io,而 crates/ 下的各 crate 各自独立发版、各自有版本号。当前仓库中各 crate 的版本快照(各 crate Cargo.toml 中均带有 #:version 注释标记,供自动化工具定位版本行):

crate 路径 当前版本
globset crates/globset/Cargo.toml 0.4.19
ignore crates/ignore/Cargo.toml 0.4.32
cli crates/cli/Cargo.toml 0.1.12
matcher crates/matcher/Cargo.toml 0.1.9
regex crates/regex/Cargo.toml 0.1.14
pcre2 crates/pcre2/Cargo.toml 0.1.10
searcher crates/searcher/Cargo.toml 0.1.17
printer crates/printer/Cargo.toml 0.3.1
grep crates/grep/Cargo.toml 0.4.1
index crates/index/Cargo.toml 0.0.1(crate 名 grep-index

注意一个细节:crates/index 在发布清单中并未出现。从源码结构看,它对应根 Cargo.toml 中可选依赖 grep-index,仅由 unstable-index feature 启用,注释明确写着“currently in active development… Use at your own risk”,因此它不参与常规发布流程。另外,“core” 也不是独立 crate——发布清单里的 crates/core 指的就是根包本身。

2. 第一步:同步分支与依赖审计

发布清单的前三条对应依赖治理,顺序不能乱:

  1. 确保本地 masterorigin/master 同步。清单以 master 作为发布基准分支,一切版本修改都应从最新的上游主干切出。
  2. 运行 cargo update 并审阅依赖更新,提交更新后的 Cargo.lock。这一步把间接依赖推到兼容的最新版,是发布前“锁文件清洁”的基线。
  3. 运行 cargo outdated 审阅 semver 不兼容的更新。清单的要求是:“除非有充分的理由不做,否则每个依赖都应该被审阅并更新”;同时要求追加 --aggressive 参数查看跨 semver 的候选,但不要更新到仍处于 beta 阶段的 crate

这条约束的动机很直白:ripgrep 是面向最终用户的命令行工具,依赖面(正则引擎、搜索器、忽略规则、打印器)任何一个不稳定都可能直接影响产物质量,因此“beta 依赖”被明确排除在发布路径之外。

3. 第二步:文档、Changelog 与各 crate 发版

3.1 更新 man 页日期

清单要求“Update date in crates/core/flags/doc/template.rg.1”。这个模板文件的首行正是需要修改的位置:

.TH RG 1 2026-07-15 "!!VERSION!!" "User Commands"

2026-07-15CHANGELOG.md 中 15.2.0 的发布日期一致,!!VERSION!! 是构建时注入的版本占位符。也就是说,这一项的实际操作就是:把 .TH 行里的日期改成本次发布日期,让 man 页头部的“文档日期”随版本走。

3.2 更新 CHANGELOG 并逐 crate 审查改动

清单第 5、6 条要求:按情况更新 CHANGELOG,然后审查 crates 目录下每个 crate 自上次 ripgrep 发布以来的改动;若某个 crate 的改动集非空,就要为该 crate 发一个新版本。审查顺序被严格固定为:

1. crates/globset
2. crates/ignore
3. crates/cli
4. crates/matcher
5. crates/regex
6. crates/pcre2
7. crates/searcher
8. crates/printer
9. crates/grep(必要时 bump 最小版本)
10. crates/core(不 bump 版本号,但按需更新依赖)

这个顺序实际上就是依赖拓扑序:底层基础件(globset → ignore)先走,中间件(cli、matcher、regex、pcre2、searcher、printer)随后,组合层 grep 最后按需提高所声明依赖的“最小版本”(minimal version),最顶层的 core(即根包)只更新依赖、不发 crate 版本。这样做保证了下游 crate 发布时,其 Cargo.toml 里写的最小依赖版本在 crates.io 上真实可用。

清单还给出了发布单个 crate 的自动化命令:

cargo-up --no-push crates/{CRATE}/Cargo.toml

每次更新某个 crate 后,清单特别强调:“确保依赖方中的最小版本得到相应更新”——这正是第 9 项 grep 条目里 “bump minimal versions as necessary” 的含义。

4. 第三步:修改根版本号并做打包验证

4.1 设置新版本

清单给出的操作是:编辑根 Cargo.toml 设置新的 ripgrep 版本(当前仓库为 version = "15.2.0",该行同样带 #:version 标记),然后:

cargo update -p ripgrep   # 让 Cargo.lock 与根包版本保持一致

提交变更后创建一个签名 tag。清单还提供了等价的一步式写法:

cargo-up --no-push --no-release Cargo.toml {VERSION}

4.2 cargo package 验证

清单要求在发布前执行 cargo package 并确保成功。这一步模拟 cargo 从 crates.io 下载源码后的打包过程,可以提前暴露两类问题:

  • 打包产物遗漏或误包含文件;
  • 打包后的包无法独立构建(例如依赖了未提交的文件)。

ripgrep 在根 Cargo.toml 中为此显式维护了排除清单,让发布包只包含真正需要的内容:

exclude = [
  "HomebrewFormula",
  "/.github/",
  "/ci/",
  "/pkg/brew",
  "/benchsuite/",
  "/scripts/",
  "/crates/fuzz",
]

可以看到 CI 脚本、Homebrew formula、基准数据目录都被排除在 crates.io 包之外——这也解释了为什么仓库里 CI 与 formula 更新流程必须单独存在,而不是随 crate 一起分发。

5. 第四步:两阶段推送与 CI 协同

发布清单中最体现“实战经验”的是推送策略,核心规则有三条:

  1. 先把变更 push 到 GitHub,但不包含 tag;同时不要向 crates.io 发布新版 ripgrep(crates.io 发布推迟到 GitHub release 完成之后)。
  2. master 的 CI 全部通过后再 push 版本 tag。清单解释了原因:试图一步完成(推代码和推 tag 合并操作)会导致 GitHub Actions 看不到 tag push 事件,从而不触发 release workflow。
  3. 若 release 构建失败,处理路径是固定的:从 GitHub 删除该 tag → 修复 → 重新打 tag → 删除失败产生的 release → 重新推送 tag。

这种“代码先行、tag 殿后”的两阶段策略本质上是把 tag 当作触发器:tag push 才会启动多平台 release 构建,因此必须保证主干 CI 已经验证过这份代码,tag 触发的构建才不会在已知问题上浪费一轮完整构建。

release 构建完成后,清单要求把 CHANGELOG 中对应版本的章节复制进 GitHub release notes,并固定附上一段项目简介:

In case you haven't heard of it before, ripgrep is a line-oriented search tool that recursively searches the current directory for a regex pattern. By default, ripgrep will respect gitignore rules and automatically skip hidden files/directories and binary files.

这段文案与根 Cargo.toml[package]description[package.metadata.deb]extended-description 保持同一语义,保证各个分发渠道对 ripgrep 的描述一致。

6. 第五步:cargo publish 与 Homebrew formula 更新

6.1 发布到 crates.io

GitHub release 构建成功后,执行:

cargo publish

只有此时才把新版 ripgrep 推到 crates.io——清单刻意把这一动作排在 release 构建之后,确保二进制发布与 crates.io 发布不会因为 CI 故障出现“crate 已发、二进制没有”的不一致状态。

6.2 用 ci/sha256-releases 更新 Homebrew formula

清单的最后一段命令是:

ci/sha256-releases {VERSION} >> pkg/brew/ripgrep-bin.rb

然后编辑 pkg/brew/ripgrep-bin.rb,更新版本号与 sha256 值,并删掉 ci/sha256-releases 追加进来的多余内容,最后提交。

对照脚本实现可以弄清“多余内容”指什么。ci/sha256-releases 会遍历 i686x86_64 两个架构、apple-darwinunknown-linux-musl 两个目标,外加 zip/tar.gz 两种源码归档,逐条从 release 下载计算 sha256:

for arch in i686 x86_64; do
  for target in apple-darwin unknown-linux-musl; do
    url=".../releases/download/$version/ripgrep-$version-$arch-$target.tar.gz"
    sha=$(curl -sfSL "$url" | sha256sum)
    echo "$version-$arch-$target $sha"
  done
done

而当前 formula 实际只消费其中两条(macOS 用 x86_64-apple-darwin,Linux 用 x86_64-unknown-linux-musl):

class RipgrepBin < Formula
  version '15.0.0'
  ...
  if OS.mac?
      url ".../ripgrep-#{version}-x86_64-apple-darwin.tar.gz"
      sha256 "af7825..."
  elsif OS.linux?
      url ".../ripgrep-#{version}-x86_64-unknown-linux-musl.tar.gz"
      sha256 "33e15b..."
  end
  ...
end

因此“删掉多余内容”就是剔除 i686 行和 source 归档行等 formula 用不到的输出,再手工把 version 与两个 sha256 对齐到新版本。仓库根目录还保留了一份 HomebrewFormula/ripgrep-bin.rb 作为 Homebrew 官方 formula 目录结构的镜像。

值得注意的一个现实细节:当前仓库里 formula 的 version 仍是 15.0.0,而根包版本已到 15.2.0——从文件状态可以推断,formula 的更新是在对应版本的发布流程中随 tag 一并完成的,这里看到的是清单执行完某一版本后、尚未推进下一次更新时的快照。

7. 收尾:在 CHANGELOG 顶部补回 TBD 段

发布动作全部完成后,清单要求在 CHANGELOG.md 顶部重新加入占位段:

TBD
===
Unreleased changes. Release notes have not yet been written.

当前仓库的 CHANGELOG 正是这个形态:TBD 段在最上方,其后紧接 15.2.0 (2026-07-15) 的正式条目。这保证主干在任何时刻都有一段“未发布变更”的落点,下一个开发周期的提交说明有处可写,也保证了下次发布时“复制相关章节到 release notes”这一步有明确的边界。

8. 工具链依赖与关键注意事项汇总

清单末尾说明 cargo-up 脚本可参考作者 BurntSushi 的 dotfiles 仓库获取,仓库本身不内置该脚本。它是整个清单自动化程度的关键:

命令 清单中的作用
cargo update / cargo outdated --aggressive 发布前依赖审计(第 2 节)
cargo-up --no-push crates/{CRATE}/Cargo.toml 发布单个 workspace crate
cargo-up --no-push --no-release Cargo.toml {VERSION} 一步完成根版本号修改 + Cargo.lock 更新
cargo package 发布前打包验证(第 4.2 节)
cargo publish release 构建成功后发布 crates.io 包

几条最容易踩坑的点,都直接来自清单原文:

  1. 不要更新到 beta cratecargo outdated --aggressive 的结果需人工筛选);
  2. core 不 bump 版本号,它不是独立 crate,只同步依赖;
  3. tag 必须晚于代码 push 单独推送,否则 GitHub Actions 可能不触发 release workflow;
  4. crates.io 发布排在 GitHub release 构建成功之后,避免半发布状态;
  5. ci/sha256-releases 的输出是“原料”不是成品,追加进 formula 后必须手工裁剪并核对版本与哈希。

9. 参考资料

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341