Jenkins持續部署-自動生成版本號
目錄
Jenkins持續集成學習-Windows環境進行.Net開發1
Jenkins持續集成學習-Windows環境進行.Net開發2
Jenkins持續集成學習-Windows環境進行.Net開發3
Jenkins持續集成學習-Windows環境進行.Net開發4
Jenkins持續集成學習-搭建jenkins問題匯總
Jenkins持續部署-Windows環境持續部署探究1
Jenkins持續部署-自動生成版本號
前言
在上一篇之前的文章開始對Windows環境下持續部署的方案進行學習與研究。上一篇文章主要介紹關於持續部署需要的一些技術方案的實現,在本篇文章開始對持續部署的一些細節實現展開討論。
本篇文章先對版本號的自動更新流程進行梳理和說明。后續需要通過版本號比較創建差量更新包。
目的
本章文章主要是通過調用svn客戶端命令和powershell腳本實現完全無需人工干預自動生成版本號。
詳細流程
若程序需要定義版本號,則可以將版本號記錄在程序集的AssemblyInfo.cs
文件中
[assembly: AssemblyVersion("1.0.0")]
[assembly: AssemblyFileVersion("1.0.0")]
也可以在程序集右鍵選擇屬性(或者通過快捷鍵Alt + Enter),在Application
點擊Assembly Infomation...
按鈕修改程序集版本號和文件版本號。
AssemblyVersion
是程序集的版本,.NET的CLR用於標識出該dll的版本信息,用於定義強名稱的版本號,該版本號每一位最大為16位長度,即最大為65535,超過時編譯不通過。
AssemblyFileVersion
是文件版本號,僅僅是文件版本號,給人看的,沒有實際什么作用,也沒有長度限制。
獲取SVN Reversion
我們規定程序的版本號為需求版本號1.0.0加上SVN的Reversion做為修訂號。這樣就能直接關聯上該程序集是哪個版本的代碼。
關於修訂號,在《TortoiseSVN》文檔中有相關的說明。我看的是《TortoiseSVN 1.8.10》的文檔,在第五章介紹了SubWCRev程序。通過SubWCRev程序可以執行關鍵字$WCREV$
替換。同時我們需要提供一個版本號模板文件,通過替換版本號模板文件的關鍵字生成我們需要的版本號文件。
首先我們根據程序集下AssemblyInfo.cs
文件復制出一個AssemblyInfo.template.cs
文件。
由於我們僅僅是為了修改版本號信息,后面就稱之為版本號模板文件。
然后將其[assembly: AssemblyFileVersion("1.0.0.0")]
修改為[assembly: AssemblyFileVersion("1.0.0.$WCREV$")]
。這樣我們就可以通過SubWCRev
程序替換修訂號。
由於
AssemblyVersion
有大小限制,不允許超過65535,而SVN修訂號很有可能會超過該值,因此CLR的程序集版本號不用改修訂號。只需要修改文件版本號即可。
由於在編譯時,VS會編譯AssemblyInfo
文件提取出程序集信息放入到程序集內。我們直接復制出來的版本號模板文件默認也會進行編譯。而我們創建的版本號模板文件用於生成版本號文件,無需編譯。我們需要的是通過版本號模板文件生成版本號文件,即通過AssemblyInfo.template.cs
生成AssemblyInfo.cs
。因此在版本號模板文件右鍵屬性中將Build Action
從Compile
修改為None
。
此時我們已經有了版本號模板文件,接下來要做的是在編譯的之前先根據版本號模板文件創建我們需要的版本號文件。
VS編譯的時候提供了編譯前預處理功能和編譯后處理功能。在程序集屬性中,我們選擇Build Event
里面有Pre-build event command line
,通過在里面輸入指令可以實現在編譯前執行我們想要的命令。
同時VS內部也提供了一些宏指令供我們使用,通過點擊Edit Pre-build
按鈕,會彈出一個編輯框
點擊Macros
可以查看所有VS支持的宏指令
SubWCRev
程序命令格式為SubWCRev WorkingCopyPath [SrcVersionFile DstVersionFile] [-nmdfe]
,WorkingCopyPath
為SVN的工作副本,SrcVersionFile
為原始版本文件,即版本模板文件。DstVersionFile
為替換關鍵子后保存的版本文件。
在VS環境變量中我們可以通過$(ProjectDir)
獲取到當前程序集路徑,通過$(SolutionDir)
獲取到解決方案路徑。
宏指令為
$(指令名)
格式
在預編譯事件中輸入以下指令SubWCRev $(SolutionDir) $(ProjectDir)Properties\AssemblyInfo.template.cs $(ProjectDir)Properties\AssemblyInfo.cs
即可在編譯前獲取到SVN的reversion填充到修訂號中。
編譯后可以在輸出窗口看到關鍵字替換的信息
1>------ Build started: Project: FGMain, Configuration: Debug Any CPU ------
1> SubWCRev: 'F:\工作\SVN\Platform\trunk\FGMain\FGMain\'
1> Last committed at revision 100268
1> Mixed revision range 100267:100268
1> Local modifications found
1> Unversioned items found
獲取需求號
在實際工作中,我們每次發版都會有一個需求版本號。當產生需求時整個版本都會使用這個版本號。因此我們可以在開發的時候就在開發分支上創建該版本號的需求分支。分支名稱以版本號命名,這樣程序就可以獲取到URL的版本號信息填充到版本號模板號模板文件中。而省去了人為修改版本號的麻煩。
比如當前版本號為1.32.0,則在SVN程序的分支上創建一個1.32.0的版本。branches/FGMain/1.32.0
。
接下來在我們使用SubWCRev
程序關鍵字替換之前需要先獲取到分支的版本號填充到版本號模板文件中。這樣在編譯前就會將版本號和SVN的修訂號一同生成。
我們還需要提前判斷當前SVN工作目錄是否有修改,只有在工作目錄有修改時,才需要更新版本號,工作目錄沒有修改時,則無需修改版本號。
當我們安裝了SVN客戶端后(同時需要選擇安裝命令行工具),我們可以通過SVN
執行執行命令,通過SVN help
查看支持的所有參數。
SVN客戶端安裝時需要勾選命令行工具,如下截圖
獲取版本號
我們需要獲取url的版本號。而版本號只有在分支目錄上才有,因此我們可以通過正則解析以下url,提取版本號。若提取不到則無需執行后續邏輯
通過svn info
獲取當前目錄的svn信息,通過svn info 路徑
獲取指定路徑的svn信息。
F:\工作\SVN\Platform\trunk\FGMain>svn info
Path: .
Working Copy Root Path: F:\工作\SVN\Platform\trunk\FGMain
URL: http://inner.svn.com:81/ATS_Code/Platform/branches/FGMain/1.32.0
Relative URL: ^/Platform/branches/FGMain/1.32.0
Repository Root: http://inner.svn.com:81/ATS_Code
Repository UUID: 2fd9d0ce-2897-f849-b9e2-af1303b08de7
Revision: 99512
Node Kind: directory
Schedule: normal
Last Changed Author: wish
Last Changed Rev: 99512
Last Changed Date: 2019-06-14 17:54:47 +0800 (周五, 14 6月 2019)
命令會返回多行信息,我使用的時SVN 1.11 版本的客戶端,其他版本可能會有不同。我們解析第二行的URL從而解析出URL的版本號。
$svnInfo = svn info $projectDir
$urlInfo = $svnInfo[2]
$url = $urlInfo.Replace("URL: ","");
$urlMatchStr= 'branches/(.*?)/(.*?)/(.*?)'
if($url -notmatch $urlMatchStr)
{
# 主線不再處理
Write-Host "$url not match $urlMatchStr"
return
}
這里需要注意由於我們當前目錄不一定就是解決方案目錄,在VS中我們實在解決方案調用的編譯工作,但是在jenkins我們的目錄可能會是bin/release
或bin/debug
,因此匹配URL時需要用非貪婪匹配。這樣無論路徑為branches/FGMain/1.32.0/FGBussness
還是branches/FGMain/1.32.0/FGMain/bin/Debug
第二項都可以匹配到版本號。
現在同$matches[2]
即可獲取到我們獲取到的版本號。
獲取當前工作副本狀態
當獲取到版本號時,表明當前實在分支目錄,則需要判斷工作副本是否有修改。有修改則需要更新版本號。通過svn status
查看路徑的svn狀態,通過svn status 路徑
可以查看指定路徑的SVN狀態。
PS F:\工作\SVN\Platform\trunk\FGMain> svn status FGBussness
? FGBussness\FGClientBussness.csproj.user
M FGBussness\MainWorkServer.cs
? FGBussness\app.config
? FGBussness\bin
命令返回了一個集合,每一行是一個文件或文件夾的SVN狀態。SVN共包含以下狀態
- " ": 無修改
- "A": 新增
- "C": 沖突
- "D": 刪除
- "G": 合並
- "I": 忽略
- "M": 改變
- "R": 替換
- "X": 未納入版本控制,但被外部定義所用
- "?": 未納入版本控制
- "!": 該項目已遺失 (被非 svn 命令所刪除) 或是不完整
- "~": 版本控制下的項目與其它類型的項目重名
- "L": 鎖定
- "S": 已切換
- "K": 存在鎖定標記
可以看到" "、"X"、"?"可以認為是本地無修改。其他狀態都有修改,需要更新版本號。當有沖突時,編譯也會出錯,同時編輯完沖突有可能就沒有修改了,因此狀態為"C"時也認為時無修改。
$svnStatuses = svn status $projectDir
#遍歷每個文件狀態
foreach($svnStatus in $svnStatuses)
{
$status = $svnStatus.SubString(0,1)
if(($status -ne " ") -and ($status -ne "X") -and ($status -ne "?") -and ($status -ne "C"))
{
#存在編輯
Write-Host $svnStatus.SubString(1).Trim()"Modified"
$modified = $true
break
}
}
通過$modified
記錄當前工作副本的是否修改。同時只要一個文件修改了就無需判斷其他文件。
更新版本號模板
接下來我們讀取版本號模板文件,首先我們需要確認一下VS保存的文件編碼,我們按照VS的編碼讀取並保存文件。
在文件
-高級保存選項
中可以看到設置的文本編碼
$versionContent = Get-Content $versionFile -encoding UTF8
for($count = 0 ; $count -lt $versionContent.Length; $count++)
{
if(($versionContent[$count] -match '\[assembly: AssemblyVersion\(\"(\d*
\.\d*\.\d*)\"\)\]') -or
($versionContent[$count] -match '\[assembly: AssemblyFileVersion\(\"(\d*\.\d*\.\d*)\.\$WCREV\$\"\)\]'))
{
#版本號不一致則更新版本號
if($matches[1] -ne $marjorVersion)
{
Write-Host "Change Version"$matches[1]"To $marjorVersion"
$versionContent[$count] = $versionContent[$count] -replace $matches[1],$marjorVersion
}
continue
}
}
\d*\.\d*\.\d*
匹配3位版本號,如1.32.0
遍歷文件的每一行進行匹配,若匹配上了則將匹配的版本號替換為新的版本號。
最后更新版本號模板文件
Set-Content $versionContent -Path $versionFile -encoding UTF8
同時由於我們程序只能獲取一個程序集當作整個程序的版本號,因此我們每次編譯的時候可以將啟動項強制更新版本號。我們可以添加一個$force
當設置為true
的時候不管本地是否有修改都更新版本號。
完整的腳本如下:
param([string] $projectDir,[string]$versionFile, $force)
Write-Host "current path:"$projectDir
try
{
# 指定路徑
$svnInfo = svn info $projectDir
$urlInfo = $svnInfo[2]
$url = $urlInfo.Replace("URL: ","");
Write-Host "url:$url"
$urlMatchStr= 'branches/(.*?)/(.*?)/(.*?)'
if($url -notmatch $urlMatchStr)
{
# 主線不再處理
Write-Host "$url not match $urlMatchStr"
return
}
# 分支
# PS F:\工作\SVN\Platform\trunk\FGMain> $matches
# Name Value
# ---- -----
# 3 FGBussness
# 2 1.32.0
# 1 FGMain
# 0 branches/FGMain/1.32.0/FGBussness
$marjorVersion = $matches[2]
Write-Host "Current Working Copy Version:$marjorVersion"
# 沒有強制修改,則需要判斷當前工作路徑是否編輯過。
$modified = $force
if($modified)
{
Write-Host "Force Modified"
}
else
{
#當路徑含有中文時,參數傳入會亂碼。暫時獲取當前路徑狀態
$svnStatuses = svn status $projectDir
#遍歷每個文件狀態
foreach($svnStatus in $svnStatuses)
{
$status = $svnStatus.SubString(0,1)
if(($status -ne "X") -and ($status -ne "?"))
{
#存在編輯
Write-Host $svnStatus.SubString(1).Trim()"Modified"
$modified = $true
break
}
}
}
# 若當前工作目錄沒有修改過的文件則無需修改版本號
# 查找模板文件的路徑
if($modified)
{
Write-Host "Version File :$versionFile"
$versionContent = Get-Content $versionFile -encoding UTF8
for($count = 0 ; $count -lt $versionContent.Length; $count++)
{
if(($versionContent[$count] -match '\[assembly: AssemblyVersion\(\"(\d*\.\d*\.\d*)\"\)\]') -or
($versionContent[$count] -match '\[assembly: AssemblyFileVersion\(\"(\d*\.\d*\.\d*)\.\$WCREV\$\"\)\]'))
{
#版本號不一致則更新版本號
if($matches[1] -ne $marjorVersion)
{
Write-Host "Change Version"$matches[1]"To $marjorVersion"
$versionContent[$count] = $versionContent[$count] -replace $matches[1],$marjorVersion
}
continue
}
}
# 編輯過則將模板的版本號替換掉
# 在VS的菜單-文件-高級保存選項中默認的文件編碼是使用UTF8 With BOM的格式
Set-Content $versionContent -Path $versionFile -encoding UTF8
}
else
{
Write-Host "No Modified"
}
}
catch
{
$Error
}
設置編譯前讀取版本號
腳本編寫好,我們將腳本放到項目根目錄下,這樣所有的程序集都能通過解決文件夾獲取到該腳本。
powershell -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(SolutionDir)Update-Version.ps1 $(ProjectDir) $(ProjectDir)Properties\AssemblyInfo.template.cs
在Pre-build event command line
添加以上命令調用更新版本號的腳本。
-ExecutionPolicy Bypass
表示允許該腳本執行,否則可能沒有權限執行本地腳本文件。-NoProfile
表示不加載powershell的配置文件。默認會加powershell所有的配置文件。-NonInteractive
表示不向用戶顯示交互式提示。
現在完整的Pre-build
命令如下
powershell -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(SolutionDir)Update-Version.ps1 $(ProjectDir) $(ProjectDir)Properties\AssemblyInfo.template.cs
SubWCRev $(SolutionDir) $(ProjectDir)Properties\AssemblyInfo.template.cs $(ProjectDir)Properties\AssemblyInfo.cs
若啟動項默認需要強制更新版本號,則使用以下命令
powershell -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(SolutionDir)Update-Version.ps1 $(ProjectDir) $(ProjectDir) $true
若當前版本文件的版本號為1.31.0,在1.32.0的分支上進行編譯,則會在輸出窗口輸出以下日志
1>------ Build started: Project: FGMain, Configuration: Debug Any CPU ------
1> current path: F:\工作\SVN\Platform\trunk\FGMain\FGMain\
1> url:http://124.160.27.118:81/ATS_Code/Platform/branches/FGMain/1.32.0/FGMain
1> Current Working Copy Version:1.32.0
1> Force Modified
1> Version File :F:\工作\SVN\Platform\trunk\FGMain\FGMain\Properties\AssemblyInfo.template.cs
1> Change Version 1.31.0 To 1.32.0
1> Change Version 1.31.0 To 1.32.0
...
總結
在腳本編寫的時候遇到了以下錯誤
-
我們可以在傳入參數設置
$force
為bool類型,但是在外部調用powershell腳本傳參傳入bool類型會報以下錯誤
無法處理對參數“force”的參數轉換。無法將值“System.String”轉換為類型“System.Boolean”。布爾參數僅接受布爾值和數字,例如 $True、$False、1 或 0。
但是通過提示的傳入值仍然會報錯,因此我們只能將[bool]
顯示的類型去掉,避免強制轉換時出現錯誤。 -
外部傳入路徑含有中文會導致powershell由於亂碼處理不了
參考文獻
- 給PowerShell腳本傳遞一個布爾值
- Using PowerShell in post/pre build action in Visual Studio
- How to pass boolean values to a PowerShell script from a command prompt
- Always Use -NoProfile To Launch Scripts
微信掃一掃二維碼關注訂閱號傑哥技術分享
本文地址:https://www.cnblogs.com/Jack-Blog/p/11108136.html
作者博客:傑哥很忙
歡迎轉載,請在明顯位置給出出處及鏈接