release-plz 驱动的 Rust 工作区发布流水线:OpenLogi 版本管理全流程拆解
【免费下载链接】OpenLogi⚡️A native, local-first alternative to Logitech Options+, written in Rust 🦀 — remap buttons, DPI, and SmartShift over HID++. No account, no telemetry.项目地址: https://gitcode.com/GitHub_Trending/op/OpenLogi
OpenLogi 是一个用 Rust 编写的本地优先(local-first)罗技外设管理工具,作为 Logitech Options+ 的开源替代:重映射按键、调节 DPI 与 SmartShift,无需账号、无遥测。这篇文章拆解它的 Rust 工作区发布流水线——如何用 release-plz 统一管理 19 个 crate 的版本号、自动生成发布 PR、用 git-cliff 生成变更日志,再到打 tag 触发签名打包与多渠道发布。
📦 先看全景:19 个 crate,一个版本号
打开根目录的 Cargo.toml,你会发现一个关键设计:
- 工作区包含 19 个成员 crate(HID 协议、设备驱动、GUI、Agent、CLI 等),见 Cargo.toml
- 所有 crate 共享一个版本号:
[workspace.package]下声明version = "0.8.1",各 crate 通过version.workspace = true继承
这意味着整个项目"同进同退"——发版时只改一处,所有 crate 一起升版本、一起打一个v{version}tag。对新手来说,这是多 crate 项目最省心的一种版本模型。
整条流水线可以用一张图概括:
push master ──▶ release-plz 自动开 PR(升版本 + 预览) │ 合并 "chore: release" PR │ release job:xtask 校验 ──▶ 打 v0.9.0 tag │ release.yml:编译 ──▶ 签名公证 ──▶ 校验和 ──▶ 发布 │ GitHub Release + 更新通道 latest.json + homebrew-tap⚙️ 配置核心:release-plz.toml 如何把版本"锁"在一起
一切从根目录的 release-plz.toml 开始。核心配置只有三点,却回答了多 crate 发布最常见的三个难题:
1️⃣version_group:强制版本同步
在 release-plz.toml 中,11 个要发布到 crates.io 的 crate(openlogi、openlogi-core、openlogi-hidpp、openlogi-cli等)都归入version_group = "openlogi"。这样它们永远联动升级,避免"A crate 依赖的 B crate 还没发布"这种地狱场景。
2️⃣release = false:应用 crate 不参与发版
release-plz.toml 里,openlogi-desktop(GUI)、openlogi-agent(托盘后台)、openlogi-overlay等 6 个"应用型"crate 被标记为不发布——OpenLogi 以安装程序形态交付,不是给人cargo install的 API 库。
3️⃣ 职责切分:谁管 tag、谁管 Release、谁管 CHANGELOG
这是最容易踩坑的地方,配置文件头部的注释写得很直白:
| 职责 | 归属 | 原因 |
|---|---|---|
| 升版本号 | release-plz | 它的看家本领 |
打v*tag | release-plz(仅openlogi根 crate) | 一个 tag 代表整个工作区 |
| GitHub Release | release.yml的 softprops | 若 release-plz 先建 Release,后续上传资产时会遇到 "release immutable" 错误 |
| CHANGELOG.md | git-cliff | release-plz 按 crate 路径过滤,看不到不发布的应用 crate,无法覆盖全仓库 |
另外几个细节值得新手注意:
semver_check = false:release-plz.toml 里禁用了 semver 破坏性检查——应用项目不需要在发布 PR 里看"API breaking"噪音release_always = false:release-plz.toml 确保只有发布 PR 合并时才发版,master 上后续的提交绝不会误切 tag
🚀 第一棒:push master 自动打开发布 PR
工作流 .github/workflows/release-plz.yml 监听 master 的每次 push,运行release-plz release-pr:
- 从
release-plz/前缀的分支打开一个 PR,内容是各 crate 的版本号 bump + 按 crate 的变更预览 - 通过 1Password 中的本地 Action 铸造 GitHub App token(.github/workflows/release-plz.yml),因为 GITHUB_TOKEN 推送无法触发下游工作流
亮点动作:PR 打开后,流水线会用git cliff为这个即将发布的版本写入整库 CHANGELOG,然后直接把发布 PR 的正文替换成这份 changelog(见 release-plz.yml)。也就是说,评审发布 PR = 阅读版本说明,合并 PR = 确认发版,流程非常直觉。
📝 CHANGELOG.md 为什么交给 git-cliff
.config/cliff.toml 定义了 changelog 生成规则:
- 只认 Conventional Commits:
feat→ Added、fix→ Fixed、perf→ Changed(cliff.toml) - 覆盖全仓库,不按 crate 路径过滤——GUI 的改动同样进 changelog
#123自动转换为 PR 链接
xtask 中对应的命令是 xtask/src/commands/release/changelog.rs:它以最近一个vX.Y.Ztag 为下界运行git cliff,且每次重跑都会先删除旧的同版本段落,保证发布 PR 反复更新时结果幂等。
🛡️ 第二棒:合并前的三道 xtask 保险
发布 PR 合并后,触发条件苛刻的releasejob 才会执行(仅当提交信息以chore: release开头,或手动 dispatch,见 release-plz.yml)。它先运行两个 xtask 子命令,全部定义在 xtask/src/commands/release.rs:
①checkout-version-bump:把发布钉死在正确的 commit 上
checkout_version_bump.rs 会先按提交信息chore: release v0.9.0查找升版提交,找不到再回退到"首次把 Cargo.toml 改成该版本"的提交,然后强制检出到它。目的:绝不让比升版提交更新的 master 尖端被发版——如果 crates.io 发布失败,你可以修好凭据后在同一 SHA 上重试,而不是切出一个错误的版本。若该版本已打过 tag,直接输出skip=true跳过。
②check-publish:发布闭包校验
check_publish.rs 解析cargo metadata,确保:
- 每个可发布 crate 对内部 path 依赖都声明了 registry 版本号(
req == "*"直接报错) - 可发布 crate 不能依赖未发布的内部 crate
这正是 release-plz.toml 中openlogi-camera必须留在版本组里的原因:crates.io 上的包无法依赖"未发布的路径依赖"。
🏷️ 第三棒:tag 触发 release.yml,签名、校验、发布一条龙
release-plz 打完v*tag 后,接力棒交给 .github/workflows/release.yml,它由四个 job 组成:
build:复用同一构建矩阵为 macOS / Windows / Linux 编译安装程序,sign: true启用签名与公证(macOS DMG 是发布门禁)。
release-notes:用 Node 脚本生成 GitHub Release 说明(generate.ts),仅 tag 运行时执行。
publish:真正的发布中枢,注意它的门禁条件(release.yml)——只要求macOS 腿成功,Windows/Linux 腿尽力而为。这样实验性的 arm64 Windows 构建失败不会拖垮 macOS 发布。具体步骤:
- 生成
SHA256SUMS校验和(DMG 缺失则大声失败) - 用 minisign 对所有产物生成
.minisig分离签名并回验 - 运行
cargo run -p xtask -- release latest-json生成静态更新清单 latest.json(供 gpui-updater 消费) - 产物上传 R2(版本目录 +
channels/stable/latest.json更新通道) - 最后一步才用 softprops 创建 draft → 上传全部资产 → 转正式,规避 "immutable release" 陷阱(release.yml)
homebrew-tap:向 Homebrew tap 仓库发送update-openlogi事件,让brew install openlogi自动跟进新版本。
✅ 清单:这套流水线值得抄的设计
🎯 给维护多 crate Rust 项目的朋友,OpenLogi 的做法浓缩为 6 条:
- 单版本号 +
version_group:工作区同进同退,一个 tag 代表一次发版 - 发布 PR 即 changelog:git-cliff 全库视角,合并 PR = 审批发版
- 职责单一化:release-plz 只管升版本和打 tag,Release 生命周期交给专门工作流
- 发布钉在升版 commit:失败可在同一 SHA 安全重试
- 发布闭包前置校验:在打 tag 前发现"依赖了未发布 crate"的硬失败
- 门禁分级:核心平台是硬门禁,实验性平台降级为尽力而为,绝不让旁路拖垮主发布
完整细节可继续阅读仓库文档 docs/DEVELOPMENT.md 与 xtask/README.md,或直接在 release-plz.toml 中查看每行配置的注释——那是本文信息量最密集的一处"活文档"。
【免费下载链接】OpenLogi⚡️A native, local-first alternative to Logitech Options+, written in Rust 🦀 — remap buttons, DPI, and SmartShift over HID++. No account, no telemetry.项目地址: https://gitcode.com/GitHub_Trending/op/OpenLogi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考