对应英文文档:https://jenkins.io/doc/book/pipeline/jenkinsfile/
本系列主贴直达:https://testerhome.com/topics/11265
这部分基于前面Getting Started所讲过的信息,介绍更有用的步骤 (steps),一般模式,一些非试用的Jenkinsfile例子。
创建一个Jenkinsfile,然后把这个文件保存到源代码版本控制中 (例如 svn,git 等),会带来许多好处:
Pipeline 支持两种语法: 声明式 (在 Pipeline2.5 中引入) 和脚本 Pipeline。两者都支持构建持续集成的 Pipeline。都可以被用来定义一个 Pipeline,不管是在 web UI 上还是在Jenkinsfile文件中,一般认为最好的实践是创建一个Jenkinsfile文件,并放到源代码管理库中。
像前面Getting Started章节讨论的一样,Jenkinsfile文件是一个文本文件,包含一个 Jenkins Pipeline 定义。考虑如下部分,实现了一个三阶段的持续集成 pipeline。
并不是所有的 Pipelines 都有同样的三个阶段,但是对于大多数项目来说,这是一个很好的开始。下面的部分将说明一个简单的 Pipeline 的创建和执行。
注意:假设已经为项目创建好了源代码版本库,并且一个 Pipeline 已经按照前面 Getting Started 步骤定义好了。
用一个支持 Groovy 语法高亮的文本编辑器,创建一个新的 Jenkinsfile 文件,放到项目的根目录下。
上面声明式的 Pipeline 例子包含实现一个持续集成 pipeline 最小的必须的结构。Agent 指示符是必须的,告诉 Jenkins 为这个 pipeline 分配一个执行器和工作空间。没有 agent 指示符,不仅申明式的 Pipeline 不是合法的,而且它也不能做任何工作!默认情况下,agent 指示符确保源代码库代码被签出,并且是有效的可以被后面阶段的步骤 (steps) 使用。
对于一个有效的申明式的 Pipeline,stage 指示符和 steps 指示符同样是必须的,因为它们告诉 Jenkins 执行什么,哪个阶段做什么事情。
对于脚本式 Pipeline 的更高级用法,上面例子中的 node 是至关重要的第一步,因为它为 Pipeline 申请了执行器和工作空间。确切的说,没有 node,一个 Pipeline 不能做任何工作!在 node 里面,业务的第一个命令将是迁出源代码。因为 Jenkinsfile 文件是直接从源代码库中迁出,所以 Pipeline 提供了一个快速和容易的方式存取正确的源代码版本。
① checkout步骤将从源代码库中迁出代码;scm 是一个特殊的变量,它将告诉 checkout 克隆特定的版本,然后触发 Pipeline 运行。
注意,笔者实测如下:
在脚本式 Pipeline 中
checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[url: 'https://gitee.com/roclli/simple-maven-project-with-tests.git']]])。
申明式 Pipeline(Jenkinsfile 中),采用 checkout scm,会导致 checkout 两次,根据说明 Specify where to obtain a source code repository containing your Groovy script. It will be checked out somewhere on the Jenkins master and used to load your Pipeline script. (If you wish to use other files from the same repository during your Pipeline, you will need to check them out separately on some slave; this checkout cannot be reused.)。
许多使用 Pipeline 的项目开始工作第一步都是” 编译 (Build)” 阶段。通常情况下, Pipeline 的此阶段将是源代码的组装、编译或打包。Jenkinsfile 文件并不是替代现有的编译工具例如 GNU/Make,Maven,Gradle 等等,但可以看作是一个粘合层, 以粘合绑定 (bind) 项目开发的多个阶段生命周期 (构建、测试、部署等) 一起。
Jenkins 有许多插件,可以调用几乎所有的构建工具,但是这个例子将简单的使用 shell 步骤 (sh) 调用 make 命令。sh 假设系统是 Unix/Linux,如果是 Windows 系统,将会使用 bat。
① sh调用make命令,将一直运行,如果命令返回 0。任何非 0 的返回值都将使 Pipeline 失败。
② archiveArtifacts捕获模式匹配 (**/target/*.jar) 文件,并且把他们保存到 Jenkins 的 master 中。
提示:存档文件不是使用外部文件资料库 (如 Artifactory 或 Nexus) 的替代品, 应该只考虑基本的报告和档案存档。
运行自动测试是任何成功持续集成的关键组件。因此 Jenkins 有许多测试记录,报告,可视化插件。一般来说, 当有测试失败, 它是有用的,Jenkins 会记录 web UI 中的报告和可视化失败。下面的示例使用 junit 步骤, 由junit 插件提供。
下面的例子,如果运行失败,Pipeline 被标记不稳定” unstable”,在网页上用一个黄色的球标记。基于记录的测试报告,Jenkins 同样提供历史趋势分析和可视化报告。
① 使用内置的 shell 条件 (sh ‘make || true’) 确保sh总是有一个 0 的返回值,这样使得junit机会去捕获和处理测试报告。或者使用下面的处理失败 [handing-failures] 部分。
② Junit 捕获并关联 Junit XML 文件,模式匹配 (**/target/*.xml)
注意:此处笔者测试下来是有出入的,区别在于脚本式 Pipeline
部署可能意味着各种步骤, 具体取决于项目或组织的要求,可能是从将生成的文件发布到 Artifactory 服务器的任何东西, 或者将代码推送到生产系统。
在示例 Pipeline 的这个阶段,” Build” 和” Test” 阶段都已成功执行。因此发布阶段假设前面的阶段已经成功执行,否则 Pipeline 将退出。
① 读取 currentBuild.result 变量允许 Pipeline 判断是否有失败。这种情况之下,值是 UNSTABLE。
假设示例的 Jenkins Pipeline 都被成功执行,每一个成功的 Pipeline 部分都将会关联到存档文件,测试结果报告和控制台输出。
脚本式的 Pipeline 能包含条件测试 (像上面所示),循环 (loop),try/catch/finally 块,甚至函数。接下来的部分将详细讲解脚本式 Pipeline 语法的高级用法。
注意:笔者测试下来
(1).期间发现过无法读取 currentBuild.result 变量,此时提示” Cannot get property 'currentBuild' on null object”,故忽略此处的 Deploy。env 变量也是如此提示:Cannot get property 'env' on null object。最后实测发现:
脚本式 Pipeline(web UI 上面输入),可以正确输出变量值,如下方式 ${env.BUILD_NUMBER}
申明式 Pipeline(Jenkinsfile),可以正确输出变量值,如下方式 ${env.BUILD_NUMBER}
可以正确用 echo 输出值,也可以用 if 判断值。代码如下:
echo "---${env.BUILD_NUMBER}---"
echo "---${currentBuild.result}---"
if(currentBuild.result == null || currentBuild.result == 'SUCCESS') {
echo "---currentBuild.result is:${currentBuild.result}------"
}
else {
echo "---currentBuild.result is:${currentBuild.result},so, will make publish"
}
得到的结果如下几种情况:
Cases 结果 | 全局变量 | 备注 |
---|---|---|
1 个 skip,其他全对 | 61/null | |
1 个 error,其他全对 | 60/UNSTABLE | 根据前面说明,只要有失败,就是 UNSTABLE |
全部正确 | 59/null |
(2).申明式 Jenkins Pipeline(Jenkinsfile) 中,使用 steps 有报错提示:
WorkflowScript: 31: Expected a step @ line 31, column 17.。解决办法:添加 script 符号
stage('Deploy'){
steps {
script{
echo "---${env.BUILD_NUMBER}---"
echo "---${currentBuild.result}---"
if(currentBuild.result == null || currentBuild.result == "SUCCESS") {
echo "---currentBuild.result is:${currentBuild.result}------"
}
else {
echo "---currentBuild.result is:${currentBuild.result},so, will make publish"
}
}
}
}
Jenkins Pipeline 使用与 Groovy 相同的规则来进行字符串插值。Groovy 的字符串插值规则可能会让这个语言的初学者感到混乱。因为 Groovy 支持申明一个字符串使用单引号或者双引号,例如:
只有后一个字符串将支持基于美元符号 ($) 的字符串插值, 例如:
运行结果是:
笔者加:测试运行结果如下:
Hello Mr. ${username}
I said, Hello Mr. Jenkins
对于 Pipeline 的高级特性,理解怎样使用字符串插值是很重要。
Jenkins Pipeline 通过全局变量 env 暴露了许多环境变量,env 可以在 Jenkindfile 中任何地方使用。Jenkins 中完整的环境变量列表请参见:localhost:8080/pipeline-syntax/globals#env(假设 Jenkins 运行在本地的 8080 端口),环境变量包括:
当前的 build ID,同 BUILD_NUMBER 一致。
被执行的项目的名字,例如 foo 或者 foo/bar。
Jenkins 的全 URL,例如 example.com:port/Jenkins/(注意:只有” System COnfiguration” 中设置 Jenkins URL 以后才有效)
引用或使用这些环境变量可以实现, 如访问 Groovy 映射, 例如:
在申明式 Pipeline 或者脚本式 Pipeline 中设置环境变量是不同的。
申明式 Pipeline 支持环境申明,而脚本式 Pipeline 必须使用 withEnv。
① 一个environment申明使用在 pipeline 块中,将对pipeline中的所有 step 有效。
② 在stage中的environment申明将只对 stage 内的 step 有效。
声明式 Pipeline 支持现成的参数, 允许 Pipeline 接受用户通过参数指令在运行时指定参数。在脚本式 Pipeline 中配置参数,可以通过properties实现,关于 properties 我们可以在 Snippet Generator 中找到。
如果想要配置你的 Pipeline 接受 Build With parameters 的参数,那些参数是可以通过params变量读取。
假设一个名叫 Greeting 的字符串变量被配置在Jenkinsfile中,那么可以通过${params.Greeting}读取。
注意:笔者测试如下:本例如果是 script(web UI 上直接输入脚本方式) 执行,会出现参数输入
如果是 Jenkisnfile(声明式执行),会直接使用默认值 Hello。
申明式 Pipeline 支持健壮的错误处理,默认通过它的 post section 处理,post section 允许申明许多不同的” post conditions”,例如always,unstable,success,failure,和changed。Pipeline
语法 (Pipeline Syntax) 部分提供更多的细节,怎样使用不同的 post 情况。
脚本式 Pipeline 依赖于 Groovy 内嵌的 try/catch/finally 语法处理 Pipeline 执行期间出现的错误。
在上面的测试例子中,sh 被修改永远不返回非 0 值 (sh ‘make check || true’)。这个方法意味着接下来的阶段需要检查 currentBuild.result 去判断是否存在错误发生。
另一种处理此问题的方法是使用一系列 try/finally 块, 它可以保留 Pipeline 中故障的早期退出行为, 同时仍使 junit 有机会捕获测试报告:
注意:笔者实测,申明式添加了 post;其次 mail 的各参数,必须用单引号括起来。
申明式 (Jenkinsfile) 邮件发送失败,以下两种方式都失败 (脚本式 Pipeline 无邮件部分):
如果前面执行成功,那么就不会执行 post 里面的 failure 部分。
在所有前面的例子中,只使用一个代理。这意味着 Jenkins 分配的所有执行器,只有一个是有效的,无论它是如何被标记和配置。Pipeline 允许在一个 Jenkinsfile 中使用多个代理,这对于高级的用户案例是很帮助的,例如在多个平台上执行 builds/tests。
在下面的例子中,Build 阶段在一个代理上执行,build 的结果将被两个子代理重用,在 test 阶段,标记 linux 和 windows 的两个代理是相互独立的。
① stash 允许捕获模式匹配的文件 (/target/.jar),只在同一个 Pipeline 其他地方重用。一旦 Pipeline 完成了执行,stash 捕获的文件将被 Jenkins master 删除。
② 在 agent/node 中的参数允许任何有效的 Jenkins 标签表达式。查阅” Pipeline Syntax” 部分可以更详细的信息。
③ unstash 将从 Jenkins 主机中检索 stash 到 Pipeline 的当前工作区中。
④ bat 脚本允许在 windows 平台上执行脚本。
注意:笔者实测
(1).首先需要配置 windows slave node,关于 windows slave,需要配置 master 上已有的需要用到的环境变量:
(2).首先分别修改两个 node 的 label 为 linux(master) 和 windows(slave)。
Pipeline 遵循 Groovy 语言约定, 允许在方法参数周围省略括号。
许多 Pipeline steps 还使用命名参数语法作为在 Groovy 中创建映射的简写形式, 它使用语法 [key1: value1、key2: value2]。使语句像以下功能等效:
方便起见,当参数只有一个参数的时候,参数名可以省略,例如:
脚本 Pipeline 是一个 domain-specific 语言,基于 Groovy,大多数的 Groovy 语法都能不用修改被用在脚本 Pipeline 中。
上面部分中的示例在一个线性序列中跨两个不同的平台运行测试。实践中,如果 make check 需要花 30 分钟执行完,” Test” 阶段将花费 60 分钟完成。
幸运的是,Pipeline 具有内置的功能, 用于并行执行 Pipeline 脚本部分, 并在恰当命名的并行 (parallel) 步骤中实现。
重构上面的示例以使用并行 (parallel) 步骤:
不像原先的在 linux 和 windows 上线性执行,他们现在可以并行执行。
本系列主贴直达:https://testerhome.com/topics/11265