Jenkins Pipeline 多job依赖、触发多Job、并行执行及制品下载
以下是关于 Jenkins Pipeline 触发多平台Job、并行执行及制品下载 的详细攻略,涵盖核心概念、关键配置和完整示例。
一、核心场景与目标
假设我们需要在一个主Pipeline中:
- 自动识别当前代码分支(如
wip/jenkins); - 对分支名中的特殊字符(如
/)进行转义,适配Jenkins的Job命名规则; - 并发触发3个平台的子Job(MacOSX、Windows、Linux),等待它们执行完成(
!!!还有一种方式值直接下载其他job最后一次成功的制品,但是每个job的制品可能版本就不一样,就比如可能不是最新一次提交而生成的制品,有可能这次提交,其他job还没有执行完成,又或者执行失败了!!!); - 从每个子Job中下载构建好的制品(
如可执行文件,下载制品也可以通过Jenkins自带的插件下载,直接通过URL下载是由于一直有一个权限问题,目前不知道为什么); - 聚合所有平台的制品,打包存档。
二、关键概念与细节
1. 分支名转义:为什么需要处理 /?
Jenkins中,多分支Pipeline的Job名称会自动将分支名中的 / 编码为 %2F(URL编码规则)。例如:
- 原始分支名:
wip/jenkins - 对应的Jenkins Job名称:
wip%2Fjenkins
处理方式:用 sed 命令将 / 替换为 %2F,确保后续拼接Job路径时匹配Jenkins实际名称。
2. 触发Job的URL vs 下载制品的URL
Jenkins中,“触发Job”和“下载制品”的路径格式不同,这是最容易出错的点:
| 场景 | 路径格式说明 | 示例(基于分支 wip/jenkins) |
|---|---|---|
| 触发Job | 用「Jenkins项目全称」,层级用 / 分隔,无 job/ 前缀(Jenkins内部识别格式)。 | Middleware/Whiteboard/WhiteboardTools/WbresOnMacOSX/wip%2Fjenkins |
| 下载制品 | 用「浏览器URL格式」,每个层级需加 job/ 前缀(HTTP请求路径格式)。 | http://jenkins-url/job/Middleware/job/Whiteboard/job/WhiteboardTools/job/WbresOnMacOSX/job/wip%2Fjenkins/3/artifact/... |
核心区别:触发Job时Jenkins接受“项目全称”(无 job/),而下载制品时的HTTP URL必须用 job/ 分隔层级。
3. 并行触发Job:为什么需要并行?
如果3个平台的子Job无依赖关系(可独立执行),并行触发能大幅缩短总构建时间:
- 顺序执行:总时间 = t1 + t2 + t3
- 并行执行:总时间 ≈ max(t1, t2, t3)
Jenkins通过 parallel 块实现并行,需注意语法(子阶段用键值对定义,支持 failFast 快速失败)。
三、详细步骤与配置
步骤1:定义分支名并转义
在Pipeline开始阶段,获取当前分支名并转义 / 为 %2F,确保后续路径正确。
stage('Set BRANCH_CODING') {steps {script {// 获取当前分支名(Jenkins内置变量 BRANCH_NAME,如 wip/jenkins)// 转义 / 为 %2F,适配Jenkins Job名称格式env.BRANCH_CODING = sh(script: '''echo "${BRANCH_NAME}" | sed 's/\\//%2F/g' ''', // 关键:用sed替换 / 为 %2FreturnStdout: true).trim()echo "转义后的分支名(适配Jenkins):${env.BRANCH_CODING}" // 输出:wip%2Fjenkins}}
}
步骤2:并行触发多平台子Job
用 parallel 块并发触发3个平台的子Job,每个子Job包含“触发→等待完成→下载制品”逻辑。
关键配置:
triggerJobPath:触发Job用的“项目全称”(无job/);dependJobPath:下载制品用的“URL路径”(含job/);build步骤:wait: true等待Job完成,propagate: true子Job失败时主Job也失败;failFast: true:可选,一个子Job失败则立即终止所有并行任务。
步骤3:下载制品并验证
子Job执行完成后,通过构建编号(buildNumber)精准定位最新制品,用 curl 下载并验证文件是否存在。
步骤4:聚合制品并存档
所有平台制品下载完成后,打包为zip并存档到Jenkins。
四、完整Pipeline脚本(带详细注释)
pipeline {agent {node {label 'UBUNTU' // 运行主Pipeline的节点标签}}// 可选:手动指定测试分支(实际场景用Jenkins内置的BRANCH_NAME)environment {// BRANCH_NAME = 'wip/jenkins' // 测试时可手动设置,正式环境注释掉}stages {// 步骤1:处理分支名,转义 / 为 %2Fstage('Set BRANCH_CODING') {steps {script {// 从Jenkins内置变量获取当前分支名(如 git 分支)// 用sed将 / 替换为 %2F(适配Jenkins Job名称)env.BRANCH_CODING = sh(script: '''echo "${BRANCH_NAME}" | sed 's/\\//%2F/g' ''',returnStdout: true).trim()echo "✅ 转义后的分支名:${env.BRANCH_CODING}" // 示例输出:wip%2Fjenkins}}}// 步骤2:并行触发3个平台的子Job并下载制品stage('并行触发子Job并下载制品') {steps {script {// 并行执行3个子阶段(MacOSX/Windows/Linux)parallel(failFast: true, // 快速失败:一个子Job失败,所有并行任务终止// 子阶段1:MacOSX平台"MacOSX": {// 1. 定义路径(触发Job和下载制品的路径分开)def jenkinsUrl = env.JENKINS_URL.trim().replaceAll(/\/$/, '') // 去除URL末尾的 /// 触发Job用的项目全称(无 job/)def triggerJobPath = "Middleware/Whiteboard/WhiteboardTools/WbresOnMacOSX/${env.BRANCH_CODING}"// 下载制品用的URL路径(含 job/)def dependJobPath = "Middleware/job/Whiteboard/job/WhiteboardTools/job/WbresOnMacOSX/job/${env.BRANCH_CODING}"def artifactSourcePath = 'tools/wbres/bin/macos/wbres' // 子Job中制品的相对路径def artifactTargetDir = 'bin/macos/' // 本地保存路径// 2. 触发子Job并等待完成echo "=== [MacOSX] 触发子Job:${triggerJobPath} ==="def buildResult = build(job: triggerJobPath, // 传入项目全称(无 job/)wait: true, // 等待子Job执行完成propagate: true, // 子Job失败时,主Job也失败quietPeriod: 0 // 立即触发,不延迟)def buildNumber = buildResult.getNumber() // 获取子Job的构建编号(如 3)echo "=== [MacOSX] 子Job执行完成,构建编号:${buildNumber} ==="// 3. 构建下载URL并下载制品def downloadUrl = "${jenkinsUrl}/job/${dependJobPath}/${buildNumber}/artifact/${artifactSourcePath}"sh "mkdir -p ${artifactTargetDir}" // 创建本地目录echo "=== [MacOSX] 下载URL:${downloadUrl} ==="// 用Jenkins凭证下载(避免明文密码)withCredentials([usernamePassword(credentialsId: '2c66b483-ef17-436e-a0d2-114d00bdf090', // 你的凭证IDusernameVariable: 'JENKINS_USER',passwordVariable: 'JENKINS_PWD')]) {sh """# 用curl下载制品,-f确保失败时返回非0状态码curl -f -L -v \-u "${JENKINS_USER}:${JENKINS_PWD}" \"${downloadUrl}" \-o "${artifactTargetDir}/wbres"# 验证下载结果echo "=== [MacOSX] 验证制品 ==="ls -l "${artifactTargetDir}"if [ -f "${artifactTargetDir}/wbres" ]; thenecho "✅ [MacOSX] 制品下载成功"elseecho "❌ [MacOSX] 制品下载失败"exit 1 # 失败时终止Pipelinefi"""}},// 子阶段2:Windows x64平台(逻辑与MacOSX一致,仅路径不同)"Windows x64": {def jenkinsUrl = env.JENKINS_URL.trim().replaceAll(/\/$/, '')def triggerJobPath = "Middleware/Whiteboard/WhiteboardTools/WbresX64OnWindows/${env.BRANCH_CODING}"def dependJobPath = "Middleware/job/Whiteboard/job/WhiteboardTools/job/WbresX64OnWindows/job/${env.BRANCH_CODING}"def artifactSourcePath = 'tools/wbres/bin/win32/wbres.exe'def artifactTargetDir = 'bin/win32/'echo "=== [Windows] 触发子Job:${triggerJobPath} ==="def buildResult = build(job: triggerJobPath, wait: true, propagate: true, quietPeriod: 0)def buildNumber = buildResult.getNumber()echo "=== [Windows] 子Job执行完成,构建编号:${buildNumber} ==="def downloadUrl = "${jenkinsUrl}/job/${dependJobPath}/${buildNumber}/artifact/${artifactSourcePath}"sh "mkdir -p ${artifactTargetDir}"echo "=== [Windows] 下载URL:${downloadUrl} ==="withCredentials([usernamePassword(credentialsId: '2c66b483-ef17-436e-a0d2-114d00bdf090', usernameVariable: 'JENKINS_USER', passwordVariable: 'JENKINS_PWD')]) {sh """curl -f -L -v -u "${JENKINS_USER}:${JENKINS_PWD}" "${downloadUrl}" -o "${artifactTargetDir}/wbres.exe"echo "=== [Windows] 验证制品 ==="ls -l "${artifactTargetDir}"if [ -f "${artifactTargetDir}/wbres.exe" ]; thenecho "✅ [Windows] 制品下载成功"elseecho "❌ [Windows] 制品下载失败"exit 1fi"""}},// 子阶段3:Linux (Docker)平台"Linux (Docker)": {def jenkinsUrl = env.JENKINS_URL.trim().replaceAll(/\/$/, '')def triggerJobPath = "Middleware/Whiteboard/WhiteboardTools/WbresGccLinuxDocker/${env.BRANCH_CODING}"def dependJobPath = "Middleware/job/Whiteboard/job/WhiteboardTools/job/WbresGccLinuxDocker/job/${env.BRANCH_CODING}"def artifactSourcePath = 'tools/wbres/bin/linux/wbres'def artifactTargetDir = 'bin/linux/'echo "=== [Linux] 触发子Job:${triggerJobPath} ==="def buildResult = build(job: triggerJobPath, wait: true, propagate: true, quietPeriod: 0)def buildNumber = buildResult.getNumber()echo "=== [Linux] 子Job执行完成,构建编号:${buildNumber} ==="def downloadUrl = "${jenkinsUrl}/job/${dependJobPath}/${buildNumber}/artifact/${artifactSourcePath}"sh "mkdir -p ${artifactTargetDir}"echo "=== [Linux] 下载URL:${downloadUrl} ==="withCredentials([usernamePassword(credentialsId: '2c66b483-ef17-436e-a0d2-114d00bdf090', usernameVariable: 'JENKINS_USER', passwordVariable: 'JENKINS_PWD')]) {sh """curl -f -L -v -u "${JENKINS_USER}:${JENKINS_PWD}" "${downloadUrl}" -o "${artifactTargetDir}/wbres"echo "=== [Linux] 验证制品 ==="ls -l "${artifactTargetDir}"if [ -f "${artifactTargetDir}/wbres" ]; thenecho "✅ [Linux] 制品下载成功"elseecho "❌ [Linux] 制品下载失败"exit 1fi"""}})}}}// 步骤3:聚合所有平台的制品并存档stage('聚合制品并存档') {steps {script {// 将所有平台的制品打包为zipsh 'mkdir -p bin && zip -r wbres_all_platforms.zip bin/'}// 存档到Jenkins(仅当所有步骤成功时)archiveArtifacts(artifacts: 'wbres_all_platforms.zip',fingerprint: true,onlyIfSuccessful: true)echo "✅ 所有平台制品已聚合并存档:wbres_all_platforms.zip"}}}// 构建结束后通知(可选)post {success {echo "🎉 主Pipeline执行成功!所有制品已就绪。"// 可添加邮件/企业微信通知:emailext to: 'xxx@example.com', subject: '构建成功', body: '...'}failure {echo "❌ 主Pipeline执行失败,请查看日志。"// 失败通知:emailext to: 'xxx@example.com', subject: '构建失败', body: '...'}}
}
五、调试与验证技巧
-
验证分支名转义:
查看Set BRANCH_CODING阶段的输出,确认BRANCH_CODING正确转义(如wip/jenkins→wip%2Fjenkins)。 -
检查触发Job的路径:
在Jenkins网页端导航到子Job,查看浏览器URL中的“项目全称”(如Middleware/Whiteboard/.../wip%2Fjenkins),与脚本中的triggerJobPath对比是否一致。 -
验证下载URL:
下载阶段输出的downloadUrl可直接复制到浏览器,用Jenkins账号密码访问,确认能否下载制品(排除URL格式错误)。 -
并行执行日志:
在Jenkins Pipeline页面,并行阶段会显示为“分支”,可点击单个分支查看详细日志(如MacOSX子阶段的触发和下载过程)。
六、常见问题与解决
-
“No item named … found”:
原因:triggerJobPath与Jenkins实际项目全称不匹配(如未转义/或多了job/)。
解决:按“触发Job的URL格式”修正triggerJobPath,确保无job/且/已转义为%2F。 -
并行阶段语法错误:
原因:parallel块内用stage(...) { ... }定义子阶段,或failFast位置错误。
解决:子阶段用键值对“名称”: { ... }定义,failFast作为parallel函数的参数(如parallel(failFast: true, "子阶段1": { ... }))。 -
制品下载404:
原因:downloadUrl中的dependJobPath未加job/层级,或构建编号错误。
解决:确保dependJobPath每个层级都有job/,并通过buildResult.getNumber()获取正确的构建编号。
通过以上步骤,你可以实现“并行触发多平台Job→等待完成→下载制品→聚合存档”的完整流程,大幅提升构建效率并确保制品版本一致性。
