初级jenkins-pipeline共享库使用

使用jenkins pipeline共享库,同一框架的应用基本都可以使用同一个jenkinsfile模板,更改共享库即可应用到所有使用此库的jenkins-job。目前没有用到vars目录,但完全能够满足我们日常需求,使用方式上较low,下面列出了定义的部分函数,仅供参考。

共享库目录结构:

jenkins-pipeline-libraries git:(master) ✗ tree .
.
├── jenkins-ci
│   └── jenkinsfile-java
├── out
│   └── production
├── src
│   ├── ops
│   │   └── jk
│   │       ├── appDetail.groovy
│   │       └── tools.groovy
│   └── pipeline.gdsl
└── vars
    └── pipelineCfg.groovy

7 directories, 6 files

appDetail.groovy文件里面部分函数如下:

// 获取时间 格式:20201208200419
def getTime() {
    return new Date().format('yyyyMMddHHmmss')
}

//格式化输出,需安装ansiColor插件
def printMes(value,level){
    colors = ['warning' : "\033[43;30m ==> ${value} \033[0m",
              'info'    : "\033[42;30m ==> ${value} \033[0m",
              'error'   : "\033[41;37m ==> ${value} \033[0m",
    ]
    ansiColor('xterm') {
        println(colors[level])
    }
}

// 远程主机上通过ssh执行命令
def runCmd(ip, user, pass, command, sshArgs = '') {
    return sh(returnStdout: true,
            script: "sshpass -p ${pass} ssh ${sshArgs} -oStrictHostKeyChecking=no -l ${user} ${ip} \"${command}\"")
}

// 格式化输出当前构建分支用于镜像tag
def getBranch() {
    def formatBranch = "${env.GIT_BRANCH}".replaceAll('origin/', '').replaceAll('/', '_')
    assert formatBranch != 'null' || formatBranch.trim() != ''
    return formatBranch
}

// 获取分支commitId
def getSHA() {
    def commitsha = "${env.GIT_COMMIT}".toString().substring(0, 6)
    assert commitsha != 'null' || commitsha.trim() != ''
    return commitsha
}

// 获取commit时间
def getCommitTime() {
    out = sh(returnStdout: true, script: "git show -s --format=%ct ${env.GIT_COMMIT}")
    def commitTime = out.toString().trim()
    assert commitTime != 'null' || commitTime.trim() != ''
    return commitTime
}

//获取git commit变更集
def getChangeString() {
    def result = []
    def changeString = []
    def authors = []
    def MAX_MSG_LEN = 20
    def changeLogSets = currentBuild.changeSets
    for (int i = 0; i < changeLogSets.size(); i++) {
        def entries = changeLogSets[i].items
        for (int j = 0; j < entries.length; j++) {
            def entry = entries[j]
            truncatedMsg = entry.msg.take(MAX_MSG_LEN)
            commitTime = new Date(entry.timestamp).format("yyyy-MM-dd HH:mm:ss")
            changeString << "${truncatedMsg} [${entry.author} ${commitTime}]\n"
//            changeString += ">- ${truncatedMsg} [${entry.author} ${commitTime}]\n"
            authors << "${entry.author} "
        }
    }
    if (!changeString) {
        changeString = ">- No new changes"
        authors = "No new changes, No authors"
        result << changeString << authors
        return result
    } else {
        if (changeString.size() >5) {
            changeString = changeString[0,4]
            changeString.add("......")
        }
        changeString = ">-" + changeString.join(">-")
        authors.join(", ")
        result << changeString << authors.unique()
        return result
    }
}

// java项目sonar扫描
def sonarScanner() {
    def sonarDir = tool name: 'scanner-docker', type: 'hudson.plugins.sonar.SonarRunnerInstallation'
    printMes("sonarqube scanner started. sonarHomeDir: ${sonarDir}", "info")
    withSonarQubeEnv(credentialsId: 'comp-sonar') {
        sh "${sonarDir}/bin/sonar-scanner \
           -Dsonar.projectKey=${projectName} \
           -Dsonar.projectName=${projectName} \
           -Dsonar.ws.timeout=60 \
           -Dsonar.sources=. \
           -Dsonar.sourceEncoding=UTF-8 \
           -Dsonar.java.binaries=. \
           -Dsonar.language=java \
           -Dsonar.java.source=1.8"
    }
    printMes("${projectName} scan success!", "info")
}

