问题描述
项目源代码包含多个长期存在的分支,每个分支都有自己的Jenkins任务(手动创建)。
不幸的是,用户没有使用多分支流水线来组织项目,因为当需要对流水线进行更改时,需要手动将更改添加到每个长期存在的分支的Jenkinsfile中。
用户将Jenkinsfile存储在一个单独的存储库(称为ci-repo)中,并将其作为子模块添加到项目的存储库(称为dev-repo)中。
用户需要手动监视dev-repo的分支并管理作业的创建。多分支流水线支持检出源文件的子模块,但是分支发现需要在父存储库(在我们的例子中是dev-repo)下存在Jenkinsfile。
问题是:Jenkinsfile来自多分支流水线的git子模块 – 有办法做到这一点吗?也许在dev-repo中使用一个初始/模拟的Jenkinsfile,在其中调用子模块的Jenkinsfile?Jenkinsfile是否可以在运行时被覆盖或替换?
解决方案
请注意以下操作注意版本差异及修改前做好备份。
方案1
根据回答1,你可以使用共享库来实现你的需求。共享库可以实现流水线,使得所有的Jenkinsfile看起来都是一样的。你可以参考Jenkins官方文档中关于共享库的介绍。以下是一个示例共享库的代码:
def call(body) {
// 将配置收集到pipelineParams对象中
pipelineParams= [:]
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = pipelineParams
body()
pipeline {
agent {
label pipelineParams.nodes
}
options {
buildDiscarder(logRotator(artifactNumToKeepStr: '1', numToKeepStr: '10'))
disableConcurrentBuilds()
timestamps()
timeout(time: pipelineParams.timeout, unit: 'MINUTES')
}
stages {
stage('Build') {
steps {
// 执行构建步骤
}
}
}
}
}
然后,在你的dev项目中的Jenkinsfile中加载这个共享库,并使用共享库中的pipeline来定义流水线:
import org.apache.commons.io.FileUtils
library identifier: 'shared-pipeline-library@master', retriever: modernSCM(
[$class: 'GitSCMSource',
remote: 'https://bitbucket/jenkins/hared-pipeline-library.git',
credentialsId: 'bitbucket.service.user'
])
commonpipeline {
nodes = 'BUILD' /* jenkins节点的标签 */
timeout = '60'
}
方案2
根据回答2,你可以将”devops基础设施逻辑”(放在ci-repo中)与触发流水线的Jenkinsfile分离开来。Jenkinsfile中包含”devops基础设施逻辑”作为一个标记的构件。这种方法的优点是你可以在一个集中的地方开发(和测试)devops代码,并且当变更是向后兼容时,可以通过移动标记来更新所有的流水线。当变更不向后兼容时,可以逐步将存储库和特定分支移动到新的标记。如果需要的话,特定的存储库和分支可以创建自己的流程。
这种方法的缺点是,有时人们需要在多个地方更新Jenkinsfile,因为他们接受了新版本。但是,这样做实际上是有好处的。
方案3
根据回答3,使用子模块可能无法实现你的想法。当Jenkins多分支流水线轮询SCM以触发新的构建时,它无法看到子模块。作者最近验证了这一点。在”扫描多分支流水线日志”中的结果是”Communication error for url”的响应。Jenkinsfile未找到,因此没有触发构建。
好消息是,作者发现了一种简单的方法来实现我们的目标,而无需使用子模块。可以使用”Remote Jenkinsfile Provider Plugin”来实现。当流水线轮询存储库以触发新的构建时,它会查找每个分支的两个条件:
1. 被轮询的分支与上次构建时有新的SCM更改(HEAD不同)。
2. 存储库X的分支Y下的路径Z存在Jenkinsfile。
在多分支流水线的”构建配置”部分,通常只有一种模式可用于告诉Jenkins在哪里找到Jenkinsfile:”通过Jenkinsfile”。这种模式允许我们仅指定路径Z;X和Y会隐式地设置为正在轮询的存储库和分支。
“Remote Jenkinsfile Provider Plugin”给我们提供了一种新的构建配置模式:”通过Remote Jenkins File Plugin”。这种模式允许我们显式地设置Jenkinsfile的存储库、分支和路径 – X、Y和Z。在你的例子中,X将是ci-repo,但不需要ci-repo是dev-repo的子模块,只要Jenkinsfile在指定的位置存在即可。
如果需要,”Match branches”选项使用”轮询的分支名称”作为Y,查找同名分支上的”远程”Jenkinsfile。例如,在轮询dev-repo/feature-123时,它将在ci-repo/feature-123上查找Jenkinsfile。
只要Jenkinsfile存在于指定的位置,就会满足触发新构建的第二个条件。在流水线的轮询日志中,你将看到类似下面的内容:
Looking up DR/dev-repo for branches
Checking branch master from DR/dev-repo
No local file defined. Skipping Source Code SCM probe, since Jenkinsfile will be provided by Remote Jenkins File Plugin
Met criteria
Scheduled build for branch: master
有关如何设置的说明,请参见插件页面上的说明。
需要注意的是,将更改推送到”远程”Jenkinsfile(ci-repo)不会触发轮询存储库(dev-repo)的任何构建。如果需要此行为,一种解决方案是使用webhook。另一种解决方案是创建一个轮询”远程”Jenkinsfile存储库并使用build step构建其他流水线作业的流水线,例如:
build(job: "dev-repo/${BRANCH_NAME}", wait: false)
以上是三种解决方案,你可以根据你的需求选择适合你的方案。