Jenkins持續集成學習-Windows環境進行.Net開發3


Jenkins持續集成學習-Windows環境進行.Net開發3


目錄

Jenkins持續集成學習-Windows環境進行.Net開發1
Jenkins持續集成學習-Windows環境進行.Net開發2
Jenkins持續集成學習-Windows環境進行.Net開發3
Jenkins持續集成學習-Windows環境進行.Net開發4
Jenkins持續集成學習-搭建jenkins問題匯總

前言

在前面兩篇文章介紹了關於持續集成的完整主流程。

30.png

目標

在上一篇文章中我們完成了主流程的持續集成,但是提交代碼仍然需要手動點擊構建,本篇文章就來探究如何做到SVN代碼提交后自動構建。

優化nuget包生成流程

在開始之前我需要解決上一篇文章理解有誤的一個問題。
在上一章我們將單元測試的不穩定錯誤等級設置為1。

2.27.PNG

當我添加多個失敗的單元測試時,我發現1次單元測試失敗錯誤等級就會加1,我增加了一共11個失敗的單元測試,因此單元測試失敗返回值為11。

4.PNG
因此上次的邏輯就行不通了,編譯的時候自動創建nuget包,不穩定版本刪除nuget包,這樣只能將錯誤等級設置的非常大。比如int.Max,否則失敗會導致刪除腳本不執行。
因此我們有兩種選擇:

1. 編譯的時候自動創建nuget包, 單元測試將不穩定的ERRORLEVEL設置的非常大,單元測試失敗都可以認為是不穩定,然后自動刪除nuget包。
2. 編譯的時候不自動創建nuget包,單元測試通過后再調用腳本創建nuget包。

我們優化一下使用第二種方法生成nuget包。

我們將項目中自動生成nuget包的勾去除
3.PNG

或者我們修改csprojGeneratePackageOnBuild節點值,改為false,則編譯的時候也不會自動創建nuget包。
5.PNG

然后我們修改單元測試ERRORLEVEL,單元測試失敗了就不再執行后續Build的流程,在單元測試成功時創建Nuget包。
6.png

通過nuget pack csproj文件名 -Properties Configuration=Release -OutputDirectory 輸出文件夾命令創建nuget包

需要加-Properties Configuration=Release參數。使用pack創建包的時候會先進行編譯,若沒有指定Release在默認會生成Debug版本
需要添加-OutputDirectory XXXX參數,否則默認會保存到項目的根目錄。
同時我們刪除了ERRORLEVEL,只要單元測試失敗都算失敗,這樣就不會執行報創建了。

17:53:29 Results (nunit3) saved as TestResult.xml
17:53:29 
17:53:29 D:\Program Files (x86)\Jenkins\workspace\unittest>exit 0 
17:53:29 [unittest] $ cmd /c call C:\WINDOWS\TEMP\jenkins3052083372263337733.bat
17:53:29 
17:53:29 D:\Program Files (x86)\Jenkins\workspace\unittest>E:\開發工具\VS開發工具\VS插件\nuget.exe pack Jenkins.Core/Jenkins.Core.csproj -Properties Configuration=Release -OutputDirectory Jenkins.Core\bin\Release 
17:53:29 正在嘗試從“Jenkins.Core.csproj”生成程序包。
17:53:29 MSBuild auto-detection: using msbuild version '15.9.21.664' from 'D:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\bin'.
17:53:31 正在打包“D:\Program Files (x86)\Jenkins\workspace\unittest\Jenkins.Core\bin\Release\net45”中的文件。
17:53:31 警告: NU5115: 未指定 Description。正在使用“Description”。
17:53:31 Successfully created package 'D:\Program Files (x86)\Jenkins\workspace\unittest\Jenkins.Core\bin\Release\Jenkins.Core.0.5.0.nupkg'.
17:53:32 
17:53:32 D:\Program Files (x86)\Jenkins\workspace\unittest>exit 0 
17:53:33 Recording NUnit tests results
17:53:33 Starting Publish Nuget packages publication
17:53:33 [unittest] $ E:\開發工具\VS開發工具\VS插件\NuGet.exe push Jenkins.Core\bin\Release\Jenkins.Core.0.5.0.nupkg ******** -Source http://127.0.0.1:10080/nuget -NonInteractive
17:53:33 Pushing Jenkins.Core.0.5.0.nupkg to 'http://127.0.0.1:10080/nuget'...
17:53:34   PUT http://127.0.0.1:10080/nuget/
17:53:35   Created http://127.0.0.1:10080/nuget/ 981ms
17:53:35 Your package was pushed.
17:53:35 Ended Publish Nuget packages publication
17:53:35 [WS-CLEANUP] Deleting project workspace...
17:53:35 [WS-CLEANUP] Skipped based on build state SUCCESS
17:53:35 Finished: SUCCESS

