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編譯完成之后,我們需要處理以下事項。

20190727151702.png

  1. jenkins編譯成功,先獲取所有exe,dll,獲取他們的版本號,創建文件更新清單。
  2. 將所有的exe,dll,pdb以及文件更新清單進行壓縮打包,壓縮文件名為指定主程序的版本號。
  3. 將上個版本的文件壓縮包解壓,若沒有上個版本的文件壓縮包,則無需上傳差量更新包。
  4. 比較當前文件更新清單和上個版本的文件更新清單。若文件名一樣,版本號也一樣,則刪除編譯出的對應文件。
  5. 獲取剩下的所有exe,dll,pdb以及文件更新清單進行壓縮打包,壓縮文件名為指定主程序的版本號_Diff。
  6. 遍歷每個服務配置。
  7. 獲取上傳的相對目錄為Job名/版本號.zip,調用遠程命令,檢查服務器是否存在目錄,沒有的話則創建目錄。
  8. 目錄創建完畢后,將壓縮文件上傳,若有差量更新包則也需要上傳。
  9. 最后調用遠程命令對服務進行卸載與更新。

生成版本號

上一章介紹了.net環境下如何自動生成版本號的邏輯,這里不再介紹。有興趣的可以看《Jenkins持續部署-自動生成版本號

獲取版本號

$version =(Get-ChildItem $executeFile).VersionInfo.FileVersion
  1. $executeFile為可執行的exe文件。通過Get-ChildItem 獲取到該文件。
  2. 通過VersionInfo.FileVersionVersionInfo.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
}
  1. 通過Get-ChildItem *.dll,*.exe獲取當前目錄所有的dll和exe文件
  2. 將更新清單文件名設置為FileUpdateList.txt,若已經存在更新清單文件,則刪除舊的重新生成。
  3. 通過New-Object創建一個.Net的集合,用於存放每個文件的文件信息。
  4. $fileInfo="" | Select-Object -Property FileName,Version定義了一個自定義對象類型,包含了文件名和版本號2個屬性。
  5. 將每個文件的文件名和版本號保存到集合中
  6. 將集合轉化為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 
    }
}
  1. 由於powershell2.0沒有內部的壓縮解壓方法。這里使用.net的ICSharpCode.SharpZipLib.dll進行壓縮。
  2. 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
}
  1. 我通過正則篩選出文件格式為四位數字版本.zip的文件。
  2. 然后對篩選出的文件名進行排序。
  3. 最后獲取比當前版本號小的上一個版本號。最后返回上個版本的完整文件路徑及版本號信息。

創建差量更新包

我只需要根據兩個文件清單進行文件名和版本號的對比,如果同一個文件的版本號一樣,則該文件無需更新。


    $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
        }
    }
  1. 先解壓出上個版本的壓縮文件。
  2. 然后讀取兩個版本的文件清單。
  3. 將一樣的文件加入到一個列表中。

有了重復文件的列表,接下來就可以將重復文件都刪除。最后剩下差量更新的文件

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
  1. 比較數組的數量和當前讀取到的文件數量是否一樣,若一致表示所有文件都一樣,則無需更新,返回false表示沒有生成差量更新文件。就不用創建差量更新文件了。否則則刪除所有的文件和對應的符號文件。
  2. 然后將所有的差量文件進行壓縮,文件后面加上_diff表示是差量更新的文件。
  3. 最后把解壓出來的上個版本的文件和文件加都刪除掉。返回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
    }
}
  1. 通過Remote-CreateDic執行遠程創建文件夾的命令。
  2. 通過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上傳到各個服務器上。最后就可以調用各個服務器的遠程腳本進行服務的卸載與更新了。

  1. Windows PowerShell 入門
  2. Powershell變量的作用域
  3. Windows Remote Management
  4. windows服務器遠程執行命令(PowerShell+WinRM)
  5. winServer-常用winrm命令
  6. Use Powershell to start a GUI program on a remote machine

20191127212134.png
微信掃一掃二維碼關注訂閱號傑哥技術分享
本文地址:https://www.cnblogs.com/Jack-Blog/p/11255314.html
作者博客:傑哥很忙
歡迎轉載,請在明顯位置給出出處及鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM