.NET发布管理 dotnet-release-management

这个技能专注于管理.NET项目的发布生命周期,涵盖NBGV(Nerdbank.GitVersioning)设置与配置、SemVer(语义化版本)策略、变更日志自动生成、预发布版本工作流以及发布分支模式。关键词:.NET, 发布管理, NBGV, SemVer, 变更日志, 预发布, 分支管理, 版本控制, DevOps, 软件开发。

DevOps 0 次安装 0 次浏览 更新于 3/6/2026

name: dotnet-release-management description: “管理.NET发布生命周期。NBGV版本控制、SemVer、变更日志、预发布、分支管理。” user-invocable: false

dotnet-release-management

.NET项目的发布生命周期管理:Nerdbank.GitVersioning(NBGV)设置与version.json配置、版本高度计算、公共发布与预发布模式;针对.NET库(何时提升主版本/次版本/修订版本,API兼容性考虑)和应用程序(构建元数据、部署版本控制)的SemVer 2.0策略;变更日志生成(保持变更日志格式,使用git-cliff和常规提交自动生成);预发布版本工作流(alpha、beta、rc、稳定版进展);以及发布分支模式(发布分支、热修复分支、基于主干的发布与标签)。

版本假设: .NET 8.0+ 基线。Nerdbank.GitVersioning 3.6+(当前稳定版)。SemVer 2.0 规范。

范围

  • Nerdbank.GitVersioning(NBGV)设置与版本高度
  • 针对库和应用程序的SemVer 2.0策略
  • 变更日志生成(保持变更日志格式,git-cliff)
  • 预发布版本工作流(alpha、beta、rc、稳定版)
  • 发布分支模式(发布分支、基于主干)

超出范围

  • CI/CD NuGet推送和部署工作流——参见[技能:dotnet-gha-publish]和[技能:dotnet-ado-publish]
  • GitHub Release创建和资产附件——参见[技能:dotnet-github-releases]
  • NuGet包元数据和签名——参见[技能:dotnet-nuget-authoring]
  • 项目级配置(SourceLink、CPM)——参见[技能:dotnet-project-structure]

交叉引用:[技能:dotnet-gha-publish]用于CI发布工作流,[技能:dotnet-ado-publish]用于ADO发布工作流,[技能:dotnet-nuget-authoring]用于NuGet包版本控制属性。


NBGV(Nerdbank.GitVersioning)

NBGV从git历史计算确定性版本号。版本源自version.json文件和git提交高度(自版本在version.json中设置以来的提交数),无需手动提升版本即为每个提交生成唯一版本。

安装

# 安装NBGV CLI工具
dotnet tool install --global nbgv

# 在仓库中初始化NBGV
nbgv install

# 这会在仓库根目录创建version.json

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 基础版本(主版本.次版本,可选修订版本) "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.jsonversion字段上次更改以来的提交数。该计数成为修订版本:

version.json: "version": "1.2"

提交历史:
  abc1234  功能:添加缓存          -> 1.2.3
  def5678  修复:空检查            -> 1.2.2
  ghi9012  杂务:更新依赖          -> 1.2.1
  jkl3456  提升版本到1.2          -> 1.2.0  (version.json在此更改)

版本高度确保每个提交都有唯一版本,无需手动干预。

预发布与公共发布