此時流程優化如下

28.PNG

graph LR 編譯程序集 --> 編譯單元測試程序集 編譯單元測試程序集 --> |通過| 執行單元測試 編譯單元測試程序集 --> |不通過| 失敗 執行單元測試 --> |通過| 創建nuget包 創建nuget包 --> 上傳nuget包 執行單元測試 --> |不通過| 失敗 上傳nuget包 --> 清理編譯文件夾 失敗 --> 清理編譯文件夾

自動觸發構建

SVN自動觸發構建一共有3種方式。

  1. 分別為Jenkins定時輪詢觸發。
  2. SVN客戶端創建鈎子觸發。
  3. SVN服務器端創建鈎子觸發。

Jenkins定時輪詢觸發

Jenkins定時輪詢觸發是使用Jenkins 輪詢SCM功能定時檢查SVN是否有變更觸發構建。

Jenkins的輪詢SCM的說明上提到該功能需要掃描整個Jenkins工作區並驗證,操作性能要求比較高。我們依然驗證一下這個功能。

在配置Build Triggers選項中勾選輪詢SCM,在Schedule輸入 * * * * *表示每分鍾輪詢一次,即代碼提交后1分鍾觸發構建。
1.png

設置完之后我們提交代碼就會自動構建了。相比手動構建,自動構建左邊菜單欄會顯示輪詢日志,右邊會顯示由SCM變更啟動,表明是輪詢SCM觸發的構建。

2.png

SVN客戶端鈎子觸發

SVN客戶端鈎子觸發是在本地提交的時候執行本地的Post-Commit鈎子,通過這個鈎子執行腳本使用http請求調用jenkins的遠程構建接口。

配置

生成用戶授權Token

系統配置-管理用戶-用戶-配置API TOKEN點擊生成新的Token按鈕,創建一個token。我們需要根據這個token來獲取權限。
10.png

增加項目授權token

在項目的配置中修改Build Triggers,勾選Trigger builds remotely支持觸發遠程構建。在Authentication Token輸入一個自定義的串,我們可以使用JENKINS_URL/job/JOB_NAME/build?token=TOKEN_NAME來遠程構建項目。比如我們當前項目可以使用http://127.0.0.1:8080/job/unittest/build?token=123遠程構建
11.PNG

創建客戶端鈎子腳本

20.PNG

graph LR SVN提交代碼 --> 觸發代碼提交后鈎子 觸發代碼提交后鈎子 --> 執行本地腳本

創建一個bat腳本。命名為post-commit-unittest.bat,我們在這個腳本里寫入參數,將真正執行通知的腳本分離出來,這就可以重用了。

SET CSCRIPT=%windir%\system32\cscript.exe
SET VBSCRIPT=F:\Repositories\JenkinsTest\hooks\post-commit-hook-jenkins.vbs
SET JENKINS=http://127.0.0.1:8080/
SET JOBNAME="unittest"
SET TOKEN="123"
REM AUTHORIZATION: Set to "" for anonymous acceess
REM AUTHORIZATION: Set to encoded Base64 string, generated from "user_id:api_token" 
REM                found on Jenkins under "user/configure/API token"
REM                User needs "Job/Read" permission on Jenkins
REM AUTHORIZATION=base64(test:1184023ac835f44484f52316235a033db8)
SET AUTHORIZATION="dGVzdDoxMTg0MDIzYWM4MzVmNDQ0ODRmNTIzMTYyMzVhMDMzZGI4"
"%CSCRIPT%" "%VBSCRIPT%" %JENKINS% %JOBNAME% %TOKEN% %AUTHORIZATION%