// 发送钉钉消息,按需自定义,需要安装httpRequest插件
def dingMes(){
    def user = ''
    def description = ''
    def specificCause = currentBuild.getBuildCauses('hudson.model.Cause$UserIdCause')
    if (specificCause) {
        user = "$specificCause.userName"
        description = "$specificCause.shortDescription"
    }
    def DingTalkHook = "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxx"
    def ChangeStrings = getChangeString()[0]
    def ChangeAuthors = getChangeString()[1]
    def ReqBody = """{
            "msgtype": "markdown",
            "markdown": {
                "title": "项目构建信息",
                "text": "### [${JOB_NAME}](${BUILD_URL})\\n---\\n>- 分支:**${env.GIT_BRANCH}**\\n> - 执行人: **${user}**\\n>- 描述: ${description}\\n>#### 作者:\\n>- **${ChangeAuthors}**\\n>#### 更新记录: \\n${ChangeStrings}"
            },
            "at": {
                "atMobiles": [], 
                "isAtAll": false
                }
            }"""
    httpRequest acceptType: 'APPLICATION_JSON_UTF8',
            consoleLogResponseBody: true,
            contentType: 'APPLICATION_JSON_UTF8',
            httpMode: 'POST',
            ignoreSslErrors: true,
            requestBody: ReqBody,
            responseHandle: 'NONE',
            url: "${DingTalkHook}",
            timeout: 5,
            quiet: true
}

//邮件通知
def emailSuccess(){
    emailext(
        subject: "✅${env.JOB_NAME} - 更新成功",
        body: '${SCRIPT, template="email-html.template"}',
        recipientProviders: [requestor(), developers()]
    )
}

def emailFailure(){
    emailext(
        subject: "❌${env.JOB_NAME} - 更新失败",
        body: '${SCRIPT, template="email-html.template"}',
        recipientProviders: [requestor(), developers()]
    )
}

我们jenkinsjobname都是按照环境-项目名的格式命名的,例如 dev-potatoappDetail.groovy中下面函数部分依赖了此jobname

// 通过jenkins-jobname获取项目名称
def getProjectName() {
    String projectName
    String jobName = "${env.JOB_NAME}"
    if ("${env.JOB_NAME}".contains('front-end')) {
        jobName = "${env.JOB_NAME}".replaceAll('front-end/', '')
    }
    projectName = jobName.substring(jobName.indexOf('-') + 1)
    assert projectName != 'null' || projectName.trim() != ''
    return projectName
}

// 通过jobname获取环境名称
def getNameSpace() {
    String namespace
    if ("${env.JOB_NAME}".contains('front-end')) {
        def realJobName = "${env.JOB_NAME}".replaceAll('front-end/', '')
        namespace = realJobName.split('-')[0]
    }else {
        namespace = "${env.JOB_NAME}".split('-')[0]
    }
    assert namespace != 'null' || namespace.trim() != ''
    return namespace
}

