利用 Jenkins Pipeline 编写通用脚本捕获 Job 执行信息
问题产生背景
最近企业微信更新了 bot,老板作为腾讯前员工,坚持要在内部办公用起来。当然了,这个支持 markdown 友好通知的机器人,对于能够在即时聊天工具里面告诉其他人一些工作情报,还是蛮方便的。
需要通知的信息
因为只是想试用一下看看效果,所以一开始我们只会拿 Jenkins 执行后的简单信息来告诉相关人员:
- 谁执行的
- 执行了什么项目
- 对应的是什么分支
- 执行结果是成功还是失败
- 执行任务的日志详情地址
解决思路
- 首先是要确保怎么拿到这些最基础的信息。有些信息是 Freestyle Project 即使加上插件还是很难获取得到,但 Pipeline 的 script 却可以通过一些技巧拿到。
- 紧接着要考虑的是,怎么样不在每个项目都重复写一次解析信息任务。所以可以写成一个独立通用的 Pipeline 通知任务,作为目标 Job 无论失败或成功都会执行的 downstream 任务。
- 最后,怎么将信息从主任务告诉给我们通用通知任务。这其实作为子任务的 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 的成功和失败。