我们在之前谈过 语义版本号(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 标签

参考资料

使用 GitVersion 在编译或持续构建时自动使用语义版本号(Semantic Versioning)的更多相关文章

  1. 使用DaoCloud持续构建docker镜像,自动化部署

    我们学会了在主机上安装部署docker,也学会了构建自己的docker镜像和容器,启停也都会用了,下一步就需要持续构建发布docker的技能了. 我们希望能在代码提交后,有个远程服务能自动开始构建项目 ...

  2. 正确理解java编译时,运行时以及构建时这三个概念

    Java中的许多对象(一般都是具有父子类关系的父类对象)在运行时都会出现两种类型:编译时类型和运行时类型,例如:Person person = new Student();这行代码将会生成一个pers ...

  3. 获取hudson持续构建编译结果的一种方法

    作者:朱金灿 来源:http://blog.csdn.net/clever101 很多时候使用hudson结合VisualStudio进行持续构建后需要获取持续构建的编译结果,通过编译结果来知道哪些项 ...

  4. 个人环境搭建——搭建jenkins持续构建集成环境

    ---恢复内容开始--- 搭建jenkins持续构建集成环境  要搭建jenkins持续构建集成环境,首先要安装tomcat和JDK:   第一部分,基本说明:   敏捷(Agile) 在软件工程领域 ...

  5. Jenkins持续构建打包后端服务流程详解

    背景运用场景及思路 1.为响应后端开发人员需求,提升项目开发过程效率,选择Jenkins持续构建,进行导包启动一键持续集成 思路: 使用jenkins自带,立即构建->SVN拉取代码,通过Jen ...

  6. 【转】获取Jenkins构建时Git Change Log

    原文:https://www.jianshu.com/p/513ab6915dbd 在基于Jenkins进行CI持续集成的工作,在构建后上传蒲公英时想将本次版本的git commit信息同步到蒲公英的 ...

  7. 使用MSBUILD 构建时出错 error MSB3086: Task could not find "sgen.exe" using the SdkToolsPath的解决方法

    如果项目有添加有WB引用,比如引用其它网站的WEB服务等,那么VS在编译时会自动生成个 [项目名称].Serializers.dll的文件,就是把引用服务中的相关对象信息生成硬编码的程序集,以提高效率 ...

  8. [原]项目进阶 之 持续构建环境搭建(四)Jenkins环境搭建

    在之前的几篇文章中,我给大家分别介绍了这次的持续化构建环境搭建的相关前提内容.如果说前面的文章都是小菜的话,那么今天的这篇文章就是我们这个系列文章的主菜. 1.前提 安装jenkins需要安装JDK. ...

  9. [原]项目进阶 之 持续构建环境搭建(三)Maven环境搭建

    上次的博文项目进阶 之 持续构建环境搭建(二)Nexus私服器中,我们搭建了一个Nexus的maven私服,这次我们来重点讲解一下Maven的安装和配置.这里说明一下这次的环境搭建,比较基础,但却非常 ...

随机推荐

  1. java 枚举的写法

    1.第一种形式: public interface PrealertConstants { enum platformCodeEnum{ CAINIAO(1), CLOSED(-5), OFFLINE ...

  2. RNAseq 流程

    https://github.com/twbattaglia/RNAseq-workflow

  3. vector的坑——C++primer练习6.33总结

    说来惭愧,一道简单的对vector递归的题目写了一个多小时,最后还是请教了大神才改出来. 首先贴上原代码: void return_vector(vector<int>::iterator ...

  4. PHP libevent函数基本介绍

    3.2   主要函数介绍 按照使用libevnet库顺序,看一下相关函数做什么操作. 3.2.1  event_init 调用event_base_new,初始化struct event_base对象 ...

  5. python3安装tensorflow遇到的问题

    1. 使用命令:sudo pip3 install --upgrade \ https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow ...

  6. python ssh登录

    3. 编写linkssh.py #!/usr/bin/env python# -*- coding: utf-8 -*-# filename: pexpect_test.py'''Created on ...

  7. Hive 表结构操作

    添加列  add columns alter table table_name add columns (id int comment '主键ID' ) ; 默认在表所有字段之后,分区字段之前. 替换 ...

  8. Python读取指定文件夹(包括当前目录、子目录、子文件)

    http://blog.csdn.net/lsq2902101015/article/details/51305825

  9. docker远程仓库镜像推送到本地仓库

    #!/bin/bashimageid=(`docker images |grep -v REPOSITORY|awk '{print $3}'`)image=(`docker images |grep ...

  10. 19.并发容器之BlockingQueue

    1. BlockingQueue简介 在实际编程中,会经常使用到JDK中Collection集合框架中的各种容器类如实现List,Map,Queue接口的容器类,但是这些容器类基本上不是线程安全的,除 ...