使用DingTalk實現共享庫自定義通知器


參考:https://www.ssgeek.com/post/jenkinssharelibrary-shi-jian-zhi-zi-ding-yi-tong-zhi-qi

和原博客不同的點:原博客用的是Http Request插件,本文用的是DingTalk插件

與任何編程環境一樣,在Jenkins流水線中,集中化功能,共享公共代碼和代碼重用都是快速、有效地進行開發的基本技術,這些實踐鼓勵使用標准方法來調用功能,為更復雜的操作創建構建塊並隱藏復雜性。他們還可以用於提供一致性以及鼓勵約定優於配置以簡化任務。

img

Jenkins允許用戶完成所有這些操作的一個關鍵方法就是使用流水線共享庫(pipeline share library)。共享流水線庫是由存儲在代碼倉庫中的代碼組成的,該代碼倉庫由Jenkins自動下載並可供流水線使用。

以上中文描述來自《Jenkins 2權威指南》。
關於jenkins pipeline share library的更多介紹,可以參考官方文檔

1、需求引入

隨着devops理念在公司越來越多的實踐,jenkins等工具的應用場景越來越多,當我們在執行完成某個流水線任務后,常常需要關注的是這個任務為什么執行,執行成功與否等等。於是就需要在執行完流水線后進行一定程度的消息推送,在現今的工作流中消息推送無外乎分為兩大類:郵件和企業溝通協作軟件,相比之下,我們可能更多的會去關注和使用溝通軟件來發送消息而不是通過郵件的方式。而常用的企業溝通協作軟件有以下幾類:騰訊系的企業微信、阿里系的釘釘、字節跳動的飛書等等,當然有能力的企業也會自己研發這類軟件。

本文示例以釘釘為例,通過流水線共享庫實現自定義消息通知器。

2、釘釘機器人

釘釘的群機器人是釘釘群的高級擴展功能。群機器人可以將第三方服務的信息聚合到群聊中,實現自動化的信息同步。例如:通過聚合GitHubGitLab等源碼管理服務,實現源碼更新同步;通過聚合TrelloJIRA等項目協調服務,實現項目信息同步。不僅如此,群機器人支持Webhook協議的自定義接入,支持更多可能性。

自定義釘釘機器人支持以下類型消息類型數據格式的推送,更多定義方法可參考官方的接口文檔

  • text類型
  • markdown類型
  • 整體跳轉ActionCard類型
  • 獨立跳轉ActionCard類型
  • FeedCard類型

釘釘機器人在2019年的下半年進行過升級,在新增機器人時,需要選擇一種安全條件(自定義關鍵詞、加簽、ip地址或ip地址段)來保障自定義機器人的安全。可以理解為即使機器人的token泄漏,如果不知道設置的安全條件是什么,還是無法盜用的。

3、jenkins消息推送插件

這里要提到的是在jenkins插件列表中有一個釘釘插件

簡單對此插件做了下分析:截止目前此插件在20201月份有相應代碼提交,並且發布了2.0版本,從jenkins插件官網中可以看到此版本的插件在在消息中支持了更多內容,效果如下,但是此插件目前還暫不支持流水線中使用
img

在此之前的上一版本提交記錄已經是2018年了,此插件使用方法類似,推送的消息效果如下

此版本支持在流水線中使用,相應內容如下

dingTalk accessToken: "xxx", 
imageUrl: "xxx", 
jenkinsUrl: "https://127.0.0.1:8080", 
message: "項目構建成功", 
notifyPeople: "155xxxx5533"

如上所示,在流水線腳本中配置釘釘機器人token、圖片路徑、jenkins地址、消息內容、要提醒的人手機號碼即可,可以發現,此消息還是有局限性,不夠友好。

因此在沒有編寫插件能力的情況下,我們可以通過更為靈活的自定義流水線共享庫的形式,並且按照釘釘機器人的官方接口文檔,自定義一個消息推送通知器。

4、自定義通知器的實現

4.1、內容定義

無論jenkins任務的構建觸發原因是使用者手動構建或通過代碼推送的自動觸發,往往關注此消息的人群是開發者們。因此通過一段時間的需求調研以及綜合各方的建議,最終將消息推送的內容中包含了以下信息:

  • 應用名稱
  • 構建結果
  • 當前版本
  • 構建發起
  • 持續時間
  • 構建日志
  • 更新記錄(包含用戶提交的短日志,用戶名稱,提交時間)

每次構建結果通知中包含了以上就基本完備。

