zoukankan      html  css  js  c++  java
  • 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)Jenkinsworkspaceunittest>exit 0 
    17:53:29 [unittest] $ cmd /c call C:WINDOWSTEMPjenkins3052083372263337733.bat
    17:53:29 
    17:53:29 D:Program Files (x86)Jenkinsworkspaceunittest>E:开发工具VS开发工具VS插件
    uget.exe pack Jenkins.Core/Jenkins.Core.csproj -Properties Configuration=Release -OutputDirectory Jenkins.CoreinRelease 
    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 Studio2017EnterpriseMSBuild15.0in'.
    17:53:31 正在打包“D:Program Files (x86)JenkinsworkspaceunittestJenkins.CoreinRelease
    et45”中的文件。
    17:53:31 警告: NU5115: 未指定 Description。正在使用“Description”。
    17:53:31 Successfully created package 'D:Program Files (x86)JenkinsworkspaceunittestJenkins.CoreinReleaseJenkins.Core.0.5.0.nupkg'.
    17:53:32 
    17:53:32 D:Program Files (x86)Jenkinsworkspaceunittest>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.CoreinReleaseJenkins.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%system32cscript.exe
    SET VBSCRIPT=F:RepositoriesJenkinsTesthookspost-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%system32cscript.exe
    SET VBSCRIPT=F:RepositoriesJenkinsTesthookspost-commit-svn-server.vbs
    SET SVNLOOK=D:Program FilesVisualSVN Serverinsvnlook.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:UsersDm_ca>"D:Program FilesVisualSVN Serverinsvnlook.exe" uuid "F:RepositoriesJenkinsTest"
    3f64521c-9849-7c44-a469-468730bce0a2
    

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

    获取SVN版本改变项

    通过svnlook changed --revison REV REPOS-PATH获取版本库某个版本的改变项

    C:UsersDm_ca>"D:Program FilesVisualSVN Serverinsvnlook.exe" changed --revision 50 "F:RepositoriesJenkinsTest"
    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
    作者博客:杰哥很忙
    欢迎转载,请在明显位置给出出处及链接

  • 相关阅读:
    Wix打包系列(一)如何使用wix制作安装程序
    服务器修改 ssh 22端口
    服务器遭受 ssh 攻击
    angularJS 系列(六)---$emit(), $on(), $broadcast()的使用
    angularJS 系列(五)--controller AS 语法
    模仿 BootstrapValidator 自制 模块化 表单验证
    formidable 模块化开发 代码拆分(解耦) nodejs图片服务器架构
    使用 原生js 制作插件 (javaScript音乐播放器)
    mui.fire() 和 mui.trigger()
    web 前端 常见操作 将时间戳转成日期格式 字符串截取 使用mui制作选项卡
  • 原文地址:https://www.cnblogs.com/Jack-Blog/p/10331263.html
Copyright © 2011-2022 走看看