常用 Steps:文件操作、超时、重试、错误处理
常用 Steps:文件操作、超时、重试、错误处理
这一篇是 Pipeline 里最常调的一组内置 step,按「文件/目录」和「流程控制」两类整理。
文件与目录
deleteDir:删除当前目录
递归删除当前目录及其内容(软链接/junction 只删链接不跟进)。要删指定目录,用 dir 包住:
dir('D:\\test\\logs') {
deleteDir()
}
dir:切换当前目录
dir 块内的所有 step 都以这个目录为当前目录,相对路径也以它为基准:
dir('tmp') {
writeFile(file: '.dummy', text: 'data', encoding: "UTF-8")
}
fileExists:判断文件是否存在
返回布尔。在 if 里直接用要加括号,或先赋值给变量:
def exists = fileExists 'file'
if (exists) { echo 'Yes' } else { echo 'No' }
if (fileExists('file')) { echo 'Yes' } else { echo 'No' }
fileExists('/')、fileExists('.')、fileExists('') 等价,都是检查「最近一次 dir() 进入的目录」是否存在,最后一种会报警告,不推荐:
dir('dirA') {
if (fileExists('/')) { // 检查 dirA 是否存在
println "directory exists"
} else {
println "directory does not exist"
}
}
pwd:当前目录
返回当前目录路径。可选参数 tmp: true 返回与工作空间关联的临时目录,适合放不该混进源码 checkout 的临时文件、本地缓存等:
println "current directory: ${pwd()}"
println "temporary directory: ${pwd(tmp: true)}"
writeFile / readFile:读写文件
dir('tmp') {
writeFile(file: '.dummy', text: 'This file is useful', encoding: "UTF-8")
def content = readFile(file: '.dummy', encoding: 'UTF-8')
echo "$content"
}
writeFile 的 encoding 留空则用操作系统默认编码;写 Base64 数据时可用 Base64 编码。
流程控制
error:主动报错并终止 Pipeline
类似抛异常,但不打印堆栈。只有一个必填参数 message:
error("there's an error")
// 等价于 throw new Exception("some message"),但 error 不会打印 stack trace
catchError:捕获错误并把构建标记为失败
块内抛异常时把构建标记为失败,但继续执行 catchError 之后的语句。行为可配:打印消息、设置成别的构建结果、改 stage 结果、或忽略某些用于中断构建的异常。
一个实际需求:不想因为 stage A 超时把整条 Pipeline 变成 ABORTED,只想把 stage A 标成 ABORTED 而整体仍是 SUCCESS。注意按文档,捕获到 UNSTABLE 或 ABORTED 时,不能把整体重新设回 SUCCESS;要强行 SUCCESS,得把代码放进 script 里用 try-catch:
pipeline {
agent any
stages {
stage("A") {
options { timeout(time: 3, unit: "SECONDS") }
steps {
script {
Exception caughtException = null
catchError(buildResult: 'SUCCESS', stageResult: 'ABORTED') {
try {
echo "Started stage A"
sleep(time: 5, unit: "SECONDS")
} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) {
// 超时抛的就是 FlowInterruptedException,这里 error 让 A 失败
error "Caught ${e.toString()}"
} catch (Throwable e) {
caughtException = e
}
}
if (caughtException) {
error caughtException.message
}
}
}
}
stage("B") {
steps { echo "Started stage B" }
}
}
}
catchError 的参数:
buildResult(可选):捕获到错误时整体构建结果设为此值。构建结果只会变差不会变好,所以当前已是 UNSTABLE 或更糟时改不回 SUCCESS;用SUCCESS或null可阻止设置构建结果。catchInterruptions(可选,布尔):true 时连「手动中止」「timeout 抛出」这类中断异常一起捕获处理;false 时这类异常会被重新抛出。message(可选):记录到控制台的消息,若指定了stageResult也会关联展示。stageResult(可选):捕获到错误时 stage 结果设为此值。
timeout:代码块超时
超时会抛 FlowInterruptedException 导致构建中止(除非被捕获)。参数:time(整型)、unit(默认 MINUTES,可选 SECONDS/HOURS/DAYS 等)、activity(布尔,true 时只有日志长时间无输出才算超时)。
timeout(10) // 10 分钟(默认单位)
timeout(time: 10, unit: 'SECONDS') // 10 秒
// 声明式里也可以在 options 设置,整条或单个 stage
pipeline {
options { timeout(time: 1, unit: 'HOURS') }
stages {
stage("A") {
options { timeout(time: 3, unit: "SECONDS") } // stage 级:3 秒后终止
steps {
echo "Started stage A"
sleep(time: 5, unit: "SECONDS")
}
}
stage("B") { steps { echo "Started stage B" } }
}
}
retry:失败重试
块内抛异常就重试,最多 N 次;最后一次仍失败则中止构建。重试过程中用户无法手动中止。常和 timeout 一起用,给不稳定的脚本兜底:
pipeline {
agent any
stages {
stage('test') {
steps {
retry(3) { // 失败最多重试 3 次
sh './flakey-test.sh'
}
timeout(time: 3, unit: 'MINUTES') {
sh './health-check.sh'
}
}
}
stage('Deploy') {
steps {
timeout(time: 3, unit: 'MINUTES') { // 3 分钟没完成也会被终止
retry(5) {
sh './flakey-deploy.sh'
}
}
}
}
}
}
waitUntil:等待条件满足
反复执行块直到返回 true;返回 false 就等一会儿再试(失败越多间隔越长,最长 15 秒),重试次数无上限。块内抛异常会直接向外抛。务必配 timeout 防死循环:
timeout(50) {
waitUntil {
script {
def r = sh script: 'curl http://example', returnStatus: true
return (r == 0)
}
}
}
sleep:暂停一段时间
sleep(120) // 默认单位秒
sleep(time: '2', unit: 'MINUTES')
可用来在 parallel 的一个分支里暂停,让另一个分支先推进。