SVN調用腳本會傳入3個參數

1. 當前項目的SVN倉庫地址
2. 當前的版本號
3. 事務名稱

這里暫時不需要用到。

通過CScript.exe調用執行vbs腳本。

CScript.exe是Windows腳本宿主的一個版本,可以用來從命令行運行腳本。

通知腳本參數說明

1. CSCRIPT:CScript.exe的路徑。
2. VBSCRIPT:同時jenkins的腳本路徑。
3. JENKINS:jenkins服務地址。
4. JOBNAME:項目名稱。
5. TOKEN:項目的Token。
6. AUTHORIZATION:用於授權token。

AUTHORIZATION值為base64(user_id:api_token)

設置鈎子

在SVN客戶端的設置中找到鈎子腳本,點擊添加。

7.PNG
設置路徑和腳本路徑,注意左下角兩項勾起來。
8.png

21.PNG

graph LR 獲取Jenkins-Crumb-->提交build請求

創建通知腳本

創建一個vbs腳本用於執行通知。

jenkins       = WScript.Arguments.Item(0)
Wscript.Echo "jenkins="&jenkins
jobName  = WScript.Arguments.Item(1)
Wscript.Echo "token="&token
token  = WScript.Arguments.Item(2)
Wscript.Echo "token="&token
authorization = WScript.Arguments.Item(3)
Wscript.Echo "authorization="&authorization

url = jenkins + "crumbIssuer/api/xml?xpath=concat(//crumbRequestField,"":"",//crumb)"
Wscript.Echo "url="&url
Set http = CreateObject("MSXML2.ServerXMLHTTP")
http.open "GET", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
if not authorization = "" then
  http.setRequestHeader "Authorization", "Basic " + authorization
end if
http.send
crumb = null
if http.status = 200 then
  crumb = split(http.responseText,":")
end if
Wscript.Echo crumb(0)&"="&crumb(1)
url = jenkins + "job/unittest/build?token=" + token
Wscript.Echo url
Set http = CreateObject("MSXML2.ServerXMLHTTP")
http.open "GET", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
if not authorization = "" then
  http.setRequestHeader "Authorization", "Basic " + authorization
end if
if not isnull(crumb) then 
  http.setRequestHeader crumb(0),crumb(1)
end if
http.send
Wscript.Echo "Status: " & http.status &"Body: " & http.responseText

不同項目使用不同的post-commit.bat的腳本,腳本中設置JOB_NAME和JOB_TOKEN,不同項目最終都是調用上面的這個腳本進行遠程構建。

獲取Jenkins-Crumb

我們先獲取到Jenkins-Crumb獲取到防跨域攻擊token。通過向JENKINS_URL/crumbIssuer/api/xml發送一個post請求,獲取到crumb。
12.PNG

發送的時候我們需要將Authorization加入到http頭部。

提交build請求

將獲取到的Jenkins-Crumb:XXXXX加入到http頭部,通過發送Get請求調用遠程構建,觸發成功會響應201的狀態碼。
13.png
14.png

關於遠程調用更詳細的文檔說明可以查看Remote access API

通過上面的設置SVN客戶端鈎子遠程構建就完成了,在項目中可以看到遠程構建的標志。

9.PNG

相比SCM輪詢,客戶端遠程構建實時性更高,由於是主動通知,因此代碼提交完立刻可以觸發遠程構建。

SVN服務器鈎子觸發

服務端鈎子與客戶端鈎子類似,具體區別如下。

服務端與客戶端鈎子比較

客戶端鈎子 服務端鈎子
腳本位置 客戶端post-commit鈎子 服務端post-commit鈎子
配置 需要在Build Triggers配置中勾選Trigger builds remotely,設置Authentication Token 需要在Build Triggers配置中勾選輪詢 SCM
防跨域攻擊 支持,需要獲取防跨域攻擊的token 支持,需要獲取防跨域攻擊的token
通知方式 通過Remote access API調用主動構建 通過向Subversion Plugin發送請求主動構建
其他要求 需要安裝Subversion Plugin插件,同時服務端執行腳本需要一些特殊權限

