Jenkins持续部署-创建差量更新包
Jenkins持续部署-创建差量更新包
目录
Jenkins持续集成学习-Windows环境进行.Net开发1
Jenkins持续集成学习-Windows环境进行.Net开发2
Jenkins持续集成学习-Windows环境进行.Net开发3
Jenkins持续集成学习-Windows环境进行.Net开发4
Jenkins持续集成学习-搭建jenkins问题汇总
Jenkins持续部署-Windows环境持续部署探究1
Jenkins持续部署-自动生成版本号
Jenkins持续部署-创建差量更新包
前言
上一篇文章介绍关于版本号的自动生成逻辑,本篇文章主要介绍通过脚本跟版本号创建程序的差量包。
目的
本章主要是通过jenkins持续集成之后通过powershell生成差量更新包与全量更新包,然后将他们上传到FTP上。
详细流程
当jenkins编译完成之后,我们需要处理以下事项。
- jenkins编译成功,先获取所有exe,dll,获取他们的版本号,创建文件更新清单。
- 将所有的exe,dll,pdb以及文件更新清单进行压缩打包,压缩文件名为指定主程序的版本号。
- 将上个版本的文件压缩包解压,若没有上个版本的文件压缩包,则无需上传差量更新包。
- 比较当前文件更新清单和上个版本的文件更新清单。若文件名一样,版本号也一样,则删除编译出的对应文件。
- 获取剩下的所有exe,dll,pdb以及文件更新清单进行压缩打包,压缩文件名为指定主程序的版本号_Diff。
- 遍历每个服务配置。
- 获取上传的相对目录为Job名/版本号.zip,调用远程命令,检查服务器是否存在目录,没有的话则创建目录。
- 目录创建完毕后,将压缩文件上传,若有差量更新包则也需要上传。
- 最后调用远程命令对服务进行卸载与更新。
生成版本号
上一章介绍了.net
环境下如何自动生成版本号的逻辑,这里不再介绍。有兴趣的可以看《Jenkins持续部署-自动生成版本号
》
获取版本号
$version =(Get-ChildItem $executeFile).VersionInfo.FileVersion
$executeFile
为可执行的exe文件。通过Get-ChildItem 获取到该文件。- 通过
VersionInfo.FileVersion
或VersionInfo.ProductVersion
获取到文件的文件版本号。
不知道为什么和文件右键属性看到的两个版本号有点区别右击属性的文件版本号和产品版本可能不一样,但是通过代码获取到的都是一样的
创建文件更新清单
为了能够生成程序差量更新包,减少程序更新时的更新包大小,需要可以通过比较当前版本和上一个版本的各个文件的版本号信息。通过将该文件保存成一份文件清单。再比较时直接通过该文件清单进行比对,能够很方便的生成从差量的文件清单。同时文件清单能够清晰的列出每个需要更新的文件和文件版本号,给人看会比较直观。
主要是我们自己需要用该文件,也可以不用该文件,直接获取所有文件,对比他们的文件名和版本号是否一致也可以。
$programFiles = Get-ChildItem *.dll,*.exe $updateListFileName = "FileUpdateList.txt" Create-FileUpdateList -files $programFiles -fileName $updateListFileName function Create-FileUpdateList(){ param([System.IO.FileInfo[]]$files,[string]$fileName) ## 删除原始的清单文件 if(Test-Path $fileName) { Write-Host "Remove Old UpdateList File" Remove-Item $fileName } $array=New-Object System.Collections.ArrayList foreach($file in $files) { ## 获取每个文件版本号 $fileVersion =(Get-ChildItem $file).VersionInfo.ProductVersion $fileInfo="" | Select-Object -Property FileName,Version $fileInfo.FileName = $file.Name $fileInfo.Version = $fileVersion ## 追加到文件 $null = $array.Add($fileInfo) Write-Host "Update File:"$file.Name ",Version:" $fileVersion } $json = ConvertTo-Json $array.ToArray() $json >> $fileName }
- 通过
Get-ChildItem *.dll,*.exe
获取当前目录所有的dll和exe文件 - 将更新清单文件名设置为
FileUpdateList.txt
,若已经存在更新清单文件,则删除旧的重新生成。 - 通过
New-Object
创建一个.Net的集合,用于存放每个文件的文件信息。 $fileInfo="" | Select-Object -Property FileName,Version
定义了一个自定义对象类型,包含了文件名和版本号2个属性。- 将每个文件的文件名和版本号保存到集合中
- 将集合转化为Json格式保存到文件中.
由于我需要在powershell2.0
的环境上执行脚本,powershell2.0
没有Json读写的api,这里使用的是别人写的一个脚本生成json格式的字符串。
function ConvertTo-Json { param( $InputObject ) if( $InputObject -is [string]){ "`"{0}`"" -f $InputObject } elseif( $InputObject -is [bool]) { $InputObject.ToString().ToLower() } elseif( $null -eq $InputObject){ "null" } elseif( $InputObject -is [pscustomobject]) { $result = "$space{`r`n" $properties = $InputObject | Get-Member -MemberType NoteProperty | ForEach-Object { "`"{0}`": {1}" -f $_.Name, (ConvertTo-Json $InputObject.($_.Name)) } $result += $properties -join ",`r`n" $result += "$space`r`n}" $result } elseif( $InputObject -is [hashtable]) { $result = "{`r`n" $properties = $InputObject.Keys | ForEach-Object { "`"{0}`": {1}" -f $_, (ConvertTo-Json $InputObject[$_]) } $result += $properties -join ",`r`n" $result += "`r`n}" $result } elseif( $InputObject -is [array]) { $result = "[`r`n" $items = @() for ($i=0;$i -lt $InputObject.length;$i++) { $items += ConvertTo-Json $InputObject[$i] } $result += $items -join ",`r`n" $result += "`r`n]" $result } else{ $InputObject.ToString() } }
压缩
将所有文件进行压缩,压缩文件名为版本号。
function New-ZipFile() { param(## The name of the zip archive to create [object[]]$files, $zipName = $(throw "Specify a zip file name"), ## Switch to delete the zip archive if it already exists. [Switch] $Force) ## Check if the file exists already. If it does, check ## for -Force - generate an error if not specified. if(Test-Path $zipName) { if($Force) { Write-Host "Remove File:" $zipName Remove-Item $zipName } else { throw "Item with specified name $zipName already exists." } } ## Add the DLL that helps with file compression Add-Type -Assembly System try { #打开或创建文件流 $compressedFileStream = [System.IO.File]::Open($zipName,[System.IO.FileMode]::OpenOrCreate) #创建压缩文件流 $compressionStream = New-Object ICSharpCode.SharpZipLib.Zip.ZipOutputStream($compressedFileStream) ## Go through each file in the input, adding it to the Zip file ## specified foreach($file in $files) { ## Skip the current file if it is the zip file itself if($file.FullName -eq $zipName) { continue } ## Skip directories if($file.PSIsContainer) { continue } #读取每个文件进行压缩 try { #打开文件 $originalFileStream = [System.IO.File]::Open($file.FullName,[System.IO.FileMode]::Open) $entry = New-Object ICSharpCode.SharpZipLib.Zip.ZipEntry($file.Name) $compressionStream.PutNextEntry($entry); $bytes = New-Object Byte[] $originalFileStream.Length #读取文件流 $null = $originalFileStream.Read($bytes,0,$bytes.Length) #写入到压缩流 $compressionStream.Write($bytes,0,$bytes.Length) } finally { $originalFileStream.Dispose() } } } catch{ $Error Remove-Item $Path } finally { ## Close the file $compressionStream.Dispose() $compressedFileStream.Dispose() $compressionStream = $null $compressedFileStream = $null } }
- 由于
powershell2.0
没有内部的压缩解压方法。这里使用.net的ICSharpCode.SharpZipLib.dll
进行压缩。 - 在
powershell
中可以通过New-Object Byte[]
创建.net的字节数组
,若构造函数需要传参则直接将参数写到后面即可。
该脚本不支持带文件夹的压缩。
获取上个版本的包
由于我定义的都是4位版本号,前三位是需求号,最后一位是修订号。因此我直接通过前三位获取上个版本的压缩包文件即可,若没有,则无需创建差量更新包。
function Get-LastPackage($currentVersion,$majorVersion) { #默认上个版本号就是当前版本号 $lastFileName = $null $lastMajorVersion = $null $lastVersion = $null #读取上一个版本的压缩文件 # 获取所有文件夹,程序目录下的目录都是版本号目录 $allFile = Get-ChildItem | Where-Object{ $_.Name -match '(\d*\.\d*\.\d*.\d*)\.zip' } if($null -eq $allFile) { Write-Host "No Last Package" return } ## 获取上一个版本号最新的修改 $allFile = $allFile | Where-Object {$_.Name -lt $majorVersion} | Sort-Object -descending ## 判断是否有比当前版本小的 if($null -eq $allFile) { ## 没有历史的大版本号,则全量更新,和当前版本的主版本号一样 Write-Host "No Last Package" return } #有多个文件如2.25.0,2.24.1,则获取到的是数组 elseif($allFile -is [array]) { ##存在历史更新,则获取上一个版本的更新目录 $lastFileName = $allFile[0] } #只有一个目录,首个版本打包时则获取到的是DirectoryInfo else { $lastFileName = $allFile } ## 获取最新的版本 $lastVersion =[System.IO.Path]::GetFileNameWithoutExtension($lastFileName) $lastMajorVersion = [System.IO.Path]::GetFileNameWithoutExtension($lastVersion) #返回文件名 主版本号 版本号 $lastFileName $lastVersion $lastMajorVersion }
- 我通过正则筛选出文件格式为
四位数字版本.zip
的文件。 - 然后对筛选出的文件名进行排序。
- 最后获取比当前版本号小的上一个版本号。最后返回上个版本的完整文件路径及版本号信息。
创建差量更新包
我只需要根据两个文件清单进行文件名和版本号的对比,如果同一个文件的版本号一样,则该文件无需更新。
$lastUnpackDir = UnZip $lastZipFullName $lastVersion $currentUpdateObject = Read-Json $currentUpdateListFile $lastUpdateObject = Read-Json $lastUpdateListFile $array = New-Object System.Collections.ArrayList #比较两个清单文件 foreach($currentFile in $currentUpdateObject) { if($currentFile -eq "FileUpdateList.txt") { #跳过清单文件 continue } ##遍历json数组数组对象本身也会遍历,且值为空 if($null -eq $currentFile.FileName) { #跳过清单文件 continue } #当前清单每个文件去上个版本查找 $lastFile = $lastUpdateObject | Where-Object {$_.FileName -eq $currentFile.FileName} | Select-Object -First 1 #找到文件,判断版本号 if($lastFile.Version -eq $currentFile.Version) { #版本号一样则不需要更新 $sameFile = Join-Path $currentUnpackDir $lastFile.FileName $null = $array.Add($sameFile) continue } }
- 先解压出上个版本的压缩文件。
- 然后读取两个版本的文件清单。
- 将一样的文件加入到一个列表中。
有了重复文件的列表,接下来就可以将重复文件都删除。最后剩下差量更新的文件
if($array.Count -eq $currentUpdateObject.Length - 1) { #所有都一样,不需要更新 Write-Host "No Modified File" return $false } else { #存在不一样的包,则更新所有 foreach($sameFile in $array) { Write-Host "Remove Not Modified File " $sameFile Remove-Item $sameFile #同时删除pdb文件 $pdbFile = [System.IO.Path]::GetFileNameWithoutExtension($sameFile)+".pdb" if(Test-Path $pdbFile) { Remove-Item $pdbFile } } #重新获取解压的目录 $diffFiles = Get-ChildItem *.dll,*.exe #创建新的清单文件 Create-FileUpdateList -files $diffFiles -fileName $currentUpdateListFile #重新压缩所有文件 $files = Get-ChildItem *.dll,*.pdb,*.exe,"FileUpdateList.txt" Write-Host "Need Update File " $files $diffZipFullName = [System.IO.Path]::GetFileNameWithoutExtension($currentZipFullName)+"_diff.zip" New-ZipFile -files $files -Path $diffZipFullName -Force true } #移除上个版本的解压出的压缩文件夹 Write-Host "Remove Last Update Package dir" $lastUnpackDir Get-ChildItem $lastUnpackDir | Remove-Item -Recurse Remove-Item $lastUnpackDir -Recurse $return $true
- 比较数组的数量和当前读取到的文件数量是否一样,若一致表示所有文件都一样,则无需更新,返回
false
表示没有生成差量更新文件。就不用创建差量更新文件了。否则则删除所有的文件和对应的符号文件。 - 然后将所有的差量文件进行压缩,文件后面加上_diff表示是差量更新的文件。
- 最后把解压出来的上个版本的文件和文件加都删除掉。返回
true
表示生成了差量更新文件。
读取服务器Json配置
全量和差量文件生成完毕后就可以将文件上传到指定的服务器了。我将服务器的配置信息保存到了ServerInfo.Json
文件中,这样想添加其他服务器只要修改一下这个配置文件即可。读取配置的服务器,ServerInfo.Json
包含了服务器的一些信息,包括地址,用户名,及一些路径配置。
$config = Read-Json "ServerInfo.json" foreach($itemConfig in $config) { Remote-CreateDic -userName $itemConfig.UserName -password $itemConfig.Password -address $itemConfig.Address -jobName $ftpFileName -programeDir $itemConfig.ProgramDir -ErrorAction Stop #目标 ftp://host:port/xxx/xxx.zip $destination = "ftp://"+$itemConfig.FTP.Host+":"+$itemConfig.FTP.Port+"/"+$ftpFileName # FTP上传压缩包 FTP-UpdateFile -file $zipFullName -destination $destination -userName $itemConfig.FTP.UserName -password $itemConfig.FTP.Password -ErrorAction Stop if($hasDiffFile ){ $ftpFileName = Join-Path $ENV:JOB_NAME ($version+"_diff.zip") FTP-UpdateFile -file $zipFullName -destination $destination -userName $itemConfig.FTP.UserName -password $itemConfig.FTP.Password -ErrorAction Stop } }
- 通过
Remote-CreateDic
执行远程创建文件夹的命令。 - 通过
FTP-UpdateFile
将全量更新包和差量更新包都上传到指定的服务器上。
远程创建文件夹目录
在上传到FTP上时,若有必要则需要先在FTP上创建指定的文件夹,避免上传文件夹的时候由于没有文件夹导致上传失败。
由于需要远程调用,因此需要传递用户,密码和服务器地址。同时还要包含jenkins当前的job名称以及远程服务器程序上传路径。上传的路径约定为FTP地址/Job名称/版本号.zip
function Remote-CreateDic() { param([string] $userName=$(throw "Parameter missing: -userName"), [string] $password=$(throw "Parameter missing: -password"), [string] $address=$(throw "Parameter missing: -address"), [string] $jobName=$(throw "Parameter missing: -jobName"), [string] $programeDir=$(throw "Parameter missing: -programeDir")) #非域用户需要添加\,否则远程调用有问题 if(!$userName.StartsWith('\')) { $userName="\"+$userName } $secpwd = convertto-securestring $password -asplaintext -force $cred = new-object System.Management.Automation.PSCredential -argumentlist $userName,$secpwd #程序存放目录和当前的jenkins job目录合并后是服务器锁在的FTP程序存放目录 $zipFile= [System.IO.Path]::Combine($programeDir,$jobName) #备份程序 invoke-command -computername $address -Credential $cred -command { param([string]$file) #获取文件夹路径 $dir = [System.IO.Path]::GetDirectoryName($file) if(Test-Path $dir){ Write-Host "Dic exists :" $dir #文件夹存在 if(Test-Path $file) { #压缩文件已存在.不允许,版本号没变。必须改变 throw $file + "exists,retry after change version" } } else { # 判断文件夹是否存在 # 没有文件夹则创建,否则首次FTP上传没有文件夹时则会上传失败 #防止输出 $null = New-Item -Path $dir -Type Directory } } -ArgumentList $zipFile -ErrorAction Stop }
FTP上传
FTP上传可以调用.Net的WebClient上传文件的方法处理。
function FTP-UpdateFile() { param([String]$file=$(throw "Parameter missing: -name file"), [String]$destination=$(throw "Parameter missing: -name destination"), [String]$userName=$(throw "Parameter missing: -name userName"), [String]$password=$(throw "Parameter missing: -name destination")) $pwd=ConvertTo-SecureString $password -AsPlainText -Force; #111111为密码 $cred=New-Object System.Management.Automation.PSCredential($userName,$pwd); #创建自动认证对象 $wc = New-Object System.Net.WebClient try { $wc.Credentials = $cred Write-Host "upload to ftp. " $file "->" $destination $wc.UploadFile($destination, $file) Write-Host "upload success " } finally { $wc.Dispose() Write-Host "close ftp. " } }
总结
本文对Windows下的持续部署包创建和上传的逻辑继续了说明,主要通过自动生成的版本号继续判断与比较哪些库包需要更新。最终将库包通过FTP上传到各个服务器上。最后就可以调用各个服务器的远程脚本进行服务的卸载与更新了。
- Windows PowerShell 入门
- Powershell变量的作用域
- Windows Remote Management
- windows服务器远程执行命令(PowerShell+WinRM)
- winServer-常用winrm命令
- Use Powershell to start a GUI program on a remote machine
微信扫一扫二维码关注订阅号杰哥技术分享
本文地址:https://www.cnblogs.com/Jack-Blog/p/11255314.html
作者博客:杰哥很忙
欢迎转载,请在明显位置给出出处及链接
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?