Jenkins持续部署-自动生成版本号

目录

  • Jenkins持续部署-自动生成版本号

    • 目录
    • 前言
    • 目的
    • 详细流程
      • 获取SVN Reversion
      • 获取需求号
      • 设置编译前读取版本号
    • 总结
    • 参考文献

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查看支持的所有参数。

获取版本号

我们需要获取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\FGMain1>  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
作者博客:杰哥很忙
欢迎转载,请在明显位置给出出处及链接

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

时间: 2024-10-13 04:39:58

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

工具——基于SVN的代码中自动生成版本号

SVN一般都是团队合作做一个项目所需用到的,为了是版本的统一, 我现在用的版本是 AnkhSvn-2.1.7141.181.msiSVN取出[SVN checkout]:从档案库中取出工作复本. 汇出[Export]:从档案库中汇出干净的工作复本,不含svn管理用数据夹. 汇入[Import]:汇入目录至档案库. SVN Commit[SVN送交]:将你所做的修改送交至档案库. SVN Update[SVN更新]:更新工作复本至目前档案库的最新版本. Update to reversion[更新

.net自动生成版本号

在 AssemblyInfo.cs 文件中 修改 一下属性 [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] 将这部分代码 替换为 [assembly: AssemblyVersion("1.0.*")] 这样子 编译 查看文件的属性 会 发现 文件版本 和 产品版本 就变成一个 随机数了 在程序集中获取当前 程序集的版本 Assem

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

在 bloglovin ,我们使用自动生成版本号来设置Xcode,使当前的版本号为在Git活跃的分支上 的提交数.它一直正常工作着,但我们的技术也不是一帆风顺的. 糟糕的老方法 我们使用的技术是来自一个叫 “Cocoa is my Girlfriend”1 的网友发表的一篇文章,真是个不可 原谅的名字.它工作原理如下: 1. 运行一个脚本拿到Git提交数并把它输出到一个 “infoplistwhatever.h” 文件中.这个文件名称 是什么并不重要. 2. 在Xcode中选择 Target 中

jenkins持续集成Allure生成报表+邮件推送

本次基于<jenkins 生成HTML报表,邮件推送>的基础上将生成HTML报表修改为Allure生成报表,可以参考官方文档:https://docs.qameta.io/allure/#_jenkins 1.启动tomcat登录jenkins 2.Install the latest version of Allure Plugin from "Plugin Manager" page. 3.(1)Open Plugin Advanced Settings (at <

pytest系列(四)- pytest+allure+jenkins - 持续集成平台生成allure报告

pytest是什么 pytest是python的一款测试框架,拥有unittest的功能并比它更丰富. allure是什么 有非常多的优秀的测试框架,但却是有非常少优秀的报告工具可以展示非常清楚的用例执行过程和结果. allure是一款开源的,专门用来展示测试结果的工具,目的是希望团队内部每一个人都可以看到非常清楚的测试结果. allure可以与非常多的著名测试框架做集成. 像java语言,可以与junit4,junit5,TestNG测试框架集成. python语言,可以与pytest,beh

jenkins持续集成--看我如何从1到代码自动部署

jenkins持续集成看我如何从1到代码自动化部署 背景 近期由于工作原因需要学习jenkins持续化集成.对于一个好学又帅气的我来说.学习他还不是手到擒来.公司为一个中小型创业公司,在部署代码上面,很少用gitlab.jenkins等等,也没有清晰的生产环境-测试环境-线上环境之分.唯一有的就是写完代码-登陆服务关闭服务,上传代码-开启服务.这样往往会到来很多问题,同时也会给程序员带来很多繁琐的工作.这不,公司内部整顿,需要一套清晰的流程,而且为了减轻程序员的负担,于是就希望采用gitlab+

转载:持续集成Jenkins+sonarqube部署教程

转载: 持续集成Jenkins+sonarqube部署教程 持续集成 1 引言 1.1 文档概要 本文主要介绍jenkins,sonar的安装与集成,基于ant,maven构建.用一个例子介绍jenkins的编译打包部署,代码检查.最后集成jenkins.(现阶段只是简易的集成,后续需要修改accio源码做深度集成) 1.2 预计读者 系统配置管理员:要懂得搭建持续集成环境,有问题可以排查:架构师:了解持续集成实现原理,协助项目接入持续集成.项目在持续集成环境运行中,进行维护.分析构建异常等:维

jenkins实现项目自动部署

背景 整体思路 实现方式 1 自动化部署脚本 2 远程执行 3 配置jenkins任务 背景 之前给公司搭建过一套gitlab+gerrit+jenkins的持续集成环境,由于操作起来有点繁琐,自己也没太搞清楚该怎么用,所以一直就只用了gitlab来做代码管理.最近要做一个项目自动部署的功能,使用过jenkins一定知道他的自动化功能.所以就从jenkins创建自动部署任务的方式来入手. 整体思路 jenkins可以配置触发器,当有新的提交时,触发执行相应的任务.由于jenkins和项目部署不在

Jenkins持续集成-自动化部署脚本的实现《python》

读者须知:1.本手记本着记续接前面的两张手记内容整理2.本手记针对tomcat部署测试环境实现 最近工作比较繁忙,导致这章一直拖延,没有太抽出时间来总结.要实现Jenkins端的持续集成,其实在CI服务配置端很容易,难点呢?就是如何实现自动化的部署.我的脚本设计就是为了解决以下难题: 难点一.如何使得自动化部署脚本更通用 我用的脚本,依赖依赖一个配置文件的模块化,让每一个应用业务模块更加通用.自动化所执行的命令呢?我也是设计想法本着更加通用平台的原则,至少对于tomcat+java or jav