使用 GitVersion 在編譯或持續構建時自動使用語義版本號(Semantic Versioning)
發佈於 2018-04-12 13:45 更新於 2018-09-01 00:11
我們在之前談過 語義版本號(Semantic Versioning),在項目中應用語義版本號能夠幫助庫的開發者在發佈包時表明更多的語義信息。這是趨勢,從微軟的博客 Versioning NuGet packages in a continuous delivery world 三部曲中可以看出,從 NuGet 4.3.0 以及 Visual Studio 2017 15.3 以上版本開始支持語義版本號 2.0 也能看出。
本文將從持續集成的角度來說語義版本號,告訴大家如何自動生成包含語義的版本號,並在發佈庫時採用。
This post is written in multiple languages. Please select yours:
中文 English
安裝 GitVersionTask
微軟工程師在博客 Versioning NuGet packages in a continuous delivery world: part 3 – Microsoft DevOps Blog 中推薦的語義版本號生成工具是 GitVersion。從實際尋找來看,這似乎也是唯一一個能夠讓 NuGet 包支持語義版本號的工具。
去 NuGet.org 上爲我們的庫項目安裝 GitVersionTask 即可開始我們的語義版本號。
請特別注意:
- 目前只有 GitVersionTask 4.0 以上的版本(目前都是 beta)才支持 .NET Core 那樣新格式的 csproj。
- 目前即便是最新測試版的 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/blog
或 features/new
分支上將採用 alpha
預發佈標籤。
我們在 master
分支的配置上
版本號遞增規則 increment
increment
這一項的可選值有 Major
、Minor
、Patch
、None
和 Inherit
五種。
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 - GitVersioncontinuous-deployment
持續部署,日常使用,詳細信息可閱讀Continuous deployment - GitVersionMainline
傳統的(官方文檔沒有說明,代碼中沒有註釋,但閱讀代碼發現其策略是從上一個 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 正式版。
- 如果我們正在爲庫添加新功能,則新建一個
feature
分支,一直開發,直到認爲開發完畢(功能實現完成,單元測試全綠) - 如果此時有打包需求臨時內測,則直接在
feature
分支打包,這樣能打出1.2.0-alpha
的包(後面的 + 取決於相對於此前發佈多了多少個提交) - 如果內測差不多了,則合併到
develop
分支確認這個內側包 - 如果準備發佈這個功能了,那麼從
develop
分到release
分支 - 這時如果有打包需求,則應該在打包之前新建一個標籤(Tag)
v1.2-beta
,這樣能打出1.2
的beta
包(而不是1.1
的) - 如果在此
beta
的基礎上出現持續打包,那麼需要持續新建標籤(因爲自動新建的標籤只會增加一次 Patch 號) - 如果確認可正式發佈,則
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
本文會經常更新,請閱讀原文: https://walterlv.com/post/automatically-semantic-versioning-using-git-version-task.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。
本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名 呂毅 (包含鏈接: https://walterlv.com ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請 與我聯繫 ([email protected]) 。