- Published on
《Jenkins 2.x实践指南》读书笔记-Jenkins 2.x pipeline语法
- Authors
- Name
- 老杨的博客
[TOC]
1. 大概了解 Groovy
可以通过Groovy 教程来了解。
2. pipeline 介绍
Jenkins pipeline 其实就是基于 Groovy 语言实现的一种 DSL(领域特定语言),用于描述整条流水线是如何进行的。流水线的内容包括执行编译、打包、测试、输出测试报告等步骤。
2.1 pipeline 最简结构
pipeline {
agent any
stages {
stage('Stage 1') {
steps {
echo 'Hello world!'
}
}
}
}
pipeline
:代表整条流水线,包含整条流水线的逻辑。stage
部分:阶段,代表流水线的阶段。每个阶段都必须有名称。本例中,build
就是此阶段的名称。stages
部分:流水线中多个stage
的容器。stages
部分至少包含一个stage
。steps
部分:代表阶段中的一个或多个具体步骤(step
)的容器。steps
部分至少包含一个步骤,本例中,echo
就是一个步骤。在一个stage
中有且只有一个steps
。agent
部分:指定流水线的执行位置(Jenkins agent)。流水线中的每个阶段都必须在某个地方(物理机、虚拟机或 Docker 容器)执行,agent
部分即指定具体在哪里执行。
更多更详细 pipeline 步骤参考文档: https://jenkins.io/zh/doc/pipeline/steps/
以上每一个部分(section)都是必需的,少一个,Jenkins 都会报错。
众所周知,jenkins 好用最大体现它的众多插件满足各种需求。并不是所有的插件都支持 pipeline 的。 jenkins 插件兼容 pipeline 列表: https://github.com/jenkinsci/pipeline-plugin/blob/master/COMPATIBILITY.md
post
2.2 post
部分包含的是在整个 pipeline 或阶段完成后一些附加的步骤。post
部分是可选的,所以并不包含在 pipeline 最简结构中。但这并不代表它作用不大。 根据 pipeline 或阶段的完成状态,post
部分分成多种条件块,包括:
always
:不论当前完成状态是什么,都执行。changed
:只要当前完成状态与上一次完成状态不同就执行。fixed
:上一次完成状态为失败或不稳定(unstable
),当前完成状态为成功时执行。regression
:上一次完成状态为成功,当前完成状态为失败、不稳定或中止(aborted
)时执行。aborted
:当前执行结果是中止状态时(一般为人为中止)执行。failure
:当前完成状态为失败时执行。success
:当前完成状态为成功时执行。unstable
:当前完成状态为不稳定时执行。cleanup
:清理条件块。不论当前完成状态是什么,在其他所有条件块执行完成后都执行。post
部分可以同时包含多种条件块。以下是post
部分的完整示例。
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'echo Build stage ...'
}
post {
always {
echo "post condition executed: always ..."
}
changed {
echo "post condition executed: changed ..."
}
aborted {
echo "post condition executed: aborted ..."
}
regression {
echo "post condition executed: regression ..."
}
}
}
stage('Test'){
steps {
sh 'echo Test stage ...'
}
post {
aborted {
echo "post condition executed: aborted ..."
}
failure {
echo "post condition executed: failure ..."
}
success {
echo "post condition executed: success ..."
}
}
}
stage('Deploy') {
steps {
sh 'echo Deploy stage ...'
}
}
}
post {
unstable {
echo "post condition executed: unstable ..."
}
unsuccessful {
echo "post condition executed: unsuccessful ..."
}
cleanup {
echo "post condition executed: cleanup ..."
}
}
}
2.3 pipeline 支持的指令
显然,基本结构满足不了现实多变的需求。所以,Jenkins pipeline 通过各种指令(directive)来丰富自己。指令可以被理解为对 Jenkins pipeline 基本结构的补充。 Jenkins pipeline 支持的指令有:
environment
:用于设置环境变量,可定义在stage
或pipeline
部分。tools
:可定义在pipeline
或stage
部分。它会自动下载并安装我们指定的工具,并将其加入PATH
变量中。input
:定义在stage
部分,会暂停pipeline
,提示你输入内容。options
:用于配置 Jenkins pipeline 本身的选项,比如options {retry(3)}
指当pipeline
失败时再重试 2 次。options
指令可定义在stage
或pipeline
部分。parallel
:并行执行多个step
。在pipeline
插件 1.2 版本后,parallel
开始支持对多个阶段进行并行执行。parameters
:与input
不同,parameters
是执行pipeline
前传入的一些参数。triggers
:用于定义执行pipeline
的触发器。when
:当满足when
定义的条件时,阶段才执行。
在使用指令时,需要注意的是每个指令都有自己的“作用域”。如果指令使用的位置不正确,Jenkins 将会报错。
2.4 配置 pipeline 本身
2.4.1 全局 options
options
指令允许从流水线内部配置特定于流水线的选项。 流水线提供了许多这样的选项, 比如 buildDiscarder
,但也可以由插件提供, 比如 timestamps
.
Required | No |
---|---|
Parameters | None |
Allowed | Only once, inside the pipeline block. |
可用选项
buildDiscarder 为最近的流水线运行的特定数量保存组件和控制台输出。例如: options { buildDiscarder(logRotator(numToKeepStr: '1')) }
disableConcurrentBuilds 不允许同时执行流水线。 可被用来防止同时访问共享资源等。 例如: options { disableConcurrentBuilds() }
overrideIndexTriggers 允许覆盖分支索引触发器的默认处理。 如果分支索引触发器在多分支或组织标签中禁用, options { overrideIndexTriggers(true) }
将只允许它们用于促工作。否则, options { overrideIndexTriggers(false) }
只会禁用改作业的分支索引触发器。
skipDefaultCheckout 在agent
指令中,跳过从源代码控制中检出代码的默认情况。例如: options { skipDefaultCheckout() }
skipStagesAfterUnstable 一旦构建状态变得 UNSTABLE,跳过该阶段。例如: options { skipStagesAfterUnstable() }
checkoutToSubdirectory 在工作空间的子目录中自动地执行源代码控制检出。例如: options { checkoutToSubdirectory('foo') }
timeout 设置流水线运行的超时时间, 在此之后,Jenkins 将中止流水线。例如: options { timeout(time: 1, unit: 'HOURS') }
retry 在失败时, 重新尝试整个流水线的指定次数。 例如: options { retry(3) }
timestamps 预谋所有由流水线生成的控制台输出,与该流水线发出的时间一致。 例如: options { timestamps() }
newContainerPerStage 当agent
为docker
或dockerfile
时,指定在同一个 Jenkins 节点上,每个stage
都分别运行在一个新的容器中,而不是所有stage
都运行在同一个容器中。例如: options { newContainerPerStage() }
Example
pipeline {
agent any
options {
timeout(time: 1, unit: 'HOURS')
}
stages {
stage('Example') {
steps {
echo 'Hello World'
}
}
}
}
指定一个小时的全局执行超时, 在此之后,Jenkins 将中止流水线运行。
2.4.2 阶段 option
stage
的 options
指令类似于流水线根目录上的 options
指令。然而, stage
-级别 options
只能包括 retry
, timeout
, 或 timestamps
等步骤, 或与 stage
相关的声明式选项,如 skipDefaultCheckout
。
在stage
, options
指令中的步骤在进入 agent
之前被调用或在 when
条件出现时进行检查。
可选的阶段选项
skipDefaultCheckout 在 agent
指令中跳过默认的从源代码控制中检出代码。例如: options { skipDefaultCheckout() }
timeout 设置此阶段的超时时间, 在此之后, Jenkins 会终止该阶段。 例如: options { timeout(time: 1, unit: 'HOURS') }
retry 在失败时, 重试此阶段指定次数。 例如: options { retry(3) }
timestamps 预谋此阶段生成的所有控制台输出以及该行发出的时间一致。例如: options { timestamps() }
Example
pipeline {
agent any
stages {
stage('Example') {
options {
timeout(time: 1, unit: 'HOURS')
}
steps {
echo 'Hello World'
}
}
}
}
指定 Example 阶段的执行超时时间, 在此之后,Jenkins 将中止流水线运行。
2.4.3 在声明式 pipeline 中使用脚本
声明式 pipeline 是不能直接在steps
块中写 Groovy 代码。 Jenkins pipeline 专门提供了一个script
步骤,你能在script
步骤中像写代码一样写 pipeline 逻辑。
pipeline {
agent any
stages {
stage('Build') {
steps {
script {
result = sh (script: "git log -1|grep 'Release'", returnStatus: true)
echo "result: ${result}"
}
}
}
}
}
在 script 块中的其实就是 Groovy 代码。大多数时候,我们是不需要使用script
步骤的。如果在script
步骤中写了大量的逻辑,则说明你应该把这些逻辑拆分到不同的阶段,或者放到共享库中。共享库是一种扩展 Jenkins pipeline 的技术。
2.5 pipeline 内置基础步骤
这里介绍 pipeline 内置的一些步骤。
2.5.1 文件目录相关步骤
deleteDir 删除当前目录,它是一个无参步骤,删除的是当前工作目录。通常它与dir
步骤一起使用,用于删除指定目录下的内容。
dir 切换到目录。默认 pipeline 工作在工作空间目录下,dir
步骤可以让我们切换到其它目录。例如:dir("/var/logs") { deleteDir() }
fileExists 判断文件是否存在。fileExists('/tmp/a.jar')
判断/tmp/a.jar
文件是否存在。如果参数是相对路径,则判断在相对当前工作目录下,该文件是否存在。结果返回布尔类型。
isUnix 判断是否为类 Unix 系统。如果当前 pipeline 运行在一个类 Unix 系统上,则返回true
。
pwd 确认当前目录。pwd
与 Linux 的pwd
命令一样,返回当前所在目录。它有一个布尔类型的可选参数:tmp
,如果参数值为true
,则返回与当前工作空间关联的临时目录。
writeFile 将内容写入指定文件中。 writeFile
支持的参数有:
file
:文件路径,可以是绝对路径,也可以是相对路径。text
:要写入的文件内容。encoding
(可选):目标文件的编码。如果留空,则使用操作系统默认的编码。如果写的是 Base64 的数据,则可以使用 Base64 编码。
readFile 读取指定文件的内容,以文本返回。 readFile
支持的参数有:
file
:路径,可以是绝对路径,也可以是相对路径。encoding
(可选):读取文件时使用的编码。
script {
// "amVua2lucyBib29r" 是"jenkins book"进行Base64编码后的值
writeFile(file: "base64File", text: "amVua2lucyBib29r", encoding: "Base64")
def content = readFile(file: "base64File", encoding: "UTF-8")
echo "${content}"
// 打印结果: jenkins book
}
2.5.2 制品相关步骤
stash 保存临时文件。 stash
步骤可以将一些文件保存起来,以便被同一次构建的其他步骤或阶段使用。如果整个 pipeline 的所有阶段在同一台机器上执行,则stash
步骤是多余的。所以,通常需要stash
的文件都是要跨 Jenkins node 使用的。
stash
步骤会将文件存储在tar
文件中,对于大文件的stash
操作将会消耗 Jenkins master 的计算资源。Jenkins 官方文档推荐,当文件大小为 5∼100MB 时,应该考虑使用其他替代方案。
stash
步骤的参数列表如下:
name
:字符串类型,保存文件的集合的唯一标识。allowEmpty
:布尔类型,允许stash
内容为空。excludes
:字符串类型,将哪些文件排除。如果排除多个文件,则使用逗号分隔。留空代表不排除任何文件。includes
:字符串类型,stash
哪些文件,留空代表当前文件夹下的所有文件。useDefaultExcludes
:布尔类型,如果为true
,则代表使用 Ant 风格路径默认排除文件列表。
除了name
参数,其他参数都是可选的。excludes
和includes
使用的是 Ant 风格路径表达式。
unstash 取出之前 stash 的文件。 unstash
步骤只有一个name
参数,即stash
时的唯一标识。通常stas
h 与unstash
步骤同时使用。以下是完整示例。
pipeline {
agent none
stages {
stage('stash') {
agent { label "master" }
steps {
script {
writeFile file: "a.txt", text: "$BUILD_NUMBER"
stash(name: "abc", include: "a.txt")
}
}
}
stage("unstash") {
agent { label "node2" }
steps {
script {
unstash("abc")
def content = readFile("a.txt")
echo "${content}"
}
}
}
}
}
stash
步骤在 master 节点上执行,而unstash
步骤在 node2 节点上执行。
2.5.3 命令相关步骤
与命令相关的步骤其实是 Pipeline:Nodes and Processes 插件提供的步骤。由于它是 Pipeline 插件的一个组件,所以基本不需要单独安装。
sh 执行 shell 命令。 sh
步骤支持的参数有:
script
:将要执行的 shell 脚本,通常在类 UNIX 系统上可以是多行脚本。encoding
:脚本执行后输出日志的编码,默认值为脚本运行所在系统的编码。returnStatus
:布尔类型,默认脚本返回的是状态码,如果是一个非零的状态码,则会引发 pipeline 执行失败。如果returnStatus
参数为true
,则不论状态码是什么,pipeline 的执行都不会受影响。returnStdout
:布尔类型,如果为true
,则任务的标准输出将作为步骤的返回值,而不是打印到构建日志中(如果有错误,则依然会打印到日志中)。除了script
参数,其他参数都是可选的。
returnStatus
与returnStdout
参数一般不会同时使用,因为返回值只能有一个。如果同时使用,则只有 returnStatus 参数生效。
bat、powershell bat
步骤执行的是 Windows 的批处理命令。powershell
步骤执行的是 PowerShell 脚本,支持 3+版本。这两个步骤支持的参数与 sh 步骤的一样。
2.5.4 其他步骤
error 主动报错,中止当前 pipeline。 error 步骤的执行类似于抛出一个异常。它只有一个必需参数:message
。通常省略参数:error("there's an error")
。 tool 使用预定义的工具。 如果在 Global Tool Configuration(全局工具配置)中配置了工具,那么可以通过tool
步骤得到工具路径。 tool
步骤支持的参数有:
name
:工具名称。type
(可选):工具类型,指该工具安装类的全路径类名。
每个插件的type
值都不一样,而且绝大多数插件的文档根本不写type
值。除了到该插件的源码中查找,还有一种方法可以让我们快速找到type
值,就是前往 Jenkins pipeline 代码片段生成器中生成该tool
步骤的代码即可。
timeout 代码块超时时间。 为 timeout 步骤闭包内运行的代码设置超时时间限制。如果超时,将抛出一个org.jenkinsci.plugins.workflow.steps.FlowInterruptedException
异常。timeout
步骤支持如下参数:
time
:整型,超时时间。unit
(可选):时间单位,支持的值有NANOSECONDS
、MICROSECONDS
、MILLISECONDS
、SECONDS
、MINUTES
(默认)、HOURS
、DAYS
。activity
(可选):布尔类型,如果值为true
,则只有当日志没有活动后,才真正算作超时。
waitUntil 等待条件满足。 不断重复waitUntil
块内的代码,直到条件为true
。waitUntil
不负责处理块内代码的异常,遇到异常时直接向外抛出。waitUntil
步骤最好与timeout
步骤共同使用,避免死循环。示例如下:
timeout(50) {
waitUntil {
script {
def r = sh script: 'curl http://example', returnStatus: true
retturn (r == 0)
}
}
}
retry 重复执行块 执行 N 次闭包内的脚本。如果其中某次执行抛出异常,则只中止本次执行,并不会中止整个retry
的执行。同时,在执行retry
的过程中,用户是无法中止 pipeline 的。
steps {
retry(20) {
script {
sh script: 'curl http://example', returnStatus: true
}
}
}
sleep 让 pipeline 休眠一段时间。 sleep
步骤可用于简单地暂停 pipeline,其支持的参数有:
time
:整型,休眠时间。unit
(可选):时间单位,支持的值有NANOSECONDS
、MICROSECONDS
、MILLISECONDS
、SECONDS
(默认)、MINUTES
、HOURS
、DAYS
。
2.5.5 小贴士
- Jenkins 提供了一个 pipeline 代码片段生成器,通过界面操作就可以生成代码。(只有 pipeline 项目有“Pipeline Syntax 菜单”)
- VS Code 扩展:Jenkins Pipeline Linter Connector,支持对 Jenkinsfile 的语法检验。
- 使用 Workspace Cleanup 插件清理空间。
- Ant 风格路径表达式。
Apache Ant 样式的路径有三种通配符匹配方法,利用它们可以组合出多种路径模式:
Wildcard | Description |
---|---|
? | 匹配任意单字符 |
* | 匹配 0 或者任意数量的字符,不包含/ |
** | 匹配 0 或者更多数量的目录,不包含/ |
Ant 风格路径匹配实例:
Path | Description |
---|---|
/app/*.x | 匹配(Matches)app 路径下所有.x 文件 |
/app/p?ttern | 匹配(Matches) /app/pattern 和 /app/pXttern ,但是不包括/app/pttern |
/**/example | 匹配项目根路径下 /project/example , /project/foow/example , 和 /example |
/app/**/dir/file. | 匹配(Matches) /app/dir/file.jsp , /app/foo/dir/file.html ,/app/foo/bar/dir/file.pdf , 和 /app/dir/file.java |
/**/*.jsp | 匹配项目根路径下任何的.jsp 文件 |
需要注意的是,路径匹配遵循最长匹配原则(has more characters),例如/app/dir/file.jsp
符合/*_/_.jsp
和/app/dir/*.jsp
两个路径模式,那么最终就是根据后者来匹配。
参考资料: [1] 《Jenkins 2.x 实战指南》 [2] https://jenkins.io/zh/doc/book/pipeline/syntax/ [3] https://jenkins.io/zh/doc/pipeline/steps/