4.2、共享庫創建

本文不過多介紹共享庫具體的創建與在pipeline流水線中的引用方法,整體來說,共享庫的代碼目錄結構如下

(root)
+- src                     # Groovy source files
|   +- org
|       +- foo
|           +- Bar.groovy  # for org.foo.Bar class
+- vars
|   +- foo.groovy          # for global 'foo' variable
|   +- foo.txt             # help for 'foo' variable
+- resources               # resource files (external libraries only)
|   +- org
|       +- foo
|           +- bar.json    # static helper data for org.foo.Bar

官方描述:
src目錄應該看起來像標准的Java源目錄結構。當執行流水線時,該目錄被添加到類路徑下。

vars目錄定義可從流水線訪問的全局變量的腳本。 每個 *.groovy文件的基名應該是一個Groovy (~ Java)標識符, 通常是camelCased。 匹配*.txt, 如果存在, 可以包含文檔, 通過系統的配置標記格式化從處理 (所以可能是HTML, Markdown等,雖然txt擴展是必需的)。

這些目錄中的Groovy源文件 在腳本化流水線中的CPS transformation一樣。

resources目錄允許從外部庫中使用 libraryResource步驟來加載有關的非Groovy文件。 目前,內部庫不支持該特性。

根目錄下的其他目錄被保留下來以便於將來的增強。

4.3、方法的具體實現

在var這個目錄下創建一個名為dingding.groovy的文件作為釘釘消息推送方法的代碼文件。

構建一個消息通知器的主要思路:

  • 消息指標內容從哪來
  • 消息模板如何定義
  • 消息怎么發送,發到哪里

4.3.1、消息來源

首先,消息內容從哪來,上面提到的需要在消息中體現的每個指標的可取的獲取方式

指標名稱 指標來源定義
應用名稱 定義為jenkins的任務名稱,通過全局變量env.JOB_NAME獲取或者在pipeline中自定義一個變量給出
構建結果 在pipeline中post字段指標判斷並給出
當前版本 定義為jenkins的構建編號,通過全局變量env.BUILD_NUMBER或者在pipeline中自定義版本號
構建發起 通過全局變量env.BUILD_USER獲取
持續時間 通過全局變量currentBuild.durationString獲取,這個值更為友好
構建日志 日志太多,給個鏈接即可,通過全局變量env.BUILD_URL/console獲取
更新記錄 這個指標是指代碼提交到版本庫中的更新信息,而且包含提交時間,提交者名稱,獲取思路可以通過在檢出代碼后通過類似git log的命令過濾出或者根據全局變量currentBuild.changeSet獲取

分析:
本文中的共享庫用於jenkins+k8s自動化ci測試環境,因此某些指標的定義方法為:
應用名稱自定義,用變量給出,在pipeline前文定義全局變量,在這里傳入變量即可
當前版本自定義,以代碼分支+commitid作為docker鏡像的tag,在pipeline前文中實現或亦通過共享庫實現,在這里傳入變量即可
更新記錄根據全局變量獲取,在這里通過代碼實現

較為復雜的是如何解讀currentBuild.changeSet這個全局變量,通過jenkins上的全局變量列表文檔查看如下
img
點擊其中的鏈接查看官方文檔
img
通過進一步查看官方文檔得知,currentBuild.changeSet返回的是一個集合,這個集合中包含了提交日志,commitid,作者id,作者全稱,時間戳等信息,具體對象相關屬性如下

currentBuild.changeSets{
    items[{
        msg //提交注釋
        commitId //提交hash值
        author{ //提交用戶相關信息
            id
            fullName
        }
        timestamp
        affectedFiles[{ //受影響的文件列表
            editType{
                name
            } 
            path: "path"
        }]
        affectedPaths[// 受影響的目錄,是個Collection<String>
            "path-a","path-b"
        ]
    }]
}

因此,可以通過循環遍歷得出我們需要的相關屬性值,通過groovy腳本定義方法並返回相應字符串,其中為了更優化,需要對提交日志做一下長度限制,對時間戳進行格式化,這兩個功能需要不斷調試。其中changeString變量的賦值格式定義為markdown的無序列表,最終方法如下

def getChangeString() {
    def changeString = ""
    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"
        }
    }
    if (!changeString) {
        changeString = " - No new changes"
    }
    return (changeString)
}

4.3.2、消息模板定義

消息中的相關字段都獲取到了,下一步需要做的就是定義一個消息模板,如果使用郵件發送通知,同樣的也需要定義一個模板。

