版本:Gitlab Community Edition 10.8.3、Jenkins 2.249.1 、Sonar 6.7.7

gilab 版本可能会有事件消息的不一样,其他应该不影响的。

一、Gitlab 配置

在工程项目下,settings > Integrations

添加url http://${ip}:${port}/jenkins/generic-webhook-trigger/invoke?token=${在 jenkins 中定义的token}

勾选push、tag 事件(触发)




gitlab打 tag gitlab打tag触发_git


Gitlab 上每次push、打tag,都会触发事件,调用 Jenkins 的 webhook ,消息格式如下:


{
    "object_kind":"push",
    "event_name":"push",
    "before":"98a75a40e01978568c7e703bdf9aa0db8387679f",
    "after":"cd5100f0737b5c046a358ba78ab961d01570b72d",
    "ref":"refs/heads/${分支名称}",
    "checkout_sha":"cd5100f0737b5c046a358ba78ab961d01570b72d",
    "message":null,
    "user_id":1529,
    "user_name":"张三",
    "user_username":"xx",
    "user_email":"xx@xx.cn",
    "user_avatar":"${用户的gitlab主页}",
    "project_id":111,
    "project":{
        "id":111,
        "name":"${项目名称}",
        "description":"xx需求",
        "web_url":"${project_url}",
        "avatar_url":null,
        "git_ssh_url":"${project_ssh_url}",
        "git_http_url":"****",
        "namespace":"${group}",
        "visibility_level":0,
        "path_with_namespace":"${group}/${project}",
        "default_branch":"master",
        "ci_config_path":null,
        "homepage":"${project_url}",
        "url":"${project_ssh_url}",
        "ssh_url":"${project_ssh_url}",
        "http_url":"${project_url}"
    },
    "commits":[
        {
            "id":"xxx",
            "message":"xxx改造",
            "timestamp":"2021-01-25T10:09:41+08:00",
            "url":"https://gitlab.***.cn/${group}/${project}/commit/cd5100f0737b5c046a358ba78ab961d01570b72d",
            "author":{
                "name":"xxx",
                "email":"xxx@xx.cn"
            },
            "added":[],
            "modified":[
                "module/src/main/java/cn/xxx/xxlog/xxx.java"
            ],
            "removed":[]
        }
    ],
    "total_commits_count":1,
    "repository":{
        "name":"${project}",
        "url":"${project_ssh_url}",
        "description":"xx需求",
        "homepage":"${project_url}",
        "git_http_url":"${project_url}",
        "git_ssh_url":"${project_ssh_url}",
        "visibility_level":0
    }
}


PS:上面消息中,用变量和xxx代替了实际值,手动打码~

二、Jenkins 配置

这里我用了两个Jenkins 任务,一个pipeline 用来处理消息,一个maven 任务用来构建指定模块(可以合并到pipeline,不过我对pipeline的sonar 插件不熟,所以用了独立模块来搞)

处理git 消息事件pipeline 配置如下

  1. 获取触发用户的用户名、邮箱、分支、改动点等数据(参考上一步的消息来获取)


gitlab打 tag gitlab打tag触发_2d_02


gitlab打 tag gitlab打tag触发_2d_03


2. 配置token(gitlab 配置中要用到),用来识别触发当前任务,如果多个jenkins 任务都配置了相同的token,那么gitlab 事件会触发所有这些任务


gitlab打 tag gitlab打tag触发_gitlab新建分支提交代码_04


3. 配置pipeline 脚本


