目录

* Jenkins持续集成学习-Windows环境进行.Net开发3
<https://www.cnblogs.com/Jack-Blog/p/10331263.html#jenkins持续集成学习-windows环境进行.net开发3>
* 目录 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#目录>
* 前言 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#前言>
* 目标 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#目标>
* 优化nuget包生成流程
<https://www.cnblogs.com/Jack-Blog/p/10331263.html#优化nuget包生成流程>
* 自动触发构建 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#自动触发构建>
* Jenkins定时轮询触发
<https://www.cnblogs.com/Jack-Blog/p/10331263.html#jenkins定时轮询触发>
* SVN客户端钩子触发 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#svn客户端钩子触发>
* SVN服务器钩子触发 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#svn服务器钩子触发>
* 三种钩子比较 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#三种钩子比较>
* 结语 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#结语>
* 参考文档 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#参考文档>
Jenkins持续集成学习-Windows环境进行.Net开发3

目录

Jenkins持续集成学习-Windows环境进行.Net开发1
<https://www.cnblogs.com/Jack-Blog/p/10291612.html>
Jenkins持续集成学习-Windows环境进行.Net开发2
<https://www.cnblogs.com/Jack-Blog/p/10316116.html>
Jenkins持续集成学习-Windows环境进行.Net开发3
<https://www.cnblogs.com/Jack-Blog/p/10331263.html>
Jenkins持续集成学习-Windows环境进行.Net开发4
<https://www.cnblogs.com/Jack-Blog/p/10346032.html>
Jenkins持续集成学习-搭建jenkins问题汇总 <https://www.cnblogs.com/Jack-Blog/p/10439325.html>

前言

在前面两篇文章介绍了关于持续集成的完整主流程。



目标

在上一篇文章中我们完成了主流程的持续集成,但是提交代码仍然需要手动点击构建,本篇文章就来探究如何做到SVN代码提交后自动构建。

优化nuget包生成流程

在开始之前我需要解决上一篇文章理解有误的一个问题。
在上一章我们将单元测试的不稳定错误等级设置为1。



当我添加多个失败的单元测试时,我发现1次单元测试失败错误等级就会加1,我增加了一共11个失败的单元测试,因此单元测试失败返回值为11。



因此上次的逻辑就行不通了,编译的时候自动创建nuget包,不稳定版本删除nuget包,这样只能将错误等级设置的非常大。比如int.Max,否则失败会导致删除脚本不执行。
因此我们有两种选择:
1. 编译的时候自动创建nuget包, 单元测试将不稳定的ERRORLEVEL设置的非常大,单元测试失败都可以认为是不稳定,然后自动删除nuget包。 2.
编译的时候不自动创建nuget包,单元测试通过后再调用脚本创建nuget包。
我们优化一下使用第二种方法生成nuget包。

我们将项目中自动生成nuget包的勾去除


或者我们修改csproj的GeneratePackageOnBuild节点值,改为false,则编译的时候也不会自动创建nuget包。


然后我们修改单元测试ERRORLEVEL,单元测试失败了就不再执行后续Build的流程,在单元测试成功时创建Nuget包。


通过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
此时流程优化如下


graph LR 编译程序集 --> 编译单元测试程序集 编译单元测试程序集 --> |通过| 执行单元测试 编译单元测试程序集 --> |不通过| 失败
执行单元测试 --> |通过| 创建nuget包 创建nuget包 --> 上传nuget包 执行单元测试 --> |不通过| 失败 上传nuget包 -->
清理编译文件夹 失败 --> 清理编译文件夹
自动触发构建

SVN自动触发构建一共有3种方式。

* 分别为Jenkins定时轮询触发。
* SVN客户端创建钩子触发。
* SVN服务器端创建钩子触发。
Jenkins定时轮询触发

Jenkins定时轮询触发是使用Jenkins 轮询SCM功能定时检查SVN是否有变更触发构建。

Jenkins的轮询SCM的说明上提到该功能需要扫描整个Jenkins工作区并验证,操作性能要求比较高。我们依然验证一下这个功能。

在配置Build Triggers选项中勾选轮询SCM,在Schedule输入 * * * * *表示每分钟轮询一次,即代码提交后1分钟触发构建。


