首页
Search
1
解决 docker run 报错 oci runtime error
49,608 阅读
2
WebStorm2025最新激活码
28,204 阅读
3
互点群、互助群、微信互助群
23,060 阅读
4
常用正则表达式
21,664 阅读
5
罗技鼠标logic g102驱动程序lghub_installer百度云下载windows LIGHTSYNC
20,036 阅读
自习室
互通有无
搞钱日记
养生记
包罗万象
Search
标签搜索
职场副业
职业发展
副业赚钱
后端开发
内容创作
微服务
分布式系统
效率提升
DevOps
技能提升
流量变现
性能优化
云原生
高并发
编程学习
深度学习
人工智能
架构设计
机器学习
前端开发
loong
累计撰写
3,206
篇文章
累计收到
4
条评论
首页
栏目
自习室
互通有无
搞钱日记
养生记
包罗万象
页面
搜索到
1
篇与
的结果
2026-05-27
Rust开发CLI工具并发布到Homebrew全流程:从项目骨架到用户一行安装
坦白说,很多 Rust CLI 教程只讲到 cargo run 就结束了。但真正让工具被别人用起来,卡点往往不在写代码,而在后半段:如何打包二进制、如何处理版本、如何让 macOS 用户通过 Homebrew 一行命令安装。我见过不少 CLI 项目代码质量不错,却停在 README 里一句“请自行 clone 后 cargo install”。这对开发者还行,对普通用户几乎等于没发布。这篇文章我会按真实发布链路讲完整:用 Rust 开发一个 CLI 工具,配置命令行参数、日志和错误处理,构建 release 二进制,然后发布到 GitHub Release,最后写 Homebrew Formula,让用户执行:brew install yourname/tap/mycli就能安装。先说清楚:Rust CLI 发布到 Homebrew 的本质是什么?Homebrew 并不会神奇地“理解”你的 Rust 项目。它做的事情很朴素:找到你的源码包或二进制包校验 sha256执行构建或安装命令把生成的可执行文件放到 Homebrew 管理的 bin 目录所以 Rust CLI 发布到 Homebrew 通常有两条路:方式做法优点缺点源码构建Formula 下载源码,执行 cargo install 或 cargo build --release符合 Homebrew 习惯,跨架构更自然用户安装时要编译,速度慢预编译二进制Formula 下载已构建好的 tar.gz安装快,可控性强需要为不同平台准备产物根据我的经验,个人工具、团队内部工具可以先走源码构建,简单可靠;如果工具用户量增长,或者依赖较重,再考虑预编译二进制。这篇先用“源码构建”路线,因为它最适合第一次把 Rust CLI 发布到 Homebrew。一个最小但像样的 Rust CLI 项目我们创建一个叫 mycli 的工具,用来接收一个名字并输出问候语。功能不重要,重要的是项目结构和发布方式。cargo new mycli cd mycli推荐的目录结构如下:mycli/ ├── Cargo.toml ├── README.md ├── LICENSE └── src/ └── main.rsCargo.toml 不要随便写。Homebrew、crates.io、GitHub Release 都会间接依赖这里的元信息。[package] name = "mycli" version = "0.1.0" edition = "2021" description = "A small Rust CLI example" license = "MIT" repository = "https://github.com/yourname/mycli" readme = "README.md" [dependencies] anyhow = "1" clap = { version = "4", features = ["derive"] }这里用了两个常见依赖:clap:处理命令行参数,Rust CLI 事实上的主流选择之一anyhow:简化应用层错误处理,适合 CLI 场景如果你写的是库,我会更谨慎地选择错误类型;但 CLI 应用不一样,用户更关心错误信息是否清楚,而不是错误类型层级是否优雅。main.rs:别把 CLI 写成脚本风格很多 Rust CLI 的第一个坑,是把所有逻辑都塞进 main。短期看很快,后面加子命令、配置文件、日志、测试时就会变得难维护。一个更稳的写法是:main 只负责接住错误,业务逻辑放到 run。use anyhow::Result; use clap::Parser; #[derive(Parser, Debug)] #[command(name = "mycli")] #[command(version)] #[command(about = "A small Rust CLI example", long_about = None)] struct Cli { #[arg(short, long, default_value = "world")] name: String, } fn main() { if let Err(err) = run() { eprintln!("error: {err:#}"); std::process::exit(1); } } fn run() -> Result<()> { let cli = Cli::parse(); println!("hello, {}", cli.name); Ok(()) }试一下:cargo run -- --name rust cargo run -- --help cargo run -- --version这里有个坑要注意:cargo run -- --name rust 中间的 -- 不是多余的。前面的参数给 Cargo,后面的参数才传给你的程序。发布前别急:先把 release 构建跑通Homebrew 最终安装的是 release 构建,不是 debug 构建。所以本地必须先跑:cargo build --release ./target/release/mycli --name homebrew如果你依赖了 OpenSSL、系统动态库、C 编译工具链,这一步尤其重要。很多“我本地能跑,brew 安装失败”的问题,本质都是构建环境差异导致的。我建议在提交前至少跑这几条:cargo fmt --check cargo clippy -- -D warnings cargo test cargo build --release如果项目稍微正式一点,可以加 GitHub Actions:name: CI on: push: pull_request: jobs: test: runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo fmt --check - run: cargo clippy -- -D warnings - run: cargo test - run: cargo build --release为什么用 macos-latest?因为我们目标是 Homebrew,至少要保证 macOS 环境构建没问题。当然,实际项目里我通常会再加 Linux。给 Git 打 tag,并创建 GitHub ReleaseHomebrew Formula 需要一个稳定 URL。最常见的做法是引用 GitHub 的源码归档:https://github.com/yourname/mycli/archive/refs/tags/v0.1.0.tar.gz发布流程大概是:git add . git commit -m 'release v0.1.0' git tag v0.1.0 git push origin main --tags然后到 GitHub 创建 Release,选择 v0.1.0 这个 tag。关键在于版本号要统一:Cargo.toml 里的 version = "0.1.0"Git tag 用 v0.1.0Homebrew Formula 里的 URL 指向 v0.1.0这三处不一致,短期也许能跑,但后续维护会非常痛苦。创建自己的 Homebrew TapHomebrew Tap 本质上就是一个 Git 仓库,命名通常是:homebrew-tap比如你的 GitHub 用户名是 yourname,就创建:https://github.com/yourname/homebrew-tap用户安装时会这样写:brew tap yourname/tap brew install mycli也可以合并成:brew install yourname/tap/mycli仓库里创建文件:Formula/mycli.rb注意目录名是 Formula,不是 formula。大小写在某些环境里会让你踩坑。编写 Homebrew Formula一个源码构建版 Formula 可以这样写:class Mycli < Formula desc "A small Rust CLI example" homepage "https://github.com/yourname/mycli" url "https://github.com/yourname/mycli/archive/refs/tags/v0.1.0.tar.gz" sha256 "REPLACE_WITH_SHA256" license "MIT" depends_on "rust" => :build def install system "cargo", "install", "--locked", "--root", prefix, "--path", "." end test do assert_match "hello", shell_output("#{bin}/mycli --name brew") end end这里几个点很关键。depends_on "rust" => :build 表示 Rust 只在构建时需要。安装完成后,用户运行二进制不需要 Rust 工具链。--locked 很重要。它要求使用 Cargo.lock 中锁定的依赖版本,避免 Homebrew 构建时拉到不同依赖导致结果不可控。CLI 项目建议提交 Cargo.lock。--root prefix 是 Homebrew 常见写法,确保二进制安装到 Formula 对应路径。计算 sha256:这是最容易被忽略的小细节Formula 中的 sha256 必须是 URL 下载内容的哈希。你可以这样算:curl -L -o mycli-v0.1.0.tar.gz https://github.com/yourname/mycli/archive/refs/tags/v0.1.0.tar.gz shasum -a 256 mycli-v0.1.0.tar.gz把输出的哈希填进 Formula。这里要注意:如果你重新打了同名 tag,GitHub 归档内容会变,sha256 也会变。更麻烦的是,用户可能已经缓存了旧版本。最佳实践是:tag 发布后不要改。如果必须修复,就发 v0.1.1。本地测试 Formula,而不是直接推给用户试错在 homebrew-tap 仓库里执行:brew install --build-from-source Formula/mycli.rb安装后验证:mycli --name local brew test mycli如果你已经 tap 了自己的仓库,也可以:brew tap yourname/tap brew install yourname/tap/mycli brew test yourname/tap/mycli更新 Formula 后,本地可能有缓存。必要时清理:brew uninstall mycli brew cleanup mycli brew update还有一点,brew audit --strict --online mycli 对个人 tap 不一定每条都必须满足,但它能帮你发现不少格式和元信息问题。brew audit --strict --online Formula/mycli.rb用户安装路径应该怎么写在 README 里?README 不要只写一堆构建命令。用户最关心的是“我怎么安装”和“怎么开始用”。我通常会这样写:## Installation ### Homebrew brew install yourname/tap/mycli ### Cargo cargo install mycli ## Usage mycli --name rust如果你的工具还没发布到 crates.io,就不要写 cargo install mycli,避免误导。可以写:cargo install --git https://github.com/yourname/mycli说实话,README 的安装命令比很多人想象中重要。一个 CLI 工具的转化率,往往就卡在安装步骤是否清楚。版本升级:Formula 要改哪些地方?当你从 0.1.0 升级到 0.1.1:修改 Cargo.toml 版本更新 Cargo.lock提交代码打 v0.1.1 tag创建 GitHub Release更新 Formula 的 url重新计算并更新 sha256Formula 变成:url "https://github.com/yourname/mycli/archive/refs/tags/v0.1.1.tar.gz" sha256 "NEW_SHA256"然后提交到 homebrew-tap:git add Formula/mycli.rb git commit -m 'mycli 0.1.1' git push用户侧执行:brew update brew upgrade mycli常见失败原因:大多不是 Rust 的锅找不到 Cargo.lock如果 Formula 使用 --locked,但源码包里没有 Cargo.lock,构建会失败。CLI 应用应该提交 Cargo.lock。库项目可以不提交,但应用项目提交锁文件是更可控的选择。sha256 不匹配常见原因有两个:你填错了哈希你改过同名 tag解决办法也很直接:重新下载 URL 对应文件,重新计算 sha256。不要猜。Formula 类名不对Formula/mycli.rb 对应类名应是:class Mycli < Formula如果工具名是 my-tool,类名通常写成:class MyTool < FormulaHomebrew 对命名有自己的约定,名字复杂时建议参考官方 Formula。安装成功但命令不可用检查二进制是否真的安装到了 bin:brew list mycli如果用 cargo install --root prefix --path .,正常会生成:.../Cellar/mycli/0.1.0/bin/mycli如果没有,通常是 package name、binary name 或 workspace 配置出了问题。要不要发布到 Homebrew core?很多人一上来就想进 Homebrew core。我的建议是:先维护自己的 tap。Homebrew core 对项目成熟度、下载稳定性、依赖、维护状态都有要求,而且审核标准比个人 tap 严格得多。对大多数新工具来说,自建 tap 已经足够好:安装简单、升级可控、维护成本低。等工具有稳定用户、清晰定位、持续维护记录,再考虑提交到 core 更现实。一张流程图,把全链路串起来Rust CLI 项目 │ ├─ cargo fmt / clippy / test │ ├─ cargo build --release │ ├─ git tag v0.1.0 │ ├─ GitHub Release │ ├─ 计算源码包 sha256 │ ├─ 编写 Formula/mycli.rb │ ├─ brew install --build-from-source 本地验证 │ └─ 用户 brew install yourname/tap/mycli这个流程看起来步骤多,但每一步都在解决一个具体问题:可构建、可校验、可安装、可升级。我认为最值得坚持的几个最佳实践如果只记几条,我建议记这些:Rust CLI 项目提交 Cargo.lockmain 保持薄,把逻辑放进 run发布 tag 后不要修改同名 tagFormula 使用 --locked每次升级都重新计算 sha256README 里把 Homebrew 安装命令放在显眼位置本地跑通 brew install --build-from-source 后再推送Rust 写 CLI 很舒服,但“写出来”和“让别人稳定安装”是两件事。Homebrew 发布流程的价值就在于,把你的工具从一个开发者项目,变成用户可以信任的一条安装命令。如果你正在做第一个 Rust CLI,我的建议是:不要等功能全部完美再发布。先走通一次从代码到 Homebrew 的完整链路。后来你会发现,真正提升工程质量的,往往不是多写一个功能,而是把构建、版本、发布和安装这些环节打磨顺。
2026年05月27日
13 阅读
0 评论
0 点赞