mcpField
R
rust-semver

rust-semver

github.com/full-stack-skills开发12106 次安装5 周前更新
代码仓库

关于 rust-semver

面向 Rust crate 维护者的发布管理技能,讲解 SemVer 兼容性并检查 API 破坏性变更,也涵盖发布、工作区版本协调、撤回版本和安全公告处理。

主要功能

  1. 01判断破坏性与兼容性变更
  2. 02应用 1.0 前版本规则
  3. 03解读 cargo-semver-checks 结果
  4. 04协调工作区发布
  5. 05处理撤回版本与安全公告

适用场景

  1. 01确定 crate 的版本升级幅度
  2. 02发布前检查 API 变更
  3. 03准备多 crate 工作区发布
  4. 04响应 crate 漏洞公告

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/full-stack-skills/rust-skills --skill rust-semver

安装后在 Claude Code 中输入/rust-semver确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
examples/
workspace-publishing.md
examples/golden-semver/
Cargo.lock
Cargo.toml
examples/golden-semver/src/
lib.rs
references/
cargo-semver-checks-lints.md
semver-reference.md

详细介绍

Rust Semver 与发布工作流

权威参考:Cargo Semver Reference、RFC 1105 (Semver)、cargo-semver-checks、RustSec Advisory Database。

此技能负责判定 Rust 中什么是破坏性变更,处理 crates.io 的发布工作流,以及协调整个工作区的发布。它不负责 API 设计(rust-api-design)、Cargo 清单机制(rust-cargo-build)或 lint 配置(rust-style-clippy)。

能力边界

✅ 优势
  1. 判断特定代码变更应提升主版本号、次版本号还是修订版本号
  2. 理解 1.0 之前(0.x.y)的特殊规则
  3. 运行并解读 cargo-semver-checks(270 多项 lint)
  4. 通过 cargo-workspaces 协调多 crate 工作区发布
  5. 处理撤回(yank)、弃用和 RustSec 安全公告工作流
  6. 了解 #[non_exhaustive]、sealed trait、功能标志、隐藏模块对 semver 的影响
  7. 编写与变更类型相符的发行说明
⚠️ 前提条件
  1. 具有明确公共 API 的 Rust crate——参见 rust-api-design
  2. Cargo 清单和 cargo publish 基础知识——参见 rust-cargo-build
❌ 不在范围内
  1. 设计 API 本身 → rust-api-design
  2. Cargo 清单格式和构建配置 → rust-cargo-build
  3. Clippy lint 规则 → rust-style-clippy
  4. 用于验证行为的测试设计 → rust-testing

数据隐私

此技能不会收集、存储或传输用户数据。向 crates.io 发布属于公开操作——运行 cargo publish 前请先征得用户同意。


第 1 部分:什么是公共 API?

Semver 适用于公共 API 表面。在 Rust 中,公共 API 是下游可见的一切,但其定义并不简单。

可见项

// src/lib.rs
pub fn hello() {}                    // public API
pub struct User { pub name: String } // public API
pub mod network {                    // public API
    pub fn connect() {}              // public API
}

mod internal {                       // private — NOT public API
    pub fn helper() {}               // pub, but module is private → not reachable
}

规则:当且仅当下游 crate 无需 unsafe 就能命名某项时,该项才属于公共 API。pub(crate) 模块中的 pub 项不属于公共 API。

通过重新导出实现可达

// src/lib.rs
mod hidden;
pub use hidden::Config;   // Config IS public API (reachable via crate root)

虽然 hidden 是私有的,但可以通过重新导出访问 Config——因此它属于公共 API。

隐藏模块

#[doc(hidden)]
pub mod unstable { /* */ }

**就 semver 而言,#[doc(hidden)] 不会将项从公共 API 中移除。**下游仍然可以 use crate::unstable::Foo。如果你希望这些项保持不稳定,应通过功能标志进行门控,或将其移至私有模块。

受功能标志控制的项

#[cfg(feature = "json")]
pub mod json;   // public API only when feature enabled

Semver 契约针对每种功能组合分别成立。在 json 下添加项,对启用了 json 的用户来说属于次版本变更,对未启用该功能的用户则不可见。


第 2 部分:破坏性变更与非破坏性变更

Cargo Book 列出了规范变更。概要如下:

始终属于破坏性变更(提升主版本号,1.0 之前除外)

变更原因
移除公共项引用了该项的下游代码会出错
重命名公共项等同于移除旧项并添加新项
更改函数签名(参数/返回类型)调用方将无法通过编译
向非 #[non_exhaustive] 枚举添加新变体match 缺少通配分支
向公共结构体字面量添加字段如果 Foo 新增字段,Foo { x: 1 } 将出错
为外部类型实现 trait(或反过来)会与下游的孤儿实现冲突
收紧 trait 约束下游实现可能不再满足要求
将 pub 项改为 pub(crate)从 API 中移除
移除 Copy/Clone/Debug 派生下游可能依赖这些特性
更改公共依赖项的 semver 版本可能导致传递性破坏

