当前位置: 首页 > news >正文

使用 GitVersion 在编译或持续构建时自动使用语义版本号(Semantic Versioning)

我们在之前谈过 语义版本号(Semantic Versioning),在项目中应用语义版本号能够帮助库的开发者在发布包时表明更多的语义信息。这是趋势,从微软的博客 Versioning NuGet packages in a continuous delivery world 三部曲中可以看出,从 NuGet 4.3.0 以及 Visual Studio 2017 15.3 以上版本开始支持语义版本号 2.0 也能看出。

本文将从持续集成的角度来说语义版本号,告诉大家如何自动生成包含语义的版本号,并在发布库时采用。


安装 GitVersionTask

微软工程师在博客 Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog 中推荐的语义版本号生成工具是 GitVersion。从实际寻找来看,这似乎也是唯一一个能够让 NuGet 包支持语义版本号的工具。

去 NuGet.org 上为我们的库项目安装 GitVersionTask 即可开始我们的语义版本号。

请特别注意

  1. 目前只有 GitVersionTask 4.0 以上的版本(目前都是 beta)才支持 .NET Core 那样新格式的 csproj。
  2. 目前即便是最新测试版的 GitVersionTask 也不支持使用基于 .NET Core 的 dotnet build 编译,原因和解决方案我已经提交给 GitTools 团队了(详见:dotnet build command always fails with GitVersionTask 4.0.0-beta · Issue #1399 · GitTools/GitVersion),临时方案是使用 .NET Framework 版本的 msbuild

配置 GitVersion

特别吐槽一下 GitVersion 的官方文档,把功能堆积得很多很强大,却忽视了面向新手的入门教程。

GitVersion 的配置文件名为 GitVersion.yml,要求放到仓库的根目录下。官方文档对于配置文件的解释非常抽象,看完也不知道值应该写成什么样,也不知道每个值代表什么意义。于是我基本上是通过阅读它的源码来了解配置文件的实际含义的。

经过一番折腾,我把配置文件改成了下面这样。

next-version: 1.0
mode: ContinuousDelivery
increment: Inherit
tag-prefix: '[vV]'
source-branches: ['master', 'develop', 'hotfix']
ignore:
  sha: []
  commits-before: 2018-01-01T00:00:00
branches:
  master:
    regex: master$
    mode: ContinuousDelivery
    tag: ''
    increment: Patch
    prevent-increment-of-merged-branch-version: true
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: true
  release:
    regex: r(elease$|(eleases)?[-/])
    mode: ContinuousDelivery
    tag: beta
    increment: Patch
    prevent-increment-of-merged-branch-version: true
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: true
  feature:
    regex: f(eatures)?[-/]
    mode: ContinuousDeployment
    tag: alpha
    increment: Minor
    prevent-increment-of-merged-branch-version: false
    track-merge-target: false
    tracks-release-branches: false
    is-release-branch: false

▲ 别看这配置文件写得这么长,但其实官方的默认配置文件更长!

好了不开玩笑了,这配置文件分两部分来看:1. branches 之前;2. branches 之后。

写在 branches 之前的为全局配置,写在 branches 之后的是按分支分类的配置;它们的配置键值其实都是一样的。分支里的配置优先级高于全局配置。也就是说,如果编译打包的分支名能被 regex 正则表达式匹配上,那么就使用匹配的分支配置,否则使用全局配置。

举例,假设我们现在的版本库是这样的:

版本库

分支名称匹配 regex

那么当我们在 release 分支的 f 提交上编译,使用的配置将是 release 分支的配置。

由于我将 release 分支的正则表达式写成了 r(elease$|(eleases)?[-/])(注意,我们不需要加行首标记 ^,因为 GitVersionTask 里会为我们在最前面加一个),所以类似这样的分支名也是使用 release 分支的配置:

  • r/1.2.0
  • releases/1.2.0
  • release

但是,这样的分支名将采用默认的全局配置(因为不符合正则表达式):

  • r
  • releases

以上配置中我只列举了三组分支,但其实在 一个成功的 Git 分支流模型 中,还有 hotfix develop 这样更多的分支。如果你的项目足够大,建议自己参考其他分支写出这两个分支的配置出来。

预发布标签 tag

我们的 release 配置中,会为版本号加一个 beta 预发布标签,所以可能打出 2.0.0-beta 这样的包出来,或者 2.0.0-beta+3。但在全局配置下,默认打出的包会加一个以分支名命名的预发布标签;像这样 2.0.0-r(在 r 分支),或者 2.0.0-temp-walterlv-custombranch(在 temp/walterlv/custombranch 分支)。

继续看以上的配置,在 f/blogfeatures/new 分支上将采用 alpha 预发布标签。

我们在 master 分支的配置上

版本号递增规则 increment

increment 这一项的可选值有 MajorMinorPatchNoneInherit 五种。

  • Major 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag,那么现在将打出 2.0.0 的包来(无论此分支当前距离那个 Tag 有多少个提交,都只加 1)
  • Minor 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag,那么现在将打出 1.3.0 的包来(无论此分支当前距离那个 Tag 有多少个提交,都只加 1)
  • Patch 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag,那么现在将打出 1.2.1 的包来(无论此分支当前距离那个 Tag 有多少个提交,都只加 1)
  • None 如果此前在 Git 仓库此分支前有一个 1.2.0 的 Tag,那么现在将打出 1.2.0 的包来
  • Inherit 如果此分支上没有发现能够确认版本号的线索(例如一个 Tag),那么将自动寻找此分支的来源分支,继承来源分支的版本号递增规则。注意我在全局配置中加了一个 source-branches 配置,用于指定如果要自动寻找来源分支,请去这个集合中指定的分支名称里找。

下图是 release 分支上打包的版本号。

版本号递增的方式 mode

mode 可选的值有三种:

  • continuous-delivery 持续交付,临近产品发布时使用,详细信息可阅读Continous delivery - GitVersion
  • continuous-deployment 持续部署,日常使用,详细信息可阅读Continuous deployment - GitVersion
  • Mainline 传统的(官方文档没有说明,代码中没有注释,但阅读代码发现其策略是从上一个 Tag 递增版本号)

语义版本号使用教程

在了解了以上的配置之后,使用 GitVersionTask 才不会显得版本号的规则诡异。

我们从简单的使用开始,逐步向难演进。学习规则为:单个 master 分支 -> Git 分支流与预发布版本

单个 master 分支

如果我们只在 master 上开发,那么上手就非常容易了。

如果我们刚开始接触 GitVersionTask,那么我们在上一个发布包的提交上新建一个标签(Tag),命名为 v1.2.0,那么此标签之后的版本号打包将自动变为 1.2.1。Git 提交每次增多,那么构建号将加 1。下图中的版本号是 1.2.1+3。(注意:加号是语义版本号 2.0 的新特性,重申需要 NuGet 4.3.0 以及 Visual Studio 2017 15.3 以上版本。)

Git 分支流与预发布版本

当使用 Git 分支流时,版本号的递增方式其实与前面配置章节和单个 master 章节讲的时一致的。如下图。

但是,我们需要学习如何充分利用这样的分支流,以便让语义版本号充分发挥它的作用。

假设:我们最近发布了 1.1.0 正式版。

  1. 如果我们正在为库添加新功能,则新建一个 feature 分支,一直开发,直到认为开发完毕(功能实现完成,单元测试全绿)
  2. 如果此时有打包需求临时内测,则直接在 feature 分支打包,这样能打出 1.2.0-alpha 的包(后面的 + 取决于相对于此前发布多了多少个提交)
  3. 如果内测差不多了,则合并到 develop 分支确认这个内侧包
  4. 如果准备发布这个功能了,那么从 develop 分到 release 分支
  5. 这时如果有打包需求,则应该在打包之前新建一个标签(Tag)v1.2-beta,这样能打出 1.2beta 包(而不是 1.1 的)
  6. 如果在此 beta 的基础上出现持续打包,那么需要持续新建标签(因为自动新建的标签只会增加一次 Patch 号)
  7. 如果确认可正式发布,则 release 合并到 master,新建 v1.2 标签

参考资料

  • Versioning NuGet packages in a continuous delivery world: part 1 – Microsoft DevOps Blog
  • Versioning NuGet packages in a continuous delivery world: part 2 – Microsoft DevOps Blog
  • Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog
  • C#/.NET - How to generate and increase package version automatically especially via CI? - Stack Overflow
  • GitTools/GitVersion: Easy Semantic Versioning (http://semver.org) for projects using Git
  • GitVersion
  • Gitversion Task for VS2017-style csproj · Issue #1349 · GitTools/GitVersion
  • Change Assembly Version - Jenkins - Jenkins Wiki
  • Not working in .NET Core v2.0 project · Issue #15 · jeffkl/RoslynCodeTaskFactory
  • NuGet Gallery - RoslynCodeTaskFactory 1.2.1
  • dotnet build command always fails with GitVersionTask 4.0.0-beta · Issue #1399 · GitTools/GitVersion
  • .NET Core MSBuild cannot load tasks built against MSBuild 4.0 · Issue #2111 · Microsoft/msbuild
  • Should the SDK include Microsoft.Build.Utilities.v4.0? · Issue #1870 · dotnet/sdk

相关文章:

  • UWP 流畅设计中的光照效果(容易的 RevealBorderBrush 和不那么容易的 RevealBackgroundBrush)
  • 使用 Emit 生成 IL 代码
  • 如何快速编写和调试 Emit 生成 IL 的代码
  • 自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference
  • 冷算法:自动生成代码标识符(类名、方法名、变量名)
  • WPF/UWP 的 Grid 布局竟然有 Bug,还不止一个!了解 Grid 中那些未定义的布局规则
  • Git 更安全的强制推送,--force-with-lease
  • 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)
  • 理解 C# 项目 csproj 文件格式的本质和编译流程
  • 如何创建一个基于命令行工具的跨平台的 NuGet 工具包
  • 如何创建一个基于 MSBuild Task 的跨平台的 NuGet 工具包
  • 如何最快速地将旧的 NuGet 包 (2.x, packages.config) 升级成新的 NuGet 包 (4.x, PackageReference)
  • 每次都要重新编译?太慢!让跨平台的 MSBuild/dotnet build 的 Target 支持差量编译
  • C# 中那些可以被重载的操作符,以及使用它们的那些丧心病狂的语法糖
  • 神器如 dnSpy,无需源码也能修改 .NET 程序
  • 分享一款快速APP功能测试工具
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • iOS小技巧之UIImagePickerController实现头像选择
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • Linux快速复制或删除大量小文件
  • Netty+SpringBoot+FastDFS+Html5实现聊天App(六)
  • tensorflow学习笔记3——MNIST应用篇
  • vuex 学习笔记 01
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 前端之React实战:创建跨平台的项目架构
  • 微信小程序开发问题汇总
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 用mpvue开发微信小程序
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • Android开发者必备:推荐一款助力开发的开源APP
  • HanLP分词命名实体提取详解
  • postgresql行列转换函数
  • 带你开发类似Pokemon Go的AR游戏
  • 如何在招聘中考核.NET架构师
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​Java并发新构件之Exchanger
  • ​LeetCode解法汇总2583. 二叉树中的第 K 大层和
  • #每天一道面试题# 什么是MySQL的回表查询
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转)shell中括号的特殊用法 linux if多条件判断
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .net中调用windows performance记录性能信息
  • .NET中统一的存储过程调用方法(收藏)
  • .Net转Java自学之路—SpringMVC框架篇六(异常处理)
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • @zabbix数据库历史与趋势数据占用优化(mysql存储查询)
  • [ C++ ] STL---string类的使用指南