设置完之后我们提交代码就会自动构建了。相比手动构建,自动构建左边菜单栏会显示轮询日志,右边会显示由SCM变更启动,表明是轮询SCM触发的构建。



SVN客户端钩子触发

SVN客户端钩子触发是在本地提交的时候执行本地的Post-Commit钩子,通过这个钩子执行脚本使用http请求调用jenkins的远程构建接口。

配置

生成用户授权Token

在系统配置-管理用户-用户-配置的API TOKEN点击生成新的Token按钮,创建一个token。我们需要根据这个token来获取权限。


增加项目授权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远程构建


创建客户端钩子脚本


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客户端的设置中找到钩子脚本,点击添加。


设置路径和脚本路径,注意左下角两项勾起来。



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。


发送的时候我们需要将Authorization加入到http头部。

提交build请求

将获取到的Jenkins-Crumb:XXXXX加入到http头部,通过发送Get请求调用远程构建,触发成功会响应201的状态码。



关于远程调用更详细的文档说明可以查看Remote access API
<https://wiki.jenkins.io/display/JENKINS/Remote+access+API>

通过上面的设置SVN客户端钩子远程构建就完成了,在项目中可以看到远程构建的标志。



相比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文件夹即可。



windows环境钩子命名规则为钩子名.bat或钩子名.exe,如post-commit.bat或post-commit.exe。

详情可以查看官方文档Implementing Repository Hooks
<http://svnbook.red-bean.com/en/1.5/svn.reposadmin.create.html#svn.reposadmin.create.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


同时我们创建了钩子脚本放入,SVN钩子管理就可以直接读取到我们的脚本。


通知脚本参数说明
1. %1:当前项目的SVN仓库地址。 2. %2:提交后的版本号。 3. CSCRIPT:CScript.exe的路径。 4.
VBSCRIPT:同时jenkins的脚本路径。 5. SVNLOOK:svnlook.exe的路径。 6. JENKINS:jenkins服务地址。 7.
AUTHORIZATIONN:用于授权token。
svnlook是检验Subversion版本库不同方面的命令行工具。


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
<https://wiki.jenkins.io/display/JENKINS/Subversion+Plugin#SubversionPlugin-Windowsspecificpost-commithook>
示例使用的是Microsoft.XMLHTTP调用http请求,但是我本机发送会返回403错误,查到一篇msxml3.dll 错误 80070005 拒绝访问
<https://www.jb51.net/article/26182.htm>换为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一致


获取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 <https://www.cnblogs.com/Jack-Blog/p/10331263.html#创建通知脚本>
方式一样。

提交build请求

与客户端提交build请求不同,服务端是向
http://jenkins-server/subversion/${UUID}/notifyCommit?rev=$REV发送一个post请求。


服务端构建会显示SCM启动,和jenkins scm不同的是,不需要每分钟定时轮询,而是通过服务端钩子触发任务执行。


三种钩子比较

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包 --> 清理编译文件夹 失败 --> 清理编译文件夹
失败 -.-> 获取代码
参考文档

* 使用TortoiseSVN的客户端钩子脚本触发Jenkins构建
<https://blog.reohou.com/use-tortoisesvn-local-hook-script-to-trigger-jenkins-build/>
* Jenkins SVN自动构建 <https://blog.csdn.net/gowhere_/article/details/78615559>
* SVN怎么触发Jenkins自动构建 <http://blog.51cto.com/techsnail/2142599>
* msxml3.dll 错误 80070005 拒绝访问 <https://www.jb51.net/article/26182.htm>
* 通过jenkins API去build一个job <https://www.cnblogs.com/jwentest/p/8204421.html>
* Remote access API
<https://wiki.jenkins.io/display/JENKINS/Remote+access+API>
* Implementing Repository Hooks
<http://svnbook.red-bean.com/en/1.5/svn.reposadmin.create.html#svn.reposadmin.create.hooks>
* Windows specific post-commit hook
<https://wiki.jenkins.io/display/JENKINS/Subversion+Plugin#SubversionPlugin-Windowsspecificpost-commithook>
本文地址:https://www.cnblogs.com/Jack-Blog/p/10331263.html
<https://www.cnblogs.com/Jack-Blog/p/10331263.html>
作者博客:杰哥很忙
欢迎转载,请在明显位置给出出处及链接

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信