Jenkins Pipeline 如何让关键步骤完成后再处理中止?
结论
Jenkins Pipeline 没有内置开关,可以在中止构建时让当前 powershell 步骤先运行完,再将构建标记为已中止。用户中止构建会中断 Pipeline。catchError 可以处理某些中断异常,但无法保证正在运行的 CLI 一定执行完毕。
Jenkins 文档说明,手动中止会中断执行构建的线程;catchError 的 catchInterruptions 选项则决定是否捕获手动中止、超时等 Pipeline 中断异常。中止构建 · catchError 步骤
为什么包装 catchError 不等于原子执行
下面的写法可以捕获某些中断异常,让 Pipeline 继续执行后续步骤:
catchError(catchInterruptions: true) {
powershell '''
& My-CLI -v 'MyArgument'
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
'''
}
不过,catchError 处理的是异常,既不会屏蔽中止请求,也不能保证底层进程继续运行。中断发生时,CLI 是否能执行完取决于执行方式和相关插件的行为,不能只靠这段包装来确保任务完成。Jenkins 的 powershell 是 Pipeline 的持久任务步骤,但这不代表它不可中止。powershell 步骤参考
更可靠的做法:把关键操作交给独立任务
如果任务必须在 Jenkins 构建中止后继续执行,可以把 CLI 工作交给独立于当前构建生命周期的服务或任务执行器。Pipeline 提交任务并取得任务 ID,等待任务完成后,再根据持久化的任务状态执行后续逻辑。即使 Jenkins 构建被中止,执行任务的系统仍能继续处理。
示意结构如下。调用命令和状态接口需要根据你的执行系统实现:
stage('Critical step') {
when {
expression { return someCondition }
}
steps {
script {
// 提交任务,并取得可持久化查询的 taskId
def taskId = submitCriticalTask()
// 等待外部任务报告完成,并检查其退出状态
def result = waitForCriticalTask(taskId)
if (result.exitCode != 0) {
error("Critical task failed: ${result.exitCode}")
}
env.CRUTIAL_STEP_DONE = 'true'
}
}
}
submitCriticalTask() 和 waitForCriticalTask() 是说明架构的占位函数,并非 Jenkins 内置步骤。实际实现应在 Jenkins 之外持久化任务 ID 和任务结果,并考虑如何保证重复提交时的幂等性。
让任务可恢复
如果 CLI 必须直接运行在 Jenkins agent 上,仅靠 Pipeline 语法无法保证进程在构建中止时一定执行完。可以让 CLI 支持安全重试或恢复:为每次操作记录唯一标识和状态,避免重复执行产生副作用;操作完成后将结果写入持久化存储。重新运行构建时,先查询任务状态,再决定继续、跳过或恢复。对数据库或外部系统的变更,应由执行端提供事务、幂等或补偿机制。
CRUTIAL_STEP_DONE 的注意事项
当前代码会在 powershell 步骤返回后才设置 CRUTIAL_STEP_DONE。如果 CLI 以非零状态退出,exit $LASTEXITCODE 会使脚本失败,后面的赋值通常不会执行。若构建在步骤运行期间被中止,也不能保证一定会执行这条赋值。
因此,不要把这个环境变量当作跨构建的任务完成记录。如果 post 中的操作必须准确判断 CLI 是否完成,应读取持久化的任务状态或 CLI 写出的结果文件,并区分“未启动”“执行中”“成功”和“失败”。
备注:内容仅供参考。
