流程图语法

最后更新于:

Jenkins 企微群构建通知(@构建者)实现方法

一、概述

1.1 流程概述:

flowchart TD A["流水线结束 post.always"] --> B["生成构建状态 status/currentResult"] A["流水线结束 post.always"] --> Q["获取到构建用户名称 name"] B --> C["项目环境是否为:test 环境 PACKAGING_ENV == test?"] Q --> C["项目环境是否为:test 环境 PACKAGING_ENV == test?"] C -- "是" --> F["调用 wechat.notifyBuild"] C -- "否" --> G["跳过企业微信,开发环境 不生成构建通知"] F --> K["调用get-user-iphone.sh 脚本,解析用户名称,获取到手机号"] K --> L["拼接消息体 textPayload"] L --> M["curl POST webhook_url"]

1.2 环境前提概述:

  • 需要维护一份人员名单,企微@群员 可通过手机号的方式,来@群员,所以手机号是唯一。

    image-20260305155012736

  • 人员名单脚本:【存放在 jenkins 服务器上,方便流水线直接调用】

 1#get-user-iphone.sh
 2
 3#!/usr/bin/env bash
 4set -euo pipefail
 5
 6name="${1:-}"
 7
 8case "$name" in
 9  "admin") echo "13000000000" ;;
10  "张三") echo "13000000001" ;;
11  "李四") echo "13000000002" ;;
12  "王五") echo "13000000003" ;;
13  *)
14    echo ""
15    ;;
16esac

这里人员名单的用户名我们要维护好在 jenkins 上,这个用户名是 jenkins 上的全局唯一。

【关于如何批量创建用户,导入用户,可以参考:https://opforge.srebro.cn/devops/jenkins/09/09-1.html】

jenkins 流水线在构建结束之后 ,可以获取到 构建动作是哪个用户触发的。就执行这个脚本 来获取到对应用户的手机号,再执行 webhook 的发送构建通知动作。

二、实现步骤

本案例采用 jenkins 共享库的方式来实现

2.1 入口配置

Jenkinsfile 中通过 webhook_url 传入企业微信机器人 Webhook,并调用共享库入口:

1def map = [:]
2map.put('packaging_env','test')
3map.put('webhook_url','https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=81abf299-9946-426a-827a-cef9a00de4ea')
4map.put('git_credentials_id','xxxxxxxxxxxx')
5map.put('default_branch','test')
6map.put('project_choices', projectChoices)
7map.put('project_config', projectConfig)
8
9deploy_front(map)

2.2 通知触发位置

流水线在 post.always 里统一发送邮件与企业微信通知(仅 test 环境发送企业微信通知):

 1post {
 2  always {
 3    wrap([$class: 'BuildUser']) {
 4      script {
 5        if (!(params.SKIP_PIPELINE || (currentBuild.rawBuild?.getCause(hudson.triggers.TimerTrigger$TimerTriggerCause) != null))) {
 6          if (env.PROJECT_WORKSPACE) {
 7            ws(env.PROJECT_WORKSPACE) {
 8              cleanWs()
 9            }
10          }
11          def status = "${currentBuild.currentResult}"
12          email.Email(status)
13          if (env.PACKAGING_ENV == 'test') {
14            wechat.notifyBuild(env.WEBHOOK_URL, [
15              status: status,
16              buildTime: env.BUILD_TIME,
17              projectName: params.PROJECT_NAME,
18              gitBranch: params.GIT_BRANCH
19            ])
20          }
21        }
22      }
23    }
24  }
25}

2.3 手机号解析与 @人逻辑

通知对象基于 Jenkins 的构建者用户名 env.BUILD_USER

  1. 优先调用 Jenkins 服务器上的脚本 /opt/application/get-user-iphone.sh 获取手机号
  2. 如果脚本未返回手机号,则尝试读取 USER_MOBILE_MAP_JSON 环境变量
  3. 再否则尝试读取工作区内的 user_mobile_map.json 文件
  4. 得到手机号后,加入 mentioned_mobile_list,同时 mentioned_list 里包含用户名

脚本示例(Jenkins 服务器上):

 1#get-user-iphone.sh
 2
 3#!/usr/bin/env bash
 4set -euo pipefail
 5
 6name="${1:-}"
 7
 8case "$name" in
 9  "admin") echo "13000000000" ;;
10  "张三") echo "13000000001" ;;
11  "李四") echo "13000000002" ;;
12  "王五") echo "13000000003" ;;
13  *)
14    echo ""
15    ;;
16esac

2.4 企业微信发送实现

核心发送逻辑在共享库 org.devops.wechat

 1def notifyBuild(webhookUrl, args = [:]) {
 2  def buildStatus = args.status ?: (currentBuild?.currentResult ?: 'UNKNOWN')
 3  def statusIcon = buildStatus == 'SUCCESS' ? '✅' : '❌'
 4  def buildUser = env.BUILD_USER ?: '系统自动'
 5  def projectName = (args.projectName ?: env.PROJECT_NAME)
 6  def gitBranch = args.gitBranch ?: (env.GIT_BRANCH ?: '')
 7
 8  def resolveMobileForUser = { username ->
 9    def mobile = null
10    try {
11      try {
12        def out = sh(script: """if [ -x /opt/application/get-user-iphone.sh ]; then /opt/application/get-user-iphone.sh '${username}' || true; fi""", returnStdout: true).trim()
13        if (out) {
14          mobile = out
15        }
16      } catch (e1) {
17        echo "获取手机号失败: ${e1}"
18      }
19      if (!mobile && env.USER_MOBILE_MAP_JSON && env.USER_MOBILE_MAP_JSON.trim()) {
20        def map = new groovy.json.JsonSlurper().parseText(env.USER_MOBILE_MAP_JSON)
21        mobile = map[username]
22      } else if (!mobile && fileExists('user_mobile_map.json')) {
23        def json = readFile(file: 'user_mobile_map.json')
24        def map = new groovy.json.JsonSlurper().parseText(json)
25        mobile = map[username]
26      }
27    } catch (e) {
28      echo "解析手机号映射失败: ${e}"
29    }
30    return mobile
31  }
32
33  def userMobile = resolveMobileForUser(buildUser)
34  def notifyNames = new LinkedHashSet<String>()
35  notifyNames << buildUser
36  def notifyMobiles = new LinkedHashSet<String>()
37  if (userMobile) {
38    notifyMobiles << userMobile
39  }
40
41  def textPayload = [
42    msgtype: "text",
43    text   : [ content: "${statusIcon} 构建状态: ${buildStatus}\n项目名称: ${projectName}\n构建分支: ${(gitBranch ?: 'master')}" ]
44  ]
45  if (!notifyMobiles.isEmpty()) {
46    textPayload.text.mentioned_mobile_list = notifyMobiles.toList()
47  }
48  textPayload.text.mentioned_list = notifyNames.toList()
49  writeFile file: 'text_message.json', text: groovy.json.JsonOutput.toJson(textPayload)
50
51  sh """
52    curl -s -H "Content-Type: application/json" -X POST -d @text_message.json "${webhookUrl}"
53    rm -f text_message.json
54  """
55}

三、最终效果

image-20260305161621512

image-20260305161649066

推荐使用微信支付
微信支付二维码
推荐使用支付宝
支付宝二维码
最新文章