1. 程式人生 > 實用技巧 >使用DingTalk實現共享庫自定義通知器

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

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

和原部落格不同的點:原部落格用的是Http Request外掛,本文用的是DingTalk外掛

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

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外掛官網中可以看到此版本的外掛在在訊息中支援了更多內容,效果如下,但是此外掛目前還暫不支援流水線中使用

在此之前的上一版本提交記錄已經是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上的全域性變數列表文件檢視如下

點選其中的連結檢視官方文件

通過進一步檢視官方文件得知,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流水線共享庫方法,實現了較為靈活的自定義釘釘機器人訊息通知。如果是使用企信等其他軟體,與此實現思路相近。