pipeline {
    agent any
    
    environment {
        // 定义全局变量
        REPOSITORY = 'https://gitlab.xxx.cn/xxx/xxx.git'
        BRANCH = 'master'
        TRGGER_USER = "master"
        ADDED = 'a' // 不知道为啥不能定义成数组 [] 会报错
        MODIFIED = 'a'
    }
    
    triggers {
        GenericTrigger(
            genericVariables: [
              [key: 'ref', value: '$.ref'],
              [key: 'user_name', value: '$.user_name'],
              [key: 'user_email', value: '$.user_email'],
              [key: 'PROJECT_NAME', value: '$.project.name'],
              [key: 'aaa', value: '$.commits[0].added'],
              [key: 'bbb', value: '$.commits[0].modified'],
              [key: 'full_msg', value: '$']
            ],
            token: 'xxxx' ,
            causeString: 'Triggered on $ref by $user_name' ,
            printContributedVariables: true,
            printPostContent: true
        )
    }

    stages {
        stage('Init') {
            steps {
                script {
                    tmp = "${ref}".split('/');
                    BRANCH = tmp.size() > 0 ? tmp[tmp.size()-1] : tmp[0];
                    TRGGER_USER = "${user_name}";
                    MODIFIED = "${bbb}"
                    ADDED = "${aaa}"
                    println BRANCH;
                    println MODIFIED;
                    
                    wrap([$class: 'BuildUser']) {
                        currentBuild.description = "由【${TRGGER_USER}】发起n 分支:${BRANCH}"
                    }
                    
                }
                sh 'echo -n ' + REPOSITORY + '> REPOSITORY'
            }
        }
        stage('Build') {
            steps {
                // checkout([$class: 'GitSCM', branches: [[name: "FETCH_HEAD"]], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'CloneOption', noTags: true, reference: '', shallow: true]], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '{{拉代码的用户id,jenkins用户凭据那找一个}}', name: "", refspec: "+refs/heads/"+ BRANCH + ":refs/remotes/origin/" + BRANCH, url: REPOSITORY]]])
                // sh "mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent install -Dmaven.test.skip=true"
                
                script  {
                    def MODULES = getModules("${aaa}", "${bbb}")
                    
                    for (module in MODULES){
                        // 每个模块,调用一个独立的任务去扫描代码,也可以直接写到这里
                        build job: 'xxxx-single-module', parameters: [string(name: 'branch', value: BRANCH), string(name: 'MODULE', value: module), string(name: 'TRGGER_USER', value: TRGGER_USER), string(name: 'USER_EMAIL', value: "${user_email}")  ], wait: false
                    }
                }
            
            }
        }
    }
    
}

def getModules(aaa, bbb){
    def MODULES = []
    ADDED = aaa.replaceAll("[|]|"", "").split(",") // 这里不知道为啥数组被换成了字符串
    if (ADDED != ""){
        for (tmp in ADDED){
            a = tmp.split('/')
            String t = a[0]
            println "${aaa}"
            println tmp
            println a.size()
            println a.length
            if (a.size()> 1){
                t = t + '/' + a[1]
            }
            if (!MODULES.contains(t) && t != ""){
                MODULES.add(t)
            }
        }
    }
    MODIFIED = bbb.replaceAll("[|]|"", "").split(",") 
    if (MODIFIED != ""){
        for (tmp in MODIFIED){
            def a = tmp.split('/')
            println MODIFIED
            println "${bbb}"
            println MODIFIED.size()
            String t = a[0]
            if (a.size() > 1)
            t = t + '/' + a[1]
            if (!MODULES.contains(t) && t != ""){
                MODULES.add(t)
            }
        }
    }
    for (tmp in MODULES){
        println tmp
    }
    return MODULES
}


4. sonar扫描的任务配置如下:

1)定义传入参数,用来接收上面的任务传过来的参数


gitlab打 tag gitlab打tag触发_gitlab打 tag_05


2)源码里面拉取指定分支,分支名称由上一个任务传过来,这里我采用的浅拷贝,加快速度


gitlab打 tag gitlab打tag触发_2d_06


3)配置sonar property,这里我是用Pre steps 写一个脚本,把需要的配置写到文件里,sonar 扫描的配置里使用配置文件,这样做的 好处是可以比较灵活定义需要扫描的模块,代码文件等


gitlab打 tag gitlab打tag触发_gitlab打 tag_07


gitlab打 tag gitlab打tag触发_Jenkins_08


4)最后再配置一个Jacoco,后续可以再加个邮件通知


gitlab打 tag gitlab打tag触发_gitlab新建分支提交代码_09


三,sonar上的效果

每次push,Jenkins 任务就会解析改动模块,然后针对模块进行扫描,把结果推到sonar 上,由于我的sonar 是开放的,所以project 可以任意创建,按分支、模块来命名


gitlab打 tag gitlab打tag触发_git_10