// 定义镜像tag,输出格式 xxx.yy.com/comp/bnu:o_hotfix_update_publish_wwefan_0808_78ffds_10
def getImgTag() {
    String regUrl
    def imageTag = ''
    def namespace = getNameSpace()

    if ("${namespace}" != 'pre' && "${namespace}" != 'online') {
        regUrl = 'xxx.yy.net'
    }else {
        regUrl = 'xxx.yy.com'
    }

    def rollTag = "${params.ROLLTAG}"  // jenkins通过参数构建过程传进来的镜像回滚tag
    def gitProjectName = "${env.GIT_URL}".substring("${env.GIT_URL}".lastIndexOf('/') + 1, "${env.GIT_URL}".lastIndexOf('.git'))
    def projectNamespace = "${env.GIT_URL}".substring("${env.GIT_URL}".lastIndexOf(':') + 1, "${env.GIT_URL}".lastIndexOf('/'))
    def jkProjectName = getProjectName()
    if (jkProjectName != gitProjectName) {
        printMes('the jobname of Jenkins does not match the project name of Gitlab', 'warn')
//        error('the jobname of Jenkins does not match the project name of Gitlab')
    }

    if (rollTag) {
        imageTag = "${regUrl}/${projectNamespace}/${gitProjectName}:${rollTag}"
    } else {
        def commitShortSha = getSHA()
        // def COMMIT_TIME = getCommitTime()
        def formatBranch = getBranch()
        imageTag = "${regUrl}/${projectNamespace}/${gitProjectName}:${namespace}_${formatBranch}_${commitShortSha}_${env.BUILD_NUMBER}"
    }
    printMes(imageTag, 'info')
    return imageTag
}

// 构建docker容器镜像推送到仓库
def dockerBuild() {
    printMes("开始构建docker镜像", 'info')
    def namespace = getNameSpace()
    def projectName = getProjectName()
    String regUrl
    if ("${namespace}" != 'pre' && "${namespace}" != 'online') {
        regUrl = 'https://xxx.yy.net'
    }else {
        regUrl = 'https://xxx.oo.com'
    }
    if (!params.ROLLTAG) {
        def core = getCore()
        def dockerimg = ''
        docker.withRegistry("${regUrl}", "comp-image") {
            dockerimg = docker.build(getImgTag(), "-f Dockerfile ./${projectName}-${core}/target/")
            dockerimg.push()
        }
        printMes("镜像已推送到 ${regUrl}", 'info')
    }

}

java项目jenkinsfile如下:

#!groovy
@Library('jenkinslib') _

def mytool = new ops.jk.tools()
def app = new ops.jk.appDetail()
def appMap = app.getAppMap()
def projectName = app.getProjectName()
def namespace = app.getNameSpace()

pipeline {
    environment {
        CI_PROJECT_NAME = app.getProjectName()
        IMG_TAG = app.getImgTag()
        NAMESPACE = app.getNameSpace()
        APPTYPE = app.getAppType()
    }

    options {
        timestamps()
        timeout(time: 20, unit: 'MINUTES')
    }

    agent {
        docker {
            image 'xxx.yy.com/library/maven:3.6-git-kube16'
            registryUrl 'https://xxx.yy.com'
            registryCredentialsId 'comp-image'
            label app.getAppLabel()
            args '-v /var/run/docker.sock:/var/run/docker.sock -v /home/admin/:/home/admin/'
        }
    }

    parameters {
        string(name: 'ROLLTAG', defaultValue: '', description: '是否回滚到之前的版本,输入镜像tag进行回滚')
        choice(name: 'SONAR_SCANNER', choices: ['不执行', '执行'], description: '是否执行代码扫描')
    }

    stages {
        stage('mvnPackage'){
            when { expression { return ! params.ROLLTAG } }
            steps {
                script {
                    app.mvnPackage()
                }
            }
        }
        stage('sonar-scanner'){
            when { expression { return params.SONAR_SCANNER == '执行' } }
            steps {
                script {
                    app.sonarScanner()
                }
            }
        }
        stage("Quality Gate") {
            when { expression { return params.SONAR_SCANNER == '执行' } }
            steps {
                timeout(time: 5, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
        stage('dockerBuild') {
            steps {
                script {
                    app.dockerBuild()
                }
            }
        }
        stage('publish') {
            steps {
                script {
                    app.publish()
                }
            }
        }
    }

    post {
        success {
            script{
                mytool.emailSuccess()
            }
        }
        failure {
            script{
                mytool.emailFailure()
            }
        }
    }
}

钉钉通知消息示例:

初级jenkins-pipeline共享库使用

邮件通知示例:

初级jenkins-pipeline共享库使用

猜你喜欢

转载自blog.51cto.com/weifan/2588576