jenkins-集成gitlab

阅读量: zyh 2020-04-25 11:12:11
Categories: > Tags:

简要

jenkins集成gitlab有两种方式:

本文档介绍通用 webhook 触发器,更加灵活。

安装通用 webhook 插件

Generic Webhook Trigger

代码 demo

将demo放在 http://git.xxx.com/devops/simple-java-maven-app.git

git clone https://github.com/Spinestars/simple-java-maven-app
cd simple-java-maven-app
git remote add origin http://git.xxx.com/devops/simple-java-maven-app.git
git add .
git commit -m "Initial commit"
git push -u origin master

maven工具

参见文章:jenkins-构建工具集成

共享库 jenkinslib

http://git.xxx.com/devops/jenkinslib.git

jenkinslib在jenkins的配置,参见文章:jenkins☞003共享库

src/org/devops/build.groovy

👙此代码的执行一定需要先配置 jenkins maven 全局工具

package org.devops

def Build(buildType, buildShell){
	def buildTools = ["mvn":"M2", "npm":"NPM"]
	
	println("当前选择构建类型:${buildType}")
	buildHome = tool buildTools[buildType]
	
	if ("${buildType}" == "npm"){
		sh """
			export NODE_HOME=${buildHome}
			export PATH=\$NODE_HOME/bin:\$PATH
			${buildHome}/bin/${buildType} ${buildShell}"""
	}else{
		sh "${buildHome}/bin/${buildType} ${buildShell}"
	}
}

src/org/devops/gitlab.groovy

👙jenkins-gitlab-secretText-token 是标题:创建jenkins访问gitlab的凭据 的内容。

package org.devops


// 封装HTTP,用于jenkins向gitlab发起请求
def HttpReq(reqType, reqUrl,reqBody ){
    def gitServer = "http://git.xxx.com/api/v4"
    withCredentials([string(credentialsId: 'jenkins-gitlab-secretText-token', variable: 'GITLABTOKEN')]) {
        response = httpRequest acceptType: 'APPLICATION_JSON_UTF8', 
                          consoleLogResponseBody: true, 
                          contentType: 'APPLICATION_JSON_UTF8', 
                          customHeaders: [[maskValue: false, name: 'PRIVATE-TOKEN', value: "${GITLABTOKEN}"]], 
                          httpMode: "${reqType}", 
                          url: "${gitServer}/${reqUrl}", 
                          wrapAsMultipart: false,
                          requestBody: "${reqBody}"

    }
    return response
}


// 变更 gitlab 提交状态
def ChangeCommitStatus(projectId,commitSha,status){
    commitApi = "projects/${projectId}/statuses/${commitSha}?state=${status}"
    response = HttpReq('POST', commitApi, '')
    println(response)
    return response
}

src/org/devops/tools.groovy

package org.devops

def toEmail(userEmail,status){
 	emailext body: """
            <!DOCTYPE html> 
            <html> 
            <head> 
            <meta charset="UTF-8"> 
            </head> 
            <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0"> 
                <img src="http://<jenkins>/static/0eef74bf/images/headshot.png">
                <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>项目名称:${JOB_NAME}</li>         
                                <li>构建编号:${BUILD_ID}</li> 
                                <li>Git用户:${userFullName}(${userName})</li>
                                <li>Git分支:${branch}</li>
                                <li>CommitShortID:${commitShort}</li> 
                                <li>CommitID:${commitSha}</li>
                                <li>构建状态: ${status}</li>                         
                                <li>项目地址:<a href="${BUILD_URL}">${BUILD_URL}</a></li>    
                                <li>构建日志:<a href="${BUILD_URL}console">${BUILD_URL}console</a></li> 
                            </ul> 
                        </td> 
                    </tr> 
                    <tr>  
                </table> 
            </body> 
            </html>  """,
            subject: "Jenkins-${JOB_NAME}项目构建信息 ",
            to: userEmail
}

Pipeline文件 jenkinsfile

定义一个 jenkinsfile 库,存 jenkinsfile 文件。

http://git.xxx.com/devops/jenkinsfiles.git

#!groovy

@Library('jenkinslib@master') _

def build = new org.devops.build()
def gitlab = new org.devops.gitlab()
def tools = new org.devops.tools()

//def deploy = new org.devops.deploy()


String buildType = "${env.buildType}"
String buildShell = "${env.buildShell}"
//String deployHosts = "${env.deployHosts}"

String gitUrl = "${env.gitUrl}"
String branchName = "${env.branchName}"