不构成破坏性变更(次版本或补丁版本升级)

变更原因
添加新的公共函数增量式变更
为你自己的类型添加新的 trait 实现增量式变更
为 #[non_exhaustive] 枚举添加一个变体调用方有通配分支
为私有字段结构体添加一个字段调用方无法使用结构体字面量创建实例
添加新的功能标志对未使用该功能的用户不可见
放宽 trait 约束更加宽松
为封闭 trait 添加默认方法封闭 = 由你控制实现
使用 #[deprecated] 弃用渐进式移除

容易忽视的情况

为公共 trait 添加方法
// Before
pub trait Foo { fn a(&self); }
// After
pub trait Foo { fn a(&self); fn b(&self); }   // ❌ BREAKING

下游对 Foo 的实现会无法通过编译(缺少 b)。解决方法:任选其一:(a)升级主版本;(b)在添加方法前封闭该 trait;或(c)将 b 添加为默认方法(fn b(&self) {})。

为未封闭的 trait 添加默认方法
pub trait Foo { fn a(&self); fn b(&self) {} }  // ✅ non-breaking

默认方法不构成破坏性变更。但之后不能移除默认实现,否则会构成破坏性变更。

带默认值的泛型参数
// Before
pub fn parse<T>(input: &str) -> T { /* */ }
// After
pub fn parse<T = String>(input: &str) -> T { /* */ }   // ✅ non-breaking

为泛型参数添加默认值不构成破坏性变更。移除默认值则会构成破坏性变更。

trait 对象安全性

如果 Foo 当前满足对象安全(Box<dyn Foo> 可用),那么使其不再满足对象安全(添加 Self: Sized 约束、添加泛型方法)会构成破坏性变更。

添加生命周期
// Before
pub fn parse(input: &str) -> &str { /* */ }
// After
pub fn parse<'a>(input: &'a str) -> &'a str { /* */ }   // ✅ usually non-breaking

生命周期省略规则会使大多数生命周期变更不影响调用方。


第 3 部分:1.0 之前(0.x.y)的规则

版本为 0.x 的 crate 遵循经过调整的语义化版本规则:

版本兼容范围破坏性变更时递增的部分
0.0.x无——实验阶段,可进行任何变更0.0.x
0.x.y,其中 x > 0仅限相同的 0.x.*次版本号(x)的位置 =“主版本号”

因此,0.1.0 → 0.2.0 属于主版本升级。0.1.0 → 0.1.1 属于补丁版本升级(修复 bug)。

# Allowed in Cargo.toml:
[dependencies]
my-crate = "0.1"     # means >=0.1.0, <0.2.0

达到 1.0.0 后,恢复使用常规语义化版本规则。

何时升级到 1.0

  • API 已稳定(未来约 6 个月内没有计划中的破坏性变更)
  • 已被其他 crate 使用(表明它有用)
  • 所有基础设计决策都已确定

不要害怕发布 1.0。许多 crate 因为害怕承担承诺而在 0.x 阶段停留太久。1.0 的代价是“我承诺,破坏性变更需要升级主版本”,这通常完全没问题。


第 4 部分:cargo-semver-checks

cargo-semver-checks 是一个工具,可将 crate 当前的公共 API 与已发布版本进行比较,并标记破坏性变更。270 多项 lint 覆盖了大多数情况。

安装和运行

cargo install cargo-semver-checks --locked

# Before publishing a new version:
cargo semver-checks check-release

# Compare against a specific baseline:
cargo semver-checks check-release --baseline-version 1.2.0

# Compare against a git tag:
cargo semver-checks check-release --baseline-tag v1.2.0

解读输出

--- failure function_missing ---
Description: Function removed or renamed, or return type changed.
Error: the function `parse` was removed from the API

每个失败都代表违反了语义化版本规则。可通过以下方式修复:

  1. 恢复该项(不要实际移除它)
  2. 升级主版本(承认这是破坏性变更)
  3. 使用兼容性垫片(以旧名称重新添加,并委托给新的实现)

集成到 CI

# .github/workflows/semver.yml
name: Semver Check
on: [pull_request]
jobs:
  semver:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: obi1kenobi/cargo-semver-checks-action@v2

引入破坏性变更的 PR 会导致 CI 失败,除非相应地升级版本。

常见的 lint 命中项

Lint含义修复方法
function_missing函数已移除或重命名恢复该函数或升级主版本
enum_variant_added为非 #[non_exhaustive] 枚举添加了变体标记为 #[non_exhaustive]
struct_field_added为公共结构体添加了字段将字段设为私有或标记为 #[non_exhaustive]
trait_method_added为未封闭的 trait 添加了方法添加默认实现或封闭该 trait
inherent_method_missing移除了实现块中的方法恢复该方法或升级主版本
type_marked_deprecated添加了 #[deprecated]可以接受,升级次版本

