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 ActionCompile修改为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/releasebin/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
...

总结

在脚本编写的时候遇到了以下错误

  1. 我们可以在传入参数设置$force为bool类型,但是在外部调用powershell脚本传参传入bool类型会报以下错误

    无法处理对参数“force”的参数转换。无法将值“System.String”转换为类型“System.Boolean”。布尔参数仅接受布尔值和数字,例如 $True、$False、1 或 0。

    但是通过提示的传入值仍然会报错,因此我们只能将[bool]显示的类型去掉,避免强制转换时出现错误。

  2. 外部传入路径含有中文会导致powershell由于乱码处理不了

参考文献

  1. 给PowerShell脚本传递一个布尔值
  2. Using PowerShell in post/pre build action in Visual Studio
  3. How to pass boolean values to a PowerShell script from a command prompt
  4. Always Use -NoProfile To Launch Scripts



微信扫一扫二维码关注订阅号杰哥技术分享

本文地址:https://www.cnblogs.com/Jack-Blog/p/11108136.html

作者博客:杰哥很忙

欢迎转载,请在明显位置给出出处及链接

Jenkins持续部署-自动生成版本号的更多相关文章

  1. Jenkins持续部署-创建差量更新包

    目录 Jenkins持续部署-创建差量更新包 目录 前言 目的 详细流程 生成版本号 获取版本号 创建文件更新清单 压缩 获取上个版本的包 创建差量更新包 读取服务器Json配置 远程创建文件夹目录 ...

  2. Jenkins持续部署-Windows环境持续部署探究1

    目录 Jenkins持续部署-Windows环境持续部署探究1 目录 前言 目的 方案流程 技术实现 PowerShell FTP上传插件 环境变量插件 脚本执行 远程调用 升级服务 启动服务 总结 ...

  3. Jenkins持续部署

    Jenkins持续部署 Jenkins提供很好的连续部署和交付的支持.看一下部署任何软件开发的流程,将如下图所示. 连续部署的主要部分,是确保其上面所示的整个过程是自动化的.Jenkins实现所有这些 ...

  4. 最好的 Xcode 自动生成版本号技术

    在 bloglovin ,我们使用自动生成版本号来设置Xcode,使当前的版本号为在Git活跃的分支上 的提交数.它一直正常工作着,但我们的技术也不是一帆风顺的. 糟糕的老方法 我们使用的技术是来自一 ...

  5. 2018-8-10-VisualStudio-2017-项目格式-自动生成版本号

    title author date CreateTime categories VisualStudio 2017 项目格式 自动生成版本号 lindexi 2018-08-10 19:16:52 + ...

  6. 使用私有gitlab发布自动生成版本号和标签(version和tag)(骚)

    设置 semantic ,自动生成版本号和标签 FROM node:14-buster-slim LABEL maintainer="wangyunpeng" COPY sourc ...

  7. Docker + Jenkins 持续部署 ASP.NET Core 项目

    Docker 是个好东西,特别是用它来部署 ASP.NET Core Web 项目的时候,但是仅仅的让程序运行起来远远不能满足我的需求,如果能够像 DaoCloud 提供的持续集成服务那样,检测 gi ...

  8. JAVA项目如何通过Docker实现Jenkins持续部署

    原文地址:http://blog.51cto.com/dadonggg/1957691 本篇实操性的案例讲解——JAVA项目如何通过Docker实现持续部署(只需简单四步), 即:开发通过git pu ...

  9. [转载].net程序集自动生成版本号

    原文:http://hi.baidu.com/bcbgrand/item/a74a7ba71c3b0ea928ce9dce .net程序版本号的格式是4个十进制数字 比如 2.5.729.2 依次是 ...

随机推荐

  1. Android 在子线程中更新UI的几种方法

    第一种: new Handler(context.getMainLooper()).post(new Runnable() { @Override public void run() { // 在这里 ...

  2. 写在使用 Linux 工作一年后

    start 去年公司空了几台台式机,当时看了下似乎配置比我用的乞丐版 air 略高一些,而且除了 ssd 以外还有一个 1T 的大硬盘,加上后面可能会有一段时间不做 iOS 了,那就不需要 macOS ...

  3. Variability aware wear leveling

    Techniques are presented that include determining, for data to be written to a nonvolatile memory, a ...

  4. 道量化交易程序猿(25)--Cointrader之MarketData市场数据实体(12)

    转载注明出处:http://blog.csdn.net/minimicall.http://cloudtrade.top/ 前面一节我们说到了远端事件.当中.市场数据就属于远端事件.市场数据有什么?我 ...

  5. Multi-tasking RTOS for microprocessors with limited memory by saving only a single return address per task during context switching

    A real-time operating system (RTOS) for use with minimal-memory controllers has a kernel for managin ...

  6. 机器学习:DeepDreaming with TensorFlow (二)

    在前面一篇博客里,我们介绍了利用TensorFlow 和训练好的 Googlenet 来生成简单的单一通道的pattern,接下来,我们要进一步生成更为有趣的一些pattern,之前的简单的patte ...

  7. Cocostudio学习笔记(3) ImageView + Slider

    此记录使用两个控制流:ImageView 和 Slide. ---------------------------------------------------------------------- ...

  8. Jmeter 专题

    Jmeter是一个非常好用的压力测试工具.  Jmeter用来做轻量级的压力测试,非常合适,只需要十几分钟,就能把压力测试需要的脚本写好. 为什么要建立线程组?原因很简单,因为我们要模拟多个线程(用户 ...

  9. 在实现视频播放器的步骤client(三)风行网络电影列表

    (三) 今日热门电影实现这个功能.主要从server获取数据.然后显示在屏幕上.虽然说是从这个server获取电影信息数据,但,不实际的http相关知识,我们直接sdk包(56网络提供api),你将能 ...

  10. linux 用蓝牙和手机通信

    加载模块: # modprobe hci_usb    # modprobe rfcomm    # hciconfig hci0 up # hciconfig hci0 up 查看状况: # hci ...