如何在Jenkins同一流水线中获取下游任务的正确构建产物?
这个问题在多分支并行构建的场景下确实很常见——用「最新成功构建」很容易拿到其他分支的产物,根本原因是我们没有把当前流水线触发的下游构建和后续任务绑定起来。下面给你两种可靠的解决方案,从简单到灵活:
方案一:显式传递下游构建编号(最直接)
核心思路是:在触发Android - Unittests时,捕获它的构建编号,然后把这个编号传递给所有需要拷贝其产物的后续任务,最后在Copy Artifact时指定这个特定编号,而不是用「最新成功」。
步骤1:修改主流水线Jenkinsfile,捕获构建编号
把你的Build & Unittests阶段改成这样,保存触发后的构建对象并提取编号:
stage('Build & Unittests') { // 捕获构建结果,而不是只触发 def unittestBuild = build job: 'Android - Unittests', parameters: params // 将构建编号存入环境变量,方便后续传递 env.UNITTEST_BUILD_NUM = unittestBuild.number echo "Triggered Android - Unittests build #${env.UNITTEST_BUILD_NUM}" }
步骤2:把构建编号传递给后续任务
比如在Nexus publish阶段,新增一个参数传递这个编号:
stage('Nexus publish') { def publishParams = [ string(name: 'BRANCH_NAME', value: branchName), string(name: 'UNITTEST_BUILD_NUM', value: env.UNITTEST_BUILD_NUM) ] build job: 'Android - Nexus publish', parameters: publishParams }
步骤3:在下游任务中使用指定编号拷贝产物
如果是Freestyle任务(你的Android - Nexus publish这类),配置Copy Artifact Plugin时:
- 不要选「Latest successful build」
- 选择「Specific build」,然后在输入框中填入
${UNITTEST_BUILD_NUM}(引用传递过来的参数) - 如果是Multi-configuration项目的子构建,还可以在「Build number」下方的「Subproject」里指定对应的FLAVOR/TYPE(比如
FLAVOR=prod,TYPE=release)
如果是用Pipeline脚本拷贝,直接在copyArtifacts步骤中指定编号:
copyArtifacts( projectName: 'Android - Unittests', selector: specific(env.UNITTEST_BUILD_NUM), filter: '**/*.apk', target: 'apk-files', // 如果需要指定子构建,加上这句 subproject: "FLAVOR=${env.FLAVOR},TYPE=${env.TYPE}" )
方案二:使用上游构建选择器(更自动化)
如果不想手动传递编号,可以利用Copy Artifact Plugin的「Upstream Build Selector」功能,让插件自动关联触发当前任务的上游构建对应的下游产物。
配置方法(Freestyle任务):
在Copy Artifact的配置中:
- 选择「Upstream build」作为构建选择器
- 在「Which upstream build?」中选择「Build that triggered this project」
- 确保你的下游任务(比如
Android - Nexus publish)确实是被主流水线触发的,Jenkins会自动记录这个触发关系
这种方式不需要传递额外参数,但要注意:如果你的下游任务可能被多个上游任务触发,或者存在复杂的依赖链,可能会出现匹配错误的情况——所以方案一更适合分支多、并行频繁的场景。
针对Multi-configuration项目的额外注意点
因为你的Android - Unittests是多配置项目,每个FLAVOR/TYPE组合都会生成独立的子构建:
- 如果需要特定配置的产物,要么在触发时指定对应的参数(让下游任务只运行该配置),要么在拷贝时明确指定子项目的配置标识(比如
FLAVOR=prod,TYPE=release) - 主构建的编号和子构建的编号是不同的,如果你用方案一,需要确保拷贝的是子构建的产物——可以通过主构建对象的
getSubBuilds()方法获取对应配置的子构建编号(示例代码如下):
def unittestBuild = build job: 'Android - Unittests', parameters: params // 筛选出需要的子构建(比如FLAVOR=prod, TYPE=release) def targetSubBuild = unittestBuild.getSubBuilds().find { subBuild -> def subParams = subBuild.getBuildVariables() subParams.FLAVOR == 'prod' && subParams.TYPE == 'release' } env.UNITTEST_SUB_BUILD_NUM = targetSubBuild.number
这两种方案都能彻底解决你遇到的「拿错其他分支产物」的问题,根据你的项目复杂度选就行——分支多、并行频繁的话优先方案一,依赖链简单的话方案二更省心。
内容的提问来源于stack exchange,提问作者P Kuijpers