這里使用更為友好的markdown格式來發送通知,釘釘機器人接口接收的消息是json格式,具體內容可以通過查看官方文檔,為了避免換行出錯,手動指定換行符,最終的格式模板如下

{
         dingtalk (
          robot: 'b446da5d-6c82-41ba-8xxxxxxxxX',
          type: 'MARKDOWN',
          title: "${MYENV}_${PRONAME}構建通知",
          text: [
              "# $headMessage",
              "# 構建詳情",
              "- 應用名稱: ${PRONAME}",
              "- 分支: ${MYENV}",
              "- 構建結果:${statusMessage}",
              "- 構建人: **${env.BUILD_USER}**",
              "- 持續時間: ${currentBuild.durationString}",
              "- 構建日志: [日志](${env.BUILD_URL}console)",
              "# Jenkins鏈接",
              "[應用Jenkins地址](http://xxxxxxxxx/job/${NAMESPACE}-${MYENV}_${PRONAME}/)"
    
          ],
          at: ["13520419047"]
         
        )
}    

4.3.3、消息發送方法

在流水線中按照消息模板渲染好的消息發送給釘釘的接口地址,可以實現的方法包括但不限於以下幾種:

  • 通過執行shell命令發送,例如curl命令指定參數即可,最為簡單,但不夠友好
  • 通過pipeline語法和插件實現,例如使用[DingTalk插件],在Jenkins pipeline中發送HTTP請求給釘釘接口。
  • 通過調用其他腳本發送,例如python腳本,較復雜,不推薦。

綜上比較,選擇一種友好且不復雜的方案,即通過pipeline語法和插件實現

首先在插件安裝中安裝好DingTalk插件
4.3.4、最終方法

綜上所述,在調用此共享庫方法時傳入分支環境變量MYENV、項目名稱變量PRONAME、命令空間變量NAMESPACE、構建狀態變量statusMessage、標題信息變量headMessage,並結合前面實現的方法內容,最終方法dingding.groovy內容如下

#!/usr/bin/env groovy
package com.XXXX

/* dingmes.groovy
   ##################################################
   # Created by xxxxxxxxxxxx                        #
   #                                                #
   # A Part of the Project jenkins-library          #
   ##################################################
*/

def getChangeString() {
    def changeString = ""
    def MAX_MSG_LEN = 10
    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"
        }
    }
    if (!changeString) {
        changeString = " - No new changes"
    }
    return (changeString)
}

def notice(MYENV,PRONAME,NAMESPACE,statusMessage,headMessage){
    wrap([$class: 'BuildUser']){
         dingtalk (
          robot: 'b446da5d-6c82-41ba-xxxxxxxxx',
          type: 'MARKDOWN',
          title: "${MYENV}_${PRONAME}構建通知",
          text: [
              "# $headMessage",
              "# 構建詳情",
              "- 應用名稱: ${PRONAME}",
              "- 分支: ${MYENV}",
              "- 構建結果:${statusMessage}",
              "- 構建人: **${env.BUILD_USER}**",
              "- 持續時間: ${currentBuild.durationString}",
              "- 構建日志: [日志](${env.BUILD_URL}console)",
              "# Jenkins鏈接",
              "[應用Jenkins地址](http://xxxxx/job/${NAMESPACE}-${MYENV}_${PRONAME}/)"
    
          ],
          at: ["13520419047"]
         
        )
    }       
}

4.4、方法調用

此消息通知的方法通常在pipelinepost部分調用,如下所示

post{
    success{
        script{
            
            dingding.notice("$MYENV","$PRONAME","$NAMESPACE","構建成功✅","✅✅✅✅✅✅✅✅✅")
        }
    }
    failure{
        script{
            dingding.notice("$MYENV","$PRONAME","$NAMESPACE","構建失敗❌","❌❌❌❌❌❌❌❌❌")
        }
    }
   
}

4.5、最終效果

測試代碼提交,執行流水線,最終的消息通知效果如下圖

✅✅✅✅✅✅✅✅✅
構建詳情
應用名稱: gateway
分支: test
構建結果:構建成功✅
構建人: xxxx
持續時間: 58 sec and counting
構建日志: 日志
Jenkins鏈接
應用Jenkins地址
@xxxx

5、總結

至此,本文記錄通過自定義jenkins pipeline流水線共享庫方法,實現了較為靈活的自定義釘釘機器人消息通知。如果是使用企信等其他軟件,與此實現思路相近。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM