背景
- 公司目前使用环境和分支绑定的分支管理策略,不同环境有对应分支,代码由功能特性分支合并到测试环境,然后根据环境等级逐级往上合并,为提高效率,需要实现功能特性分支自动合并到测试环境对应分支。
实现思路
- 整体思路gitlab配置合并请求,自动触发merge request钩子推送相关信息至jenkins的webhook触发jenkins的合并的job,该job通过调用gitlab api实现自动合并,并且将合并的结果邮件通知到请求人员。
实施过程
- gitlab创建jenkins账号,给予管理员权限(如果对权限管理比较严格,可以在指定的项目,给予主程权限),配置服务调用token,给jenkins使用。

- jenkins配置凭据,将gitlab token 添加到全局凭据中.

- 配置邮件通知,详细教程可以参考<<jenkins配置邮件扩展插件,实现邮件发送通知>>


- 编写共享库,实现获取对git api的常见操作。
src/com/devops/gitlab.groovy
package com.devops
// 此处参考zeyang的jenkins封装的源码,也可以去gitlab的api文档中获取当前版本的api接口,根据实际需求进行编写。https://your-gitlab-url/help/api/README.md下有所有的api接口说明文档
//封装HTTP请求
def HttpReq(reqType,reqUrl,reqBody){
// 定义gitlab的接口地址
def gitServer = "https://gitlab.url.com/api/v4"
// 此处使用之前在jenkins中创建的gitlab token的凭据的id名称
withCredentials([string(credentialsId: 'jenkins-gitlab-token-text', variable: 'gitlabToken')]) {
result = httpRequest customHeaders: [[maskValue: true, name: 'PRIVATE-TOKEN', value: "${gitlabToken}"]],
httpMode: reqType,
contentType: "APPLICATION_JSON",
consoleLogResponseBody: true,
ignoreSslErrors: true,
requestBody: reqBody,
url: "${gitServer}/${reqUrl}"
//quiet: true
}
return result
}
//允许合并
def AcceptMr(projectId,mergeId){
def apiUrl = "projects/${projectId}/merge_requests/${mergeId}/merge"
HttpReq('PUT',apiUrl,'')
}
- 共享库内编写邮件模板
src/com/devops/email.groovy
package com.devops
//定义邮件内容
def mergeEmail(username, source_branch, target_branch, status, merge_url, emailUser){
emailext body: """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
<img height="100" width="100" src="https://about.gitlab.com/images/devops-tools/gitlab-logo.svg">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td><br />
<b><font color="#0B610B">代码合并处理通知</font></b>
</td>
</tr>
<tr>
<td>
<ul>
<li>提交人: ${username}</li>
<li>源分支: ${source_branch}</li>
<li>目标分支: ${target_branch}</li>
<li>合并结果: ${status} </li>
<li>合并URL: <a href="${merge_url}">${merge_url}</a></li>
</ul>
</td>
</tr>
<tr>
</table>
</body>
</html> """,
subject: "gitlab分支合并结果通知",
to: emailUser
}
- 创建jenkins item, 编写pipeline
@Library("jenkinslib@master") _
def gitlabapi = new com.devops.gitlab()
def toemail = new com.devops.email()
def sourceBranch = source_branch - "refs/heads/"
def targetBranch = target_branch - "refs/heads/"
List branch_list = ["fat", 'fat1', 'fat2', 'uat']
currentBuild.description = "Trigger by ${username} ${sourceBranch} merge to ${targetBranch}"
if (!branch_list.contains(target_branch)) {
error "目标分支不在${branch_list}中"
}
pipeline {
agent any
options {
buildDiscarder(logRotator(numToKeepStr: '60')) // 保留构建记录为60次
disableConcurrentBuilds() // 关闭同时执行流水线
timeout(time: 1, unit: 'HOURS') // 设置流水线超时时间
timestamps() //日志打印时间戳
}
triggers {
// 参考链接1: https://www.jianshu.com/p/7873d2f0dd3e
// 参考链接2: https://plugins.jenkins.io/generic-webhook-trigger/
GenericTrigger(
genericVariables: [
[key: 'username', value: '$.user.username'],
[key: 'user_email', value: '$.user.email', ],
[key: 'source_branch', value: '$.object_attributes.source_branch', ],
[key: 'target_branch', value: '$.object_attributes.target_branch', ],
[key: 'project_id', value: '$.project.id', ],
[key: 'megre_id', value: '$.object_attributes.iid', ],
[key: 'megre_state', value: '$.object_attributes.state',],
[key: 'merge_url', value: '$.object_attributes.url']
],
token: 'jenkins-gitlab-mr' ,
causeString: ' Triggered on $branch' ,
silentResponse: true,
printContributedVariables: true,
printPostContent: true,
)
}
stages {
stage('merge') {
when {
equals expected: 'opened', actual: megre_state
}
steps {
script {
try {
gitlabapi.AcceptMr(project_id, megre_id)
toemail.mergeEmail(username, source_branch, target_branch, '合并成功', merge_url, user_email)
} catch(Exception ex) {
toemail.mergeEmail(username, source_branch, target_branch, '合并失败', merge_url, user_email)
error ex
}
}
}
}
stage('do not merge') {
when {
not {
equals expected: 'opened', actual: megre_state
}
}
steps {
script {
println("mergeState is not opened, nothing to do.")
}
}
}
}
}
- 测试结果