{
  "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命令

# 显示当前计算版本
nbgv get-version

# 显示特定版本属性
nbgv get-version -v NuGetPackageVersion
nbgv get-version -v SemVer2

# 准备发布(创建发布分支,提升版本)
nbgv prepare-release

# 为CI设置版本变量
nbgv cloud

单仓库NBGV配置

对于独立版本项目的单仓库,在每个项目目录放置version.json并使用inherit

仓库根目录/
  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拉取设置(如publicReleaseRefSpeccloudBuild),同时覆盖版本号。


.NET库的SemVer策略

何时提升版本

SemVer 2.0指定版本格式主版本.次版本.修订版本

变更类型 版本提升 示例
破坏性API变更 主版本 移除公共类型/成员,更改方法签名,重命名命名空间
新功能(向后兼容) 次版本 添加公共类型/成员,新扩展方法,新重载
错误修复(向后兼容) 修订版本 修复错误行为,性能改进,内部重构

.NET特定破坏性变更考虑

变更 破坏性? 备注
移除公共类型 是(主版本) 引用它的消费者将无法编译
移除公共方法 是(主版本) 直接调用者将失败
向公共方法添加必需参数 是(主版本) 现有调用者未提供它
向公共方法添加可选参数 否(次版本) 二进制兼容,但对使用命名参数的调用者源破坏
更改返回类型 是(主版本) 二进制和源破坏
添加新公共类型 否(次版本) 无现有代码受影响
添加新重载 否(次版本) 现有调用仍解析
更改内部实现 否(修订版本) 无公共API变更
更改可选参数的默认值 否(修订版本) 二进制兼容(值在重新编译时嵌入调用点)
密封先前未密封的类 是(主版本) 继承它的消费者将失败
使虚拟方法非虚拟 是(主版本) 覆盖它的消费者将失败

API兼容性验证

使用EnablePackageValidation捕获意外破坏性变更。完整包验证设置,参见[技能: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        构建号
1.2.3+abcdef          Git提交哈希
1.2.3+2024.01.15      构建日期
1.2.3-beta.1+42       预发布带构建元数据

构建元数据有助于将部署的二进制文件追踪回其源提交。NBGV自动附加git元数据。

部署版本控制

对于持续部署的服务,版本标记有助于故障排除:

<PropertyGroup>
  <!-- 在程序集中嵌入完整版本,供运行时内省 -->
  <InformationalVersion>1.2.3+abcdef.2024-01-15</InformationalVersion>
</PropertyGroup>

在运行时读取:

var version = typeof(Program).Assembly
    .GetCustomAttribute<System.Reflection.AssemblyInformationalVersionAttribute>()
    ?.InformationalVersion;
// 返回 "1.2.3+abcdef.2024-01-15"

变更日志生成

保持变更日志格式

保持变更日志格式是广泛采用的标准:

# 变更日志

本项目所有显著变更将记录在此文件中。

格式基于[保持变更日志](https://keepachangelog.com/en/1.1.0/),
本项目遵循[语义化版本控制](https://semver.org/spec/v2.0.0.html)。

## [未发布]

### 添加
- 小部件缓存支持以提高吞吐量

## [1.2.0] - 2024-03-15

### 添加
- 小部件配置的流畅API
- 批处理支持

### 更改
- 改进无效小部件状态的错误消息

### 修复
- 高并发下小部件池中的内存泄漏
- 计划小部件操作中的时区处理

### 弃用
- `Widget.Create()`静态方法——改用`WidgetBuilder`

## [1.1.0] - 2024-01-10

### 添加
- 小部件序列化支持

[未发布]: 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 错误修复
Security 漏洞修复

使用git-cliff自动生成

git-cliff从常规提交生成变更日志:

# 安装git-cliff
cargo install git-cliff

# 为所有版本生成变更日志
git cliff --output CHANGELOG.md

# 仅生成未发布变更的变更日志
git cliff --unreleased --output CHANGELOG.md

# 为特定标签范围生成笔记
git cliff --tag v1.2.0 --unreleased

为.NET常规提交模式配置cliff.toml

# cliff.toml
[changelog]
header = """
# 变更日志

本项目所有显著变更将记录在此文件中。

"""
body = """
{% if version %}\
    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
    ## [未发布]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
    ### {{ group | upper_first }}
    {% for commit in commits %}
        - {{ commit.message | upper_first }}\
    {% endfor %}
{% endfor %}

"""
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: 添加小部件缓存支持
fix: 修正调度器中的时区处理
feat!: 重命名Widget.Create()为WidgetBuilder.Build()
chore(deps): 更新System.Text.Json到8.0.5
docs: 更新缓存的API参考

主体中的破坏性变更:
feat: 重新设计小部件API

破坏性变更:Widget.Create()已被移除。改用WidgetBuilder。
前缀 SemVer影响 变更日志部分
feat: 次版本 添加
fix: 修订版本 修复
feat!:破坏性变更: 主版本 破坏性变更
perf: 修订版本 更改
refactor: 修订版本 更改
docs: 文档
chore: (跳过)

预发布版本工作流

标准预发布进展

alpha -> beta -> rc -> 稳定版

1.0.0-alpha.1  早期开发,API不稳定
1.0.0-alpha.2  持续alpha迭代
1.0.0-beta.1   功能完整,API稳定化
1.0.0-beta.2   Beta错误修复
1.0.0-rc.1     发布候选,最终验证
1.0.0-rc.2     RC错误修复(如果需要)
1.0.0          稳定版发布

NBGV预发布工作流

# 从version.json中的预发布后缀开始
# version.json: { "version": "1.0-alpha" }
# 生成:1.0.1-alpha, 1.0.2-alpha, ...

# 提升到beta
# 编辑version.json: { "version": "1.0-beta" }
# 生成:1.0.1-beta, 1.0.2-beta, ...

# 提升到rc
# 编辑version.json: { "version": "1.0-rc" }
# 生成:1.0.1-rc, 1.0.2-rc, ...

# 提升到稳定版
# 编辑version.json: { "version": "1.0" }
# 生成:1.0.1, 1.0.2, ...

手动预发布工作流

对于不使用NBGV的项目:

<!-- 在.csproj或Directory.Build.props中 -->
<PropertyGroup>
  <VersionPrefix>1.0.0</VersionPrefix>
  <VersionSuffix>beta.1</VersionSuffix>
  <!-- 生成:1.0.0-beta.1 -->
</PropertyGroup>

从CI覆盖:

# CI设置预发布后缀
dotnet pack /p:VersionSuffix="beta.$(BUILD_NUMBER)"

# 稳定版发布:省略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
# 为发布标记并推送
git tag -a v1.0.0 -m "发布 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
# 创建发布分支
git checkout -b release/1.0 main

# 在发布分支上稳定化(仅错误修复)
git commit -m "修复:小部件池中的空检查"

# 标记并发布
git tag -a v1.0.0 -m "发布 v1.0.0"
git push origin release/1.0 v1.0.0

# 将修复合并回main
git checkout main
git merge release/1.0

最适合: 有支持合同的产品、LTS版本、需要并行开发和稳定化的团队。

NBGV prepare-release

NBGV自动化发布分支创建和版本提升:

# 创建release/v1.0分支,将main提升到1.1-alpha
nbgv prepare-release

# 这做什么:
# 1. 从当前提交创建分支"release/v1.0"
# 2. 在发布分支上:移除预发布后缀(version: "1.0")
# 3. 在main上:提升到"1.1-alpha"(下一个开发版本)

热修复分支

针对已发布版本的紧急修复:

main:         A -- B -- C -- D -- E
                         \
release/1.0:              C' -- v1.0.0
                                  \
hotfix/1.0.1:                      F' -- v1.0.1
# 从发布标签分支
git checkout -b hotfix/1.0.1 v1.0.0

# 修复关键问题
git commit -m "修复:认证处理器中的关键安全漏洞"

# 标记并发布热修复
git tag -a v1.0.1 -m "热修复 v1.0.1"
git push origin hotfix/1.0.1 v1.0.1

# 将热修复合并回main
git checkout main
git merge hotfix/1.0.1

分支模式比较

模式 发布节奏 并行版本 复杂性
主干 + 标签 持续
发布分支 计划 中等
GitFlow(完整) 计划

对于大多数.NET开源库,基于主干的标签和NBGV已足够。为同时维护多个支持版本的产品保留发布分支。


代理注意事项

  1. NBGV version.json仅使用主版本.次版本(非主版本.次版本.修订版本)——修订版本从提交高度计算。设置"version": "1.2.3"将修订版本固定为3,违背自动版本控制的目的。

  2. NBGV需要git历史来计算版本高度——浅克隆(git clone --depth 1)产生错误版本。在CI中,使用fetch-depth: 0actions/checkout获取完整历史。

  3. publicReleaseRefSpec模式是正则表达式,非通配符——使用^refs/heads/main$而非main。缺少锚点将匹配意外引用。

  4. SemVer预发布排序中,非数字段按词典顺序——alpha < beta < rc因为字母比较。数字段作为整数比较,所以beta.2 < beta.10(因为2 < 10)。不要假设数字标识符的词典顺序。

  5. 不要对NuGet库使用CalVer——NuGet解析依赖于SemVer排序。像2024.1.0这样的CalVer版本机械工作但违反消费者对API稳定性信号的期望。

  6. VersionPrefix + VersionSuffix组合形成Version——同时设置所有三个会导致冲突。单独使用Version或一起使用VersionPrefix/VersionSuffix,而非两者。

  7. 保持变更日志[未发布]部分必须在发布前更新——将条目从[未发布]移动到新版本部分,更新比较链接,并添加新的空[未发布]部分。

  8. nbgv prepare-release修改新分支和当前分支——它将当前分支上的版本提升到下一个次版本。从要继续开发的分支(通常main)运行它。