LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

利用 Jenkins Pipeline 编写通用脚本捕获 Job 执行信息

问题产生背景

最近企业微信更新了 bot,老板作为腾讯前员工,坚持要在内部办公用起来。当然了,这个支持 markdown 友好通知的机器人,对于能够在即时聊天工具里面告诉其他人一些工作情报,还是蛮方便的。

需要通知的信息

因为只是想试用一下看看效果,所以一开始我们只会拿 Jenkins 执行后的简单信息来告诉相关人员:

  • 谁执行的
  • 执行了什么项目
  • 对应的是什么分支
  • 执行结果是成功还是失败
  • 执行任务的日志详情地址

解决思路

  1. 首先是要确保怎么拿到这些最基础的信息。有些信息是 Freestyle Project 即使加上插件还是很难获取得到,但 Pipeline 的 script 却可以通过一些技巧拿到。
  2. 紧接着要考虑的是,怎么样不在每个项目都重复写一次解析信息任务。所以可以写成一个独立通用的 Pipeline 通知任务,作为目标 Job 无论失败或成功都会执行的 downstream 任务。
  3. 最后,怎么将信息从主任务告诉给我们通用通知任务。这其实作为子任务的 Pipeline 可以一直朔源 upstream 的任务去获取信息。

开始处理

在企业微信里面创建一个 bot,然后该 bot 会提供一个 webhook 的 url。

再者,针对思路的 1, 3 点,我们可以建立一个 Pipeline 任务,去掉勾选的“使用 Groovy 沙盒”,填充最基本的格式:

pipeline {
    agent any
    stages {
        stage ('Notify') {
            steps {
                notify()
            }
            post {
                always {
                    echo "OK"
                }
            }
        }
    }
}

其中的 notify 函数就是我们要处理的东西。接下来就是使用 Groovy 语言来自由处理我们的东西,只要能使用编程语言,一切都将是简单而美好的。需要注意的是,获取构建信息的时候要使用 NonCPS 注解来避免因为 CPS 优化导致的异常,这里定义一些获取基本信息的函数:

// get the first build for fetching the information of project and job
@NonCPS
def getFirstUpstreamBuild() {
    return currentBuild.upstreamBuilds[0]
}
// get the last build for fetching the information of project and job
@NonCPS
def getLastUpstreamBuild() {
    return currentBuild.upstreamBuilds[currentBuild.upstreamBuilds.length - 1]
}
// get job id
def getJobId() {
    return getFirstUpstreamBuild().id
}
// get job status
def getJobStatus() {
    return getLastUpstreamBuild().currentResult
}
// get project name
def getProjectName() {
    return getFirstUpstreamBuild().projectName
}
// get the build variable for fetching the information of user
def getEnv() {
    return getFirstUpstreamBuild().buildVariables
}
// get user name
def getUserName() {
    def build = getFirstUpstreamBuild().rawBuild
    def cause = build.getCause(hudson.model.Cause.UserIdCause.class)
    return cause.getUserName()
}
// get the branch of git
def getGitBranch() {
    return getEnv()['GIT_BRANCH']
}
// get the url of job
def getJobUrl() {
    return getEnv()['BUILD_URL']
}

补个 notify 函数来作为处理函数,这里是做的就是告诉机器人要说什么:

def notice() {
    def targetUrl = "$webhook_url"
    def logUrl = "${getJobUrl()}console"
    def logLevel = getJobStatus() == "SUCCESS" ? "info" : "warning"
    def data = "\
    {\
        \"msgtype\": \"markdown\",\
        \"markdown\": {\
            \"content\": \"<font color=\\\"${logLevel}\\\">${getUserName()}</font> 执行 <font color=\\\"${logLevel}\\\">${getProjectName()}</font> 发布 **<font color=\\\"${logLevel}\\\">${getJobStatus()}</font>**\
            \n> 日志地址:[点击查看](${logUrl})\
            \n> 发布分支:${getGitBranch()}\"\
        }\
    }\
    "
    sh "curl -X POST '${targetUrl}' -H 'Content-Type: application/json' -d '${data}'"
}

最后,在所要通知的构建项目的“Post-build Actions”里面添加个“Build other projects”,内容为前面的 Pipeline 任务,勾选“Trigger even if the build fails”来确保 Pipeline 能处理 upstream 的成功和失败。