創建服務端鈎子腳本

每個版本庫創建后都會自動生成一些文件夾和文件,hooks文件夾內就是存放了服務器端的鈎子。我們將我們需要的鈎子腳本根據命名規則放入hooks文件夾即可。

24.PNG

windows環境鈎子命名規則為鈎子名.bat或鈎子名.exe,如post-commit.batpost-commit.exe

詳情可以查看官方文檔Implementing Repository Hooks

創建服務端鈎子腳本post-commit.bat

SET REPOS=%1
SET REV=%2
SET CSCRIPT=%windir%\system32\cscript.exe
SET VBSCRIPT=F:\Repositories\JenkinsTest\hooks\post-commit-svn-server.vbs
SET SVNLOOK=D:\Program Files\VisualSVN Server\bin\svnlook.exe
SET JENKINS=http://127.0.0.1:8080/
REM AUTHORIZATION: Set to "" for anonymous acceess
REM AUTHORIZATION: Set to encoded Base64 string, generated from "user_id:api_token" 
REM                found on Jenkins under "user/configure/API token"
REM                User needs "Job/Read" permission on Jenkins
REM AUTHORIZATION=base64(test:1184023ac835f44484f52316235a033db8)
SET AUTHORIZATION="dGVzdDoxMTg0MDIzYWM4MzVmNDQ0ODRmNTIzMTYyMzVhMDMzZGI4"
"%CSCRIPT%" "%VBSCRIPT%" "%REPOS%" "%2" "%SVNLOOK%" %JENKINS% %AUTHORIZATION%

詳細的鈎子可以到SVN服務管理上找到管理hooks
25.png
26.png
同時我們創建了鈎子腳本放入,SVN鈎子管理就可以直接讀取到我們的腳本。
27.PNG

通知腳本參數說明

1. %1:當前項目的SVN倉庫地址。
2. %2:提交后的版本號。
3. CSCRIPT:CScript.exe的路徑。
4. VBSCRIPT:同時jenkins的腳本路徑。
5. SVNLOOK:svnlook.exe的路徑。
6. JENKINS:jenkins服務地址。
7. AUTHORIZATIONN:用於授權token。

svnlook是檢驗Subversion版本庫不同方面的命令行工具。

22.png

graph LR 獲取SVN版本庫的UUID --> 獲取SVN修改項 獲取SVN修改項 --> 獲取Jenkins-Crumb 獲取Jenkins-Crumb-->提交build請求

創建一個vbs腳本用於執行通知。

repos         = WScript.Arguments.Item(0)
Wscript.Echo "repos="&repos
rev           = WScript.Arguments.Item(1)
Wscript.Echo "rev="&rev
svnlook       = WScript.Arguments.Item(2)
Wscript.Echo "svnlook="&svnlook
jenkins       = WScript.Arguments.Item(3)
Wscript.Echo "jenkins="&jenkins
authorization = WScript.Arguments.Item(4)
Wscript.Echo "authorization="&authorization

Set shell = WScript.CreateObject("WScript.Shell")
Set uuidExec = shell.Exec(svnlook & " uuid " & repos)
Do Until uuidExec.StdOut.AtEndOfStream
  uuid = uuidExec.StdOut.ReadLine()
Loop
Wscript.Echo "uuid=" & uuid
Set changedExec = shell.Exec(svnlook & " changed --revision " & rev & " " & repos)
Do Until changedExec.StdOut.AtEndOfStream
  changed = changed + changedExec.StdOut.ReadLine() + Chr(10)
Loop
Wscript.Echo "changed=" & changed
url = jenkins + "crumbIssuer/api/xml?xpath=concat(//crumbRequestField,"":"",//crumb)"
Wscript.Echo "url="&url
Set http = CreateObject("MSXML2.ServerXMLHTTP")
http.open "GET", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
if not authorization = "" then
  http.setRequestHeader "Authorization", "Basic " + authorization
end if
http.send
crumb = null
if http.status = 200 then
  crumb = split(http.responseText,":")
