关于 dotnet-release-management
dotnet-release-management 是面向 .NET 团队的发布生命周期技能,涵盖版本管理、变更日志、预发布阶段和分支策略,并介绍基于 NBGV 的版本计算。
主要功能
- 01NBGV 与 version.json 版本配置
- 02按 SemVer 评估变更级别
- 03Keep a Changelog 与 git-cliff
- 04alpha、beta、rc 发布流程
- 05发布分支与 trunk-based 模式
适用场景
- 01为 .NET 库判断 major、minor 或 patch
- 02从 Git 提交历史计算版本号
- 03按 Conventional Commits 生成变更日志
- 04规划预发布到稳定版的演进
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-release-management安装后在 Claude Code 中输入/dotnet-release-management确认已加载。
详细介绍
dotnet-release-management
.NET 项目的发布生命周期管理:使用 version.json 配置 Nerdbank.GitVersioning(NBGV)、计算版本高度,以及管理公开发布与预发布模式;适用于 .NET 库的 SemVer 2.0 策略(何时提升 major/minor/patch 版本、API 兼容性考量)以及适用于应用程序的策略(构建元数据、部署版本管理);生成变更日志(Keep a Changelog 格式,使用 git-cliff 和 conventional commits 自动生成);预发布版本工作流(alpha、beta、rc、stable 的递进);以及发布分支模式(发布分支、热修复分支、带标签的主干开发发布)。
**版本假设:**基准版本为 .NET 8.0+。Nerdbank.GitVersioning 3.6+(当前稳定版)。SemVer 2.0 规范。
**范围边界:**此技能负责发布生命周期策略——版本管理、变更日志、预发布工作流和分支模式。插件专属的发布工作流(dotnet-artisan 版本管理和发布)记录在仓库级 CONTRIBUTING.md 中。CI/CD 发布工作流(NuGet 推送、容器推送、部署)由 [skill:dotnet-gha-publish] 和 [skill:dotnet-ado-publish] 负责。GitHub Release 创建和资源管理由 [skill:dotnet-github-releases] 负责。NuGet 包版本属性(Version、PackageVersion)由 [skill:dotnet-nuget-authoring] 负责。
**范围外:**插件专属的发布工作流——参见仓库级 CONTRIBUTING.md。CI/CD NuGet 推送和部署工作流——参见 [skill:dotnet-gha-publish] 和 [skill:dotnet-ado-publish]。GitHub Release 创建和资源附加——参见 [skill:dotnet-github-releases]。NuGet 包元数据和签名——参见 [skill:dotnet-nuget-authoring]。项目级配置(SourceLink、CPM)——参见 [skill:dotnet-project-structure]。
交叉引用:[skill:dotnet-gha-publish] 介绍 CI 发布工作流,[skill:dotnet-ado-publish] 介绍 ADO 发布工作流,[skill:dotnet-nuget-authoring] 介绍 NuGet 包版本属性。
NBGV(Nerdbank.GitVersioning)
NBGV 根据 git 历史计算确定性版本号。版本由 version.json 文件和 git 提交高度(自设置版本以来的提交数)推导得出,无需手动提升版本,即可为每次提交生成唯一版本。
安装
# Install NBGV CLI tool
dotnet tool install --global nbgv
# Initialize NBGV in a repository
nbgv install
# This creates version.json at the repo root
version.json 配置
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0",
"publicReleaseRefSpec": [
"^refs/heads/main$",
"^refs/tags/v\\d+\\.\\d+(\\.\\d+)?(-.*)?$"
],
"cloudBuild": {
"buildNumber": {
"enabled": true
},
"setVersionVariables": true
}
}
version.json 字段参考
| 字段 | 用途 | 示例 |
|---|---|---|
version | 基础版本(major.minor,可选 patch) | "1.0"、"2.3.0" |
publicReleaseRefSpec | 生成公开版本的分支/标签正则表达式模式 | ["^refs/heads/main$"] |
cloudBuild.buildNumber.enabled | 将 CI 构建编号设为计算得出的版本 | true |
cloudBuild.setVersionVariables | 将版本导出为 CI 环境变量 | true |
nugetPackageVersion | 覆盖 NuGet 包版本格式 | {"semVer": 2} |
assemblyVersion.precision | 程序集版本的组件数量 | "major"、"minor"、"build"、"revision" |
inherit | 从父目录的 version.json 继承 | true |
版本高度的工作原理
NBGV 统计自 version.json 中 version 字段上次更改以来的提交数。该计数会成为 patch 版本:
version.json: "version": "1.2"
Commit history:
abc1234 feat: add caching -> 1.2.3
def5678 fix: null check -> 1.2.2
ghi9012 chore: update deps -> 1.2.1
jkl3456 Bump version to 1.2 -> 1.2.0 (version.json changed here)
版本高度确保每次提交都有唯一版本,无需人工干预。
预发布与公开发布
{
"version": "1.2-beta",
"publicReleaseRefSpec": [
"^refs/heads/main$",
"^refs/tags/v\\d+\\.\\d+(\\.\\d+)?(-.*)?$"
]
}
| 分支/引用 | 计算得出的版本 | 说明 |
|---|---|---|
main(公开) | 1.2.5-beta | 公开预发布版本,高度=5 |
feature/foo(非公开) | 1.2.5-beta.gcommithash | 包含 git 哈希后缀 |
标签 v1.2.5(公开) | 1.2.5 | 创建标签前移除 -beta |
要发布稳定版本,请在发布提交前从 version.json 中移除预发布后缀:
{
"version": "1.2"
}
NBGV CLI 命令
# Show the current calculated version
nbgv get-version
# Show specific version properties
nbgv get-version -v NuGetPackageVersion
nbgv get-version -v SemVer2
# Prepare for a release (creates release branch, bumps version)
nbgv prepare-release
# Set version variables for CI
nbgv cloud
Monorepo NBGV 配置
对于包含独立版本项目的 monorepo,请将 version.json 放在每个项目目录中,并使用 inherit:
repo-root/
version.json <- { "version": "1.0" }
src/
LibraryA/
version.json <- { "version": "2.3", "inherit": true }
LibraryB/
version.json <- { "version": "1.1-beta", "inherit": true }
inherit 字段会从父级 version.json 中继承设置(例如 publicReleaseRefSpec 和 cloudBuild),同时覆盖版本号。
.NET 库的 SemVer 策略
何时提升版本号
SemVer 2.0 指定的版本格式为 MAJOR.MINOR.PATCH:
| 变更类型 | 版本提升 | 示例 |
|---|---|---|
| 破坏性 API 变更 | Major | 移除公共类型/成员、更改方法签名、重命名命名空间 |
| 新功能(向后兼容) | Minor | 添加公共类型/成员、新扩展方法、新重载 |
| Bug 修复(向后兼容) | Patch | 修复错误行为、性能改进、内部重构 |
.NET 特有的破坏性变更注意事项
| 变更 | 是否具有破坏性? | 备注 |
|---|---|---|
| 移除公共类型 | 是(Major) | 引用该类型的使用者将无法编译 |
| 移除公共方法 | 是(Major) | 直接调用方将无法使用 |
| 为公共方法添加必需参数 | 是(Major) | 现有调用方不会传入该参数 |
| 为公共方法添加可选参数 | 否(Minor) | 二进制兼容,但对使用命名参数的调用方而言会造成源代码破坏 |
| 更改返回类型 | 是(Major) | 破坏二进制兼容性和源代码兼容性 |
| 添加新的公共类型 | 否(Minor) | 不影响现有代码 |
| 添加新重载 | 否(Minor) | 现有调用仍可正常解析 |
| 更改内部实现 | 否(Patch) | 不更改公共 API |
| 更改可选参数的默认值 | 否(Patch) | 二进制兼容(重新编译时,该值会嵌入调用位置) |
| 将之前未密封的类设为密封 | 是(Major) | 继承该类的使用者将无法使用 |
| 将虚方法改为非虚方法 | 是(Major) | 重写该方法的使用者将无法使用 |
API 兼容性验证
使用 EnablePackageValidation 检查意外的破坏性变更。完整的软件包验证设置请参阅 [skill:dotnet-nuget-authoring]。
<PropertyGroup>
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion>
</PropertyGroup>
应用程序的 SemVer 策略
应用程序(Web 应用、桌面应用、服务)的版本控制注意事项与库不同,因为它们没有公共 API 使用者。
应用程序版本控制方法
| 方法 | 格式 | 最适用于 |
|---|---|---|
| SemVer(由功能驱动) | 1.2.3 | 用户可见版本号的已安装桌面/移动应用 |
| CalVer(基于日历) | 2024.1.15 | 持续部署的 SaaS 应用 |
| 构建编号 | 1.2.3+42 | 使用构建元数据进行 CI 驱动的版本控制 |
| NBGV 高度 | 1.2.42 | 根据 git 提交自动进行版本控制 |
构建元数据
SemVer 2.0 允许使用以 + 为后缀的构建元数据;它不会影响版本优先级:
1.2.3+build.42 Build number
1.2.3+abcdef Git commit hash
1.2.3+2024.01.15 Build date
1.2.3-beta.1+42 Pre-release with build metadata
构建元数据有助于追溯已部署的二进制文件对应的源代码提交。NBGV 会自动追加 git 元数据。
部署版本控制
对于持续部署的服务,版本标记有助于排查问题:
<PropertyGroup>
<InformationalVersion>1.2.3+abcdef.2024-01-15</InformationalVersion>
</PropertyGroup>
在运行时读取:
var version = typeof(Program).Assembly
.GetCustomAttribute<System.Reflection.AssemblyInformationalVersionAttribute>()
?.InformationalVersion;
// Returns "1.2.3+abcdef.2024-01-15"
变更日志生成
Keep a Changelog 格式
Keep a Changelog 格式是一种广泛采用的标准:
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Widget caching support for improved throughput
## [1.2.0] - 2024-03-15
### Added
- Fluent API for widget configuration
- Batch processing support
### Changed
- Improved error messages for invalid widget states
### Fixed
- Memory leak in widget pool under high concurrency
- Timezone handling in scheduled widget operations
### Deprecated
- `Widget.Create()` static method -- use `WidgetBuilder` instead
## [1.1.0] - 2024-01-10
### Added
- Widget serialization support
[Unreleased]: https://github.com/mycompany/widgets/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/mycompany/widgets/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/mycompany/widgets/releases/tag/v1.1.0
章节类型
| 章节 | 用途 |
|---|---|
Added | 新功能 |
Changed | 对现有功能的更改 |
Deprecated | 将在未来版本中移除的功能 |
Removed | 此版本中移除的功能 |
Fixed | Bug 修复 |
Security | 漏洞修复 |
使用 git-cliff 自动生成
git-cliff 会根据约定式提交生成变更日志:
# Install git-cliff
cargo install git-cliff
# Generate changelog for all versions
git cliff --output CHANGELOG.md
# Generate changelog for unreleased changes only
git cliff --unreleased --output CHANGELOG.md
# Generate notes for a specific tag range
git cliff --tag v1.2.0 --unreleased
配置 cliff.toml 以适配 .NET 约定式提交模式:
# cliff.toml
[changelog]
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
commit_parsers = [
{ message = "^feat", group = "Added" },
{ message = "^fix", group = "Fixed" },
{ message = "^perf", group = "Changed" },
{ message = "^refactor", group = "Changed" },
{ message = "^docs", group = "Documentation" },
{ message = "^chore\\(deps\\)", group = "Dependencies" },
{ message = "^chore", skip = true },
{ message = "^ci", skip = true },
{ message = "^test", skip = true },
]
常规提交格式
feat: add widget caching support
fix: correct timezone handling in scheduler
feat!: rename Widget.Create() to WidgetBuilder.Build()
chore(deps): update System.Text.Json to 8.0.5
docs: update API reference for caching
Breaking change in body:
feat: redesign widget API
BREAKING CHANGE: Widget.Create() has been removed. Use WidgetBuilder instead.
| 前缀 | SemVer 影响 | 更新日志章节 |
|---|---|---|
feat: | 次要版本 | 新增 |
fix: | 修订版本 | 修复 |
feat!: 或 BREAKING CHANGE: | 主版本 | 重大变更 |
perf: | 修订版本 | 变更 |
refactor: | 修订版本 | 变更 |
docs: | 无 | 文档 |
chore: | 无 | (跳过) |
预发布版本工作流
标准预发布版本递进
alpha -> beta -> rc -> stable
1.0.0-alpha.1 Early development, API unstable
1.0.0-alpha.2 Continued alpha iteration
1.0.0-beta.1 Feature-complete, API stabilizing
1.0.0-beta.2 Beta bug fixes
1.0.0-rc.1 Release candidate, final validation
1.0.0-rc.2 RC bug fix (if needed)
1.0.0 Stable release
NBGV 预发布工作流
# Start with pre-release suffix in version.json
# version.json: { "version": "1.0-alpha" }
# Produces: 1.0.1-alpha, 1.0.2-alpha, ...
# Promote to beta
# Edit version.json: { "version": "1.0-beta" }
# Produces: 1.0.1-beta, 1.0.2-beta, ...
# Promote to rc
# Edit version.json: { "version": "1.0-rc" }
# Produces: 1.0.1-rc, 1.0.2-rc, ...
# Promote to stable
# Edit version.json: { "version": "1.0" }
# Produces: 1.0.1, 1.0.2, ...
手动预发布工作流
对于未使用 NBGV 的项目:
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>beta.1</VersionSuffix>
</PropertyGroup>
从 CI 覆盖:
# CI sets the pre-release suffix
dotnet pack /p:VersionSuffix="beta.$(BUILD_NUMBER)"
# Stable release: omit VersionSuffix
dotnet pack
NuGet 预发布排序
NuGet 遵循 SemVer 2.0 的预发布优先级规则:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.2
1.0.0-alpha.2 < 1.0.0-beta
1.0.0-beta < 1.0.0-beta.1
1.0.0-rc.1 < 1.0.0
数字标识符按整数比较;字母标识符按字典序比较。
发布分支模式
基于主干并使用标签
最简单的发布模式。所有开发都在 main 上进行,发布通过标签标记。
main: A -- B -- C -- D -- E -- F -- G
| |
v1.0.0 v1.1.0
# Tag and push for release
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
适用于: 库、小型团队、持续交付。
发布分支
创建发布分支进行稳定化,同时 main 继续开发。
main: A -- B -- C -- D -- E -- F -- G
\
release/1.0: C' -- D' -- E'
|
v1.0.0
# Create release branch
git checkout -b release/1.0 main
# Stabilize on release branch (bug fixes only)
git commit -m "fix: correct null check in widget pool"
# Tag and release
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin release/1.0 v1.0.0
# Merge fixes back to main
git checkout main
git merge release/1.0
适用于: 有支持合同的产品、LTS 版本,以及需要并行开发与稳定化的团队。
NBGV prepare-release
NBGV 可自动创建发布分支并提升版本:
# Creates release/v1.0 branch, bumps main to 1.1-alpha
nbgv prepare-release
# What this does:
# 1. Creates branch "release/v1.0" from current commit
# 2. On release branch: removes pre-release suffix (version: "1.0")
# 3. On main: bumps to "1.1-alpha" (next development version)
热修复分支
针对已发布版本的紧急修复:
main: A -- B -- C -- D -- E
\
release/1.0: C' -- v1.0.0
\
hotfix/1.0.1: F' -- v1.0.1
# Branch from the release tag
git checkout -b hotfix/1.0.1 v1.0.0
# Fix the critical issue
git commit -m "fix: critical security vulnerability in auth handler"
# Tag and release the hotfix
git tag -a v1.0.1 -m "Hotfix v1.0.1"
git push origin hotfix/1.0.1 v1.0.1
# Merge hotfix back to main
git checkout main
git merge hotfix/1.0.1
分支模式比较
| 模式 | 发布节奏 | 并行版本 | 复杂度 |
|---|---|---|---|
| 主干 + 标签 | 持续 | 否 | 低 |
| 发布分支 | 定期 | 是 | 中 |
| GitFlow(完整) | 定期 | 是 | 高 |
对于大多数 .NET 开源库,基于主干并使用标签搭配 NBGV 已足够。仅当产品需要同时维护多个受支持版本时,才使用发布分支。
Agent 注意事项
-
NBGV 的
version.json仅使用 major.minor(而非 major.minor.patch)——修订版本由提交高度计算。设置"version": "1.2.3"会将修订版本固定为 3,违背自动版本管理的初衷。 -
NBGV 需要 Git 历史记录来计算版本高度——浅克隆(
git clone --depth 1)会生成错误的版本。在 CI 中,使用actions/checkout并设置fetch-depth: 0以获取完整历史记录。 -
publicReleaseRefSpec模式是正则表达式,不是 glob——应使用^refs/heads/main$,而非main。缺少锚点会匹配到非预期的引用。 -
SemVer 预发布版本中,非数字段按字典序排序——由于按字母顺序比较,
alpha<beta<rc。数字段按整数比较,因此beta.2<beta.10(因为 2 < 10)。不要假定数字标识符按字典序排序。 -
不要对 NuGet 库使用 CalVer——NuGet 的版本解析依赖 SemVer 排序。像
2024.1.0这样的 CalVer 版本虽然在机制上可用,但不符合使用者对 API 稳定性信号的预期。 -
VersionPrefix+VersionSuffix会组合成Version——同时设置三者会导致冲突。应单独使用Version,或同时使用VersionPrefix/VersionSuffix,不要两者兼用。 -
发布前必须更新 Keep a Changelog 的
[Unreleased]章节——将条目从[Unreleased]移至新版本章节,更新比较链接,并添加一个新的空[Unreleased]章节。 -
nbgv prepare-release会同时修改新分支和当前分支——它会将当前分支的版本提升到下一个次要版本。应在你打算继续开发的分支上运行(通常是main)。