// 当存在 runOpts 变量并且值等于 GitlabPush 的时候,则分支名从 webhook 数据中获取。
if ("${runOpts}" == "GitlabPush") {   
	branchName = branch - 'refs/heads/'	
	env.commitShort = commitSha.take(8)
	currentBuild.description = """Git用户: ${userFullName}(${userName})\nGit分支:${branchName}\nCommit: ${commitShort}"""
	gitlab.ChangeCommitStatus(projectId, commitSha, "running")
}
pipeline{
    agent { node { label "agent01" } }

    stages {
        stage("checkout") {
            steps {
                script {
                    checkout([$class: 'GitSCM', branches: [[name: "${branchName}"]], userRemoteConfigs: [[credentialsId: 'gitlab-jenkins-pull-token', url: "${giturl}"]]])
                }
            }
        }

        stage("build") {
            steps {
                script {
                    build.Build(buildType, buildShell)
                }
            }
        }
    }
    
    post {
    	always {
    		script {
    			println('always')
    		}
    	}
    	
    	success {
    		script {
    			println('success')
				gitlab.ChangeCommitStatus(projectId, commitSha, "success")
				tools.toEmail(userEmail, "success")
    		}
    	}
    	
    	failure {
    		script {
    			println('failure')
				gitlab.ChangeCommitStatus(projectId, commitSha, "failed")
				tools.toEmail(userEmail, "failed")
    		}
    	}
    	
    	aborted {
    		script {
    			println('aborted')
				gitlab.ChangeCommitStatus(projectId, commitSha, "canceld")
				tools.toEmail(userEmail, "aborted")
    		}
    	}
    }
}

jenkins web 配置

如果一切配置好,则 jenkins 构建的时候会输出 gitlab 通过 webhook 传递过来的信息,如下示例:

{"object_kind":"push","event_name":"push","before":"332e5cfeff94b9dec3d766955c533d2ab7e62bf4","after":"39cf0cd49ff6f156e76a171b6b8ab85ce512a8e5","ref":"refs/heads/master","checkout_sha":"39cf0cd49ff6f156e76a171b6b8ab85ce512a8e5","message":null,"user_id":1,"user_name":"zyh","user_username":"root","user_email":"","user_avatar":"http://git.xxx.com/uploads/-/system/user/avatar/1/avatar.png","project_id":68,"project":{"id":68,"name":"simple-java-maven-app","description":"","web_url":"http://git.xxx.com/devops/simple-java-maven-app","avatar_url":null,"git_ssh_url":"ssh://[email protected]:2222/devops/simple-java-maven-app.git","git_http_url":"http://git.xxx.com/devops/simple-java-maven-app.git","namespace":"devops","visibility_level":0,"path_with_namespace":"devops/simple-java-maven-app","default_branch":"master","ci_config_path":"","homepage":"http://git.xxx.com/devops/simple-java-maven-app","url":"ssh://[email protected]:2222/devops/simple-java-maven-app.git","ssh_url":"ssh://[email protected]:2222/devops/simple-java-maven-app.git","http_url":"http://git.xxx.com/devops/simple-java-maven-app.git"},"commits":[{"id":"39cf0cd49ff6f156e76a171b6b8ab85ce512a8e5","message":"Update pom.xml","title":"Update pom.xml","timestamp":"2022-05-08T01:37:58+00:00","url":"http://git.xxx.com/devops/simple-java-maven-app/-/commit/39cf0cd49ff6f156e76a171b6b8ab85ce512a8e5","author":{"name":"zyh","email":"[email protected]"},"added":[],"modified":["pom.xml"],"removed":[]},{"id":"2bf69c80b1713021be07700c4598db4884de8e1a","message":"Update pom.xml","title":"Update pom.xml","timestamp":"2022-05-08T01:36:22+00:00","url":"http://git.xxx.com/devops/simple-java-maven-app/-/commit/2bf69c80b1713021be07700c4598db4884de8e1a","author":{"name":"zyh","email":"[email protected]"},"added":[],"modified":["pom.xml"],"removed":[]},{"id":"332e5cfeff94b9dec3d766955c533d2ab7e62bf4","message":"Update README.md","title":"Update README.md","timestamp":"2020-09-06T18:16:16+08:00","url":"http://git.xxx.com/devops/simple-java-maven-app/-/commit/332e5cfeff94b9dec3d766955c533d2ab7e62bf4","author":{"name":"lizeyang","email":"[email protected]"},"added":[],"modified":["README.md"],"removed":[]}],"total_commits_count":3,"push_options":{},"repository":{"name":"simple-java-maven-app","url":"ssh://[email protected]:2222/devops/simple-java-maven-app.git","description":"","homepage":"http://git.xxx.com/devops/simple-java-maven-app","git_http_url":"http://git.xxx.com/devops/simple-java-maven-app.git","git_ssh_url":"ssh://[email protected]:2222/devops/simple-java-maven-app.git","visibility_level":0}}

👙通过 json.cn 格式化。

通过示例,我们可以在 jenkins webhook 通用触发器里配置变量来获取所需的数据,例如本次 git 提交人、本次 git commit等

同时还会输出 jenkins webhook 触发器用户配置的一些参数或者变量,示例:

Contributing variables:

    branch = refs/heads/dev
    runOpts = GitlabPush
    runOpts_0 = GitlabPush

branch 变量就是用户自定义的获取 gitlab 通过 webhook 传递过来的 json 数据的变量。

runOpts 变量就是用户自定义的 webhook get 参数。

webhook 通用触发器配置

通过pipeline方式设置 webhook 通用触发器配置。

image-20220509113201663

通过 jenkins webui 设置通用触发器配置。

设置webhook传递过来的post内容的映射变量

具体的设置可以通过上述的 post content 示例来对照配置。

需要设置的变量有:

image-20220508101641308

设置webhook get参数和token

token可以随便写

image-20220508101703780

开启各种控制台日志输出

image-20220508101721454

经过上述配置,最终生成的 webhook 地址是:

http:///generic-webhook-trigger/invoke?token=test.simple-java-maven-app&runOpts=GitlabPush

checkout 方式获取Jenkinsfile

👙需要先配置 jenkins 拉取 gitlab 代码的凭证

凭证类型:username with password

凭证id:jenkins-gitlab-pull-code

image-20220508115409236

gitlab web 配置

将webhook地址填入网址

image-20220508102131452

增加构建描述信息

Jenkins构建工程中对应的一些变量:

http://jkspj.pengwin.com:18080/job/test.simple-java-maven-app/pipeline-syntax/globals#currentBuild

我们可以给这些变量赋值,从而在jenkins pipeline或者jenkins web上展示一些自定义信息。

例如:

image-20220508115119649

👙在上面的jenkinsfile已经包含了上面截图的效果.

if ("${runOpts}" == "GitlabPush") {   
    branchName = branch - 'refs/heads/'
	commitShort = commitSha.take(8)
    currentBuild.description = """Git用户: ${userFullName}(${userName})\nGit分支:${branchName}\nCommit: ${commitShort}"""
}

将构建状态反馈给gitlab

如果你用 gitlab 插件,则这个实现不难,但如果用通用 webhook 触发器,则这个实现是需要调用 gitlab api 接口。

相关的 gitlab api 接口文档:

https://docs.gitlab.com/ee/api/commits.html#post-the-build-status-to-a-commit

官方示例:

curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/17/statuses/18f3e63d05582537db6d183d9d557be09e1f90c8?state=success"

gitlab build 状态:pending, running, success, failed, canceled

👙可以看到 gitlab 的状态单词和 jenkins 的状态单词是不一样的,因此jenkins pipeline里调用 gitlab api 传递的状态要用 gitlab 的状态单词。

创建jenkins访问gitlab的凭据

  1. 创建 gitlab 专属用户:jenkins
  2. 将所有需要自动化的仓库授予 jenkins,权限为 Maintainer
  3. 用 jenkins 用户登录 gitlab 网站,并创建个人令牌,令牌权限是 api
  4. 登录 jenkins 网站,创建凭据:
    • 凭据类型:secret text
    • 凭据ID:jenkins-gitlab-secretText-token

过滤 gitlab push 请求

默认新建分支都会触发jenkins构建。

通用webhook触发器,可以进行如下设定:

👙给定的字符串,从 post json 中获取,这样,正则就可以过滤推送的事件。

官方提供的例子:https://github.com/jenkinsci/generic-webhook-trigger-plugin/blob/master/src/test/resources/org/jenkinsci/plugins/gwt/bdd/gitlab/gitlab-push-ignore-create-remove-branch.feature

    Given the following generic variables are configured:
      | variable        | expression               | expressionType  | defaultValue | regexpFilter  |
      | object_kind     | $.object_kind            | JSONPath        |              |               |
      | before          | $.before                 | JSONPath        |              |               |
      | after           | $.after                  | JSONPath        |              |               |
      | ref             | $.ref                    | JSONPath        |              |               |
      | git_ssh_url     | $.repository.git_ssh_url | JSONPath        |              |               |

    Given filter is configured with text: $object_kind $before $after
    Given filter is configured with expression: ^push\s(?!0{40}).{40}\s(?!0{40}).{40}$

第一组是根据 post json 设置的变量

第二组是根据变量定义的字符串

第三组是匹配字符串的正则

对应的 jenkins web 配置截图:

image-20220508141037178

配置邮件发送

jenkins web 配置 smtp 服务器,见截图:

image-20220508152447885

邮件模板和发邮件的代码,参见标题:jenkinslib.src/org/devops/tools.groovy

pipeline调用,参见标题: jenkinsfile.post()部分

合并分支触发流水线

👙依赖标题:将构建状态反馈给gitlab。

只有当源分支最后一次流水线成功的时候,才允许合并到目标分支。

实现上述需求,只需要开启gitlab的功能即可:

image-20220508154034888

效果如下:

image-20220508155409563