end if
Wscript.Echo crumb(0)&"="&crumb(1)
url = jenkins + "subversion/" + uuid + "/notifyCommit?rev=" + rev
Wscript.Echo url
Set http = CreateObject("MSXML2.ServerXMLHTTP")
http.open "POST", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
if not authorization = "" then
  http.setRequestHeader "Authorization", "Basic " + authorization
end if
if not isnull(crumb) then 
  http.setRequestHeader crumb(0),crumb(1)
end if
http.send changed
if http.status <> 200 then
  Wscript.Echo "Error. HTTP Status: " & http.status & ". Body: " & http.responseText
end if

Windows specific post-commit hook示例使用的是Microsoft.XMLHTTP調用http請求,但是我本機發送會返回403錯誤,查到一篇msxml3.dll 錯誤 80070005 拒絕訪問換為MSXML2.ServerXMLHTTP發送成功。

獲取SVN版本庫的UUID

通過svnlook uuid REPOS-PATH獲取版本庫的唯一UUID

C:\Users\Dm_ca>"D:\Program Files\VisualSVN Server\bin\svnlook.exe" uuid "F:\Repositories\JenkinsTest"
3f64521c-9849-7c44-a469-468730bce0a2

可以看到和SVN版本庫的UUID一致
16.png

獲取SVN版本改變項

通過svnlook changed --revison REV REPOS-PATH獲取版本庫某個版本的改變項

C:\Users\Dm_ca>"D:\Program Files\VisualSVN Server\bin\svnlook.exe" changed --revision 50 "F:\Repositories\JenkinsTest"
U   trunk/JenkinsTest.Core/Jenkins.Core.Test/TestClass.cs
獲取Jenkins-Crumb

客戶端獲取Jenkins-Crumb方式一樣。

提交build請求

與客戶端提交build請求不同,服務端是向http://jenkins-server/subversion/${UUID}/notifyCommit?rev=$REV發送一個post請求。
17.PNG
18.png
服務端構建會顯示SCM啟動,和jenkins scm不同的是,不需要每分鍾定時輪詢,而是通過服務端鈎子觸發任務執行。
19.PNG

三種鈎子比較

SCM輪詢 客戶端鈎子 服務端鈎子
腳本位置 無腳本 客戶端post-commit鈎子 服務端post-commit鈎子
配置 需要在Build Triggers配置中勾選輪詢 SCM,在Schedule配置輸入計划規則 需要在Build Triggers配置中勾選Trigger builds remotely,設置Authentication Token 需要在Build Triggers配置中勾選輪詢 SCM
防跨域攻擊 無需考慮 支持,需要獲取防跨域攻擊的token 支持,需要獲取防跨域攻擊的token
通知方式 定時輪詢 通過Remote access API調用主動構建 通過向Subversion Plugin發送請求主動構建
時效性 最快代碼提交后1分鍾觸發 立即觸發 立即觸發
其他要求 需要安裝Subversion Plugin插件,同時服務端執行腳本需要一些特殊權限

具體使用哪種方案根據上面表格選擇即可。

結語

最終我們的完整持續集成流程圖如下圖所示

graph LR 獲取代碼 --> 編碼 編碼 --> 提交代碼 提交代碼 --> |自動構建| 編譯程序集 編譯程序集 --> 編譯單元測試程序集 編譯單元測試程序集 --> |通過| 執行單元測試 編譯單元測試程序集 --> |不通過| 失敗 執行單元測試 --> |通過| 創建nuget包 創建nuget包 --> 上傳nuget包 執行單元測試 --> |不通過| 失敗 上傳nuget包 --> 清理編譯文件夾 失敗 --> 清理編譯文件夾 失敗 -.-> 獲取代碼

參考文檔

  1. 使用TortoiseSVN的客戶端鈎子腳本觸發Jenkins構建
  2. Jenkins SVN自動構建
  3. SVN怎么觸發Jenkins自動構建
  4. msxml3.dll 錯誤 80070005 拒絕訪問
  5. 通過jenkins API去build一個job
  6. Remote access API
  7. Implementing Repository Hooks
  8. Windows specific post-commit hook

本文地址:https://www.cnblogs.com/Jack-Blog/p/10331263.html
作者博客:傑哥很忙
歡迎轉載,請在明顯位置給出出處及鏈接


免責聲明!

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



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