触发原理
多分支流水线是一种Jenkins Job 类型。
新建一个多分支流水线时,会配置 代码仓库、Scan时间、Jenkinsfile路径等信息
如果配置Scan时间为1min,那么每1min会扫描所配置的代码仓库的分支,如果在该分支上搜索到了配置的Jenkinsfile,就会在Jenkins 创建一个以分支命名的Job并编译,如下图dev,当分支上有新的提交也会触发编译
因多分支流水线 job 以分支为命名,ph rule不能直接触发,所以会创建一个中转的jump job来根据分支下发编译需求
模板Jenkinsfile详解
多分支流水线Jenkinsfile
pipeline {
agent { //2~33 使用docker images 初始化编译环境
kubernetes {
label "${UUID.randomUUID().toString()}"
yaml """
metadata:
labels:
some-label: some-label-value
class: KubernetesDeclarativeAgentTest
namespace: scm
spec:
nodeSelector:
jenkins-ci: "true"
containers:
- name: prepare
image: hub.hobot.cc/builder/cicd-common-tool:v9
command:
- cat
tty: true
env:
- name: CONTAINER_ENV_VAR
value: prepare
- name: build
image: hub.hobot.cc/builder/ubuntu16.04:20220402
command:
- cat
tty: true
env:
- name: CONTAINER_ENV_VAR
value: build
"""
}
}
environment {
LANG = 'en_US.UTF-8'
}
parameters { //环境变量,用于接收来自Ph的Post请求
string defaultValue: '', description: 'Enter PH DIFF_ID', name: 'DIFF_ID', trim: true
string defaultValue: '', description: 'Enter PH PHID', name: 'PHID', trim: true
string defaultValue: '', description: 'Enter PH REVISION ID', name: 'REVISION_ID', trim: true
}
stages{
stage('prepare') {
environment {
LANG = 'en_US.UTF-8'
SSH_KEY_FILE = credentials('725axxxxxx')
//这里需要使用项目账号的credentials. 这是配置编译时下载代码的密钥,密钥配置方法参考:CICD流程获取代码权限管理方案 中的 10. 如何在Jenkins内创建账号认证信息
}
steps {
container('prepare') {
script {
println Jenkins.instance.getNode("${env.NODE_NAME}").toComputer().getLog() //打印机器信息
if (!env.DIFF_ID) { //判断是否是来自Ph的触发
updateGitlabCommitStatus name: "${env.JOB_NAME}", state: 'running'
}//如果DIFF_ID为空,则更新gitlab commit Status
else {
process_ph()
}//否则运行process_ph()函数
}
}
}
}
stage('building') {
steps {
container('build') {//括号里填写的是docker名,第23行配置的name
script {
sh 'sleep 10s' //此处运行编译脚本
}
}
}
}
}
options {
gitLabConnection('Gitlab connector by hobot.ci')
}
post { // 76~144行 根据构建结果执行不同的后续步骤,没有特殊需求不用修改
failure {
container('prepare') {
script {
if (!env.DIFF_ID) {
updateGitlabCommitStatus name: "${env.JOB_NAME}", state: 'failed'
send_mail()
}
}
}
}
success {
container('prepare') {
script {
if (!env.DIFF_ID) {
updateGitlabCommitStatus name: "${env.JOB_NAME}", state: 'success'
}
else {
change_ph_status()
}
}
}
}
unstable {
container('prepare') {
script {
if (!env.DIFF_ID) {
updateGitlabCommitStatus name: "${env.JOB_NAME}", state: 'success'
}
else {
change_ph_status()
}
send_mail()
}
}
}
aborted {
container('prepare') {
script {
if (!env.DIFF_ID) {
updateGitlabCommitStatus name: "${env.JOB_NAME}", state: 'canceled'
send_mail()
}
}
}
}
always {
container('prepare') {
script {
if (env.DIFF_ID) {
try {
timeout(time: 10, unit: 'SECONDS') {
step([$class: 'PhabricatorNotifier', commentOnSuccess: true, commentWithConsoleLinkOnFailure: true, customComment: true, commentFile: '.phabricator-comment', commentSize: '999999999'])
}
}
catch (Exception e) {
try {
timeout(time: 10, unit: 'SECONDS') {
step([$class: 'PhabricatorNotifier', commentOnSuccess: true, commentWithConsoleLinkOnFailure: true])
}
}
catch (Exception f) {
echo "finish"
}
}
}
}
}
}
}
}
//147~215 函数
def process_ph() {
sh '''
curl https://cr.hobot.cc/api/differential.revision.edit \
-d api.token= \
-d transactions[0][type]=comment \
-d transactions[0][value]="build job is running \n click this link then redirect to build job: \n ${BUILD_URL}" \
-d objectIdentifier=$REVISION_ID
'''
addShortText(text: "Link to PH D${env.REVISION_ID}", padding: "1", border: "0", margin: "0px", background: "transparent", color: "blue", link: "https://cr.hobot.cc/D${env.REVISION_ID}")
title = get_ph_title()
title = title.replaceAll("\\[Build Not Finish Or FAIL DO NOT ACCEPT\\]", "")
title = "[Build Not Finish Or FAIL DO NOT ACCEPT]" + title
change_ph_title(title,"${env.REVISION_ID}")
}
def get_ph_title() {
title = sh (script: 'curl -s https://cr.hobot.cc/api/differential.query -d api.token= -d ids[0]=$REVISION_ID | python3 -c "import sys, json; print(json.load(sys.stdin)[\'result\'][0][\'title\'])"',returnStdout: true)
return title
}
def change_ph_title(title_message,revision_id) {
title_message = java.net.URLEncoder.encode(title_message, "UTF-8")
sh """
curl https://cr.hobot.cc/api/differential.revision.edit \
-d api.token= \
-d transactions[0][type]=title \
-d transactions[0][value]=${title_message} \
-d objectIdentifier=${revision_id}
"""
}
def change_ph_status() {
title = get_ph_title()
title = title.replaceAll("\\[Build Not Finish Or FAIL DO NOT ACCEPT\\]", "")
change_ph_title(title,"${env.REVISION_ID}")
}
def get_code() {
echo "push process"
checkout([
$class: 'GitSCM',
branches: [[name: "origin/${PH_BRANCH_NAME}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'CloneOption', depth: 0, noTags: false, reference: '', shallow: false],
[$class: 'SubmoduleOption', disableSubmodules: false, parentCredentials: true, recursiveSubmodules: true, reference: '', trackingSubmodules: true],
[$class: 'AuthorInChangelog'],
[$class: 'UserIdentity', email: 'xxxx@horizon.ai', name: 'xxxx'],
],
submoduleCfg: [],
userRemoteConfigs: [[credentialsId: 'e486xxxx', url: "${env.gitlabSourceRepoSshUrl}"]]
])
if (env.REVISION_ID) {
echo "apply patch from ph"
try{
PH_BRANCH_NAME = sh (script: 'curl -s https://cr.hobot.cc/api/differential.diff.search -d api.token= -d constraints[ids][0]=$DIFF_ID | python3 -c "import sys, json; print(json.load(sys.stdin)[\'result\'][\'data\'][0][\'fields\'][\'refs\'][1][\'name\'])"',returnStdout: true).trim().replaceAll("/", "%2F")
}catch (Exception err){
PH_BRANCH_NAME = sh (script: 'curl -s https://cr.hobot.cc/api/differential.diff.search -d api.token= -d constraints[ids][0]=$DIFF_ID | python3 -c "import sys, json; print(json.load(sys.stdin)[\'result\'][\'data\'][0][\'fields\'][\'refs\'][0][\'name\'])"',returnStdout: true).trim().replaceAll("/", "%2F")
}
sh """
echo "PH_BRANCH_NAME: ${PH_BRANCH_NAME}"
git config --global user.name xxxx
git config --global user.email xxx@horizon.ai
git reset --hard origin/"${PH_BRANCH_NAME}"
git clean -fd -f
arc patch --diff "$DIFF_ID" --nobranch --force --conduit-uri=https://cr.hobot.cc/ --conduit-token=
"""
}
}
Jump Jenkinsfile
//change to current build jenkins job
def build_jenkins_project = "xxx/XXX" //""里配置需要触发编译的Jenkins job
//Ex: def build_jenkins_project = "CICD-Demo/Demo_Pre_build_Jump/"
pipeline {
agent {//6~29 行 初始化环境,因为不涉及编译,所以不用修改
kubernetes {
label "${UUID.randomUUID().toString()}"
yaml """
metadata:
labels:
some-label: some-label-value
class: KubernetesDeclarativeAgentTest
namespace: scm
spec:
nodeSelector:
jenkins-ci: "true"
containers:
- name: build
image: hub.hobot.cc/builder/cicd-common-tool:v8
command:
- cat
tty: true
env:
- name: CONTAINER_ENV_VAR
value: build
"""
}
}
options {
skipDefaultCheckout()
}
parameters { //参数,用来接收来自Ph的post 请求
string defaultValue: '', description: 'Enter PH DIFF_ID', name: 'DIFF_ID', trim: true
string defaultValue: '', description: 'Enter PH PHID', name: 'PHID', trim: true
string defaultValue: '', description: 'Enter PH REVISION ID', name: 'REVISION_ID', trim: true
}
stages{ //38~62配置jump触发编译任务
stage('jump') {
steps {
container('build') {
script {
branch = sh (script: 'curl -s https://cr.hobot.cc/api/differential.query -d api.token= -d ids[0]=$REVISION_ID | python3 -c "import sys, json; print(json.load(sys.stdin)[\'result\'][0][\'branch\'])"',returnStdout: true).trim().replaceAll("/", "%2F")
remote_job = build job: build_jenkins_project + '/' + branch,
wait: false,
parameters: [
[$class: 'StringParameterValue', name: 'DIFF_ID', value: "${env.DIFF_ID}"],
[$class: 'StringParameterValue', name: 'PHID', value: "${env.PHID}"],
[$class: 'StringParameterValue', name: 'REVISION_ID', value: "${env.REVISION_ID}"],
]
sh '''
curl https://cr.hobot.cc/api/differential.revision.edit \
-d api.token= \
-d transactions[0][type]=comment \
-d transactions[0][value]="Jump job success \n click this link then redirect to build job: \n ${BUILD_URL}" \
-d objectIdentifier=$REVISION_ID
'''
}
}
}
}
}
post {
success {
container('build') {
sh 'echo continue'
script {
branch = sh (script: 'curl -s https://cr.hobot.cc/api/differential.query -d api.token= -d ids[0]=$REVISION_ID | python3 -c "import sys, json; print(json.load(sys.stdin)[\'result\'][0][\'branch\'])"',returnStdout: true).trim().replaceAll("/", "%2F")
if (!job_branch_list.contains(branch)) {
step([$class: 'PhabricatorNotifier', commentOnSuccess: true, commentWithConsoleLinkOnFailure: true])
}
}
}
}
failure {
script {
step([$class: 'PhabricatorNotifier', commentOnSuccess: true, commentWithConsoleLinkOnFailure: true])
}
}
aborted {
script {
step([$class: 'PhabricatorNotifier', commentOnSuccess: true, commentWithConsoleLinkOnFailure: true])
}
}
}
}
怎么使用模板
编写多分支流水线Jenkinsfile
- 拷贝Jenkinsfile_Pre_build_branch模板,根据项目情况修改docker image、编译步骤等
- 将修改完的Jenkinsfile_Pre_build_branch命名为Jenkinsfile ,提交到项目代码仓库的scm/project/目录下
- 如果需要编译的是多个分支,这些分支的scm/project/ 下都要有Jenkinsfile
编写Jump Jenkinsfile - 拷贝Jenkinsfile_Pre_build_Jump模板,根据项目情况修改docker image、编译步骤等
- 将修改完的Jenkinsfile_Pre_build_Jump命名为Jenkinsfile_Jump ,提交到项目代码仓库的目录下
创建并配置多分支流水线Jenkins Job
在需要创建 Jenkins job的文件夹下新建多分支流水线。
输入Jenkins job名,选择多分支流水线,点击OK完成创建
配置多分支流水线
1.对Branch Sources进行配置
- Branch Sources点击add source,选择git
- Project Repository:输入仓库地址
- Credentials:输入认证凭据(凭据选择git)
Behaviours:
-
add 选择Discover Tags
-
add 选择Advanced clone behaviours
-
勾选下Shallow clone,Shallow clone depth为1
-
Build strategies选择named branches
-
add添加wildcard include/excludes
-
include默认为*,表示所有分支,也可以在include中添加所需触发的分支名
配置完成后点击保存
创建Jump job
参考 创建Jenkins job 创建Jump job ,命名可以加上Jump 字符
配置Jenkins job ,调用代码仓库中的Jump Jenkinsfile Jenkins job调用代码仓库中的Jenkinsfile