第 5 部分:发布工作流

发布单个 crate

# 1. Bump version in Cargo.toml
# 2. Update CHANGELOG.md
# 3. Run quality gates
cargo fmt --all --check
cargo test --workspace --all-targets --all-features
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo semver-checks check-release

# 4. Verify package contents
cargo package --list    # show files included
cargo package           # build the .crate in target/package/
cargo publish --dry-run # full simulation

# 5. Publish
cargo publish           # 🚨 public action — confirm with user first

通过 cargo-workspaces 同步发布工作区

对于多 crate 工作区,cargo-workspaces 可自动完成版本升级和发布流程:

cargo install cargo-workspaces

# Bump all members together:
cargo workspaces version minor      # 0.1.0 → 0.2.0 across all members

# Bump with custom prerelease:
cargo workspaces version prerelease --pre-id beta

# Publish all members in topological order:
cargo workspaces publish

cargo-workspaces:

  1. 编辑每个成员的 Cargo.toml 版本
  2. 将内部的 path = "" 依赖更新到新版本
  3. 如果传入 --no-individual-tags 或 --individual-tags,则提交变更并打标签
  4. 按依赖顺序发布(先发布依赖,再发布依赖方)

撤回已发布的版本

如果某个版本存在严重漏洞,撤回它(阻止新增依赖,不会移除现有依赖):

cargo yank --vers 1.2.3
# To undo:
cargo yank --vers 1.2.3 --undo

对于通过 Cargo.lock 固定该版本的项目,已撤回的版本仍会保留在注册表中。

弃用 crate

通过维护者页面在 crates.io 上将该 crate 标记为已弃用。下游用户运行 cargo add 时会看到弃用通知。


第 6 部分:RustSec 安全公告

RustSec 安全公告数据库 跟踪已发布 crate 中已知的漏洞。

cargo-audit

cargo install cargo-audit --locked
cargo audit               # scan Cargo.lock against advisory DB
cargo audit --deny warnings   # fail CI on any advisory

cargo-deny(功能更全面)

cargo-deny 涵盖:

  • 安全公告 — RustSec
  • 许可证 — 拒绝未经授权的许可证
  • 禁止项 — 拒绝特定 crate
  • 来源 — 拒绝特定的 git/注册表来源
# deny.toml
[advisories]
db-urls = ["https://github.com/rustsec/advisory-db"]
yanked = "deny"

[licenses]
allow = ["MIT", "Apache-2.0", "BSD-3-Clause"]
confidence-threshold = 0.8
cargo install cargo-deny --locked
cargo deny check

响应 crate 中的安全公告

  1. 在问题跟踪器中确认收到
  2. 修复问题并发布新版本
  3. 向 RustSec 提交安全公告(如适用):https://github.com/rustsec/advisory-db/
  4. 撤回存在漏洞的版本(修复发布后)
  5. 通过 CHANGELOG 和发行说明告知用户

第 7 部分:发行说明

每次发布时,都要在 CHANGELOG.md 中添加条目。建议格式:

## [1.2.0] - 2026-07-21

### Added
- `Client::builder()` for ergonomic construction (#45)
- `Visibility::IncludeHidden` variant

### Changed
- `parse()` now accepts `impl AsRef<str>` instead of `&str`

### Deprecated
- `Client::new()` — use `Client::builder()` instead

### Fixed
- Panic on empty input in `parse()` — now returns `Err`

工具:


工作流

  1. 盘点差异 — 自上次发布以来,pub 项发生了哪些变化?
  2. 对每项变更分类 — 破坏性变更、新增功能或修复(参见第 2 部分)
  3. 决定版本号调整幅度 — 主版本/次版本/补丁版本;记住 1.0 前的规则
  4. 运行 cargo-semver-checks — 确认你的分类与工具结果一致
  5. 更新 CHANGELOG — 说明变更内容、原因以及迁移说明
  6. 运行质量检查 — fmt、test、clippy、doc
  7. 发布 — 单个 crate(cargo publish)或工作区(cargo workspaces publish)
  8. 处理发布后的事项 — 如果有问题则撤回,如果涉及安全问题则提交安全公告

快速决策参考

变更版本号调整备注
新增 pub fn次版本新增功能
修复 bug补丁版本行为变更,不涉及 API 变更
移除 pub fn主版本或恢复后标记为弃用
新增封闭 trait 方法次版本封闭表示你可以控制其实现
新增未封闭 trait 方法主版本或添加默认实现
新增枚举变体主版本(如果标记为 #[non_exhaustive],则为次版本)
新增 pub 结构体字段主版本(如果标记为 #[non_exhaustive] 或该字段为私有,则为次版本)
提升 MSRV次版本(如果最低版本为 1.0+)记录相关策略
将依赖升级至不兼容的语义化版本主版本主版本传递性破坏变更

资源

上游来源

评论

支持 Markdown