Jenkins部署项目通知到飞书的实现

背景

刚来公司的时候,前端项目的部署都是人工直接手动部署,是直接本地打包之后拖拽到服务器上去,个人觉得有很多隐患。存在多个项目同时部署时的混乱,多人直接接触服务器的不安全,误操作等,及开发部署流程的不规范等。

在联合多人的建议下,及开发经理的带头下,换成了Jenkins部署。但是Jenkins部署又总不能一直等着,于是Jenkins部署通知的想法油然而生。

创建部署机器人

公司的工作通讯软件的是飞书,因而必须借助飞书群组自定义机器人的能力。

新建一个群 ---> 设置里面找到群机器人 ---> 点击添加机器人 ---> 选择自定义机器人

机器人的设置里面有很多配置项,其中出于安全考虑,在安全设置里面选择了签名校验(如果你不需要,也可以不用)

接着开始发挥API工程师的能力开始翻阅对应API了。

编写脚本

经过大量反复的查找,飞书官方提供了一份详细的 说明,但是官方的demo里面没有JS的版本,思考再三,决定使用Python版本来编写脚本。但现在看来,完全是可以用JS来写的,毕竟有NodeJS在。也因此还学了几天Python

代码简单直接上

js 复制代码
#!/usr/bin/env python  #Pyhon脚本的开头,设置utf-8能保证输入中文不会乱码
#-*- coding:utf-8 -*-

import sys       #用于获取Python脚本外部参数
import requests  #用于发送请求
import time      #时间戳工具
import hashlib   #加密工具
import base64    #base64处理
import hmac      #加密工具
import jenkins   #用于连接Jenkins


jenkinsUrl = 'http://xxx.xxx.xxx.xxx:xxxx'  #Jenkins服务器地址
userName = 'userName'  #用户名
password = 'password'  #密码
#飞书webhook地址 和 签名校验密钥,可在自定义机器人上面获取
url = 'https://open.feishu.cn/open-apis/bot/v2/hook/XXXXXXXX'
secret = 'secret'
timestamp = int(time.time()) #秒级

#通过类的方式实现
class Assistant():
    def __init__ (self):
        #连接Jenkins 服务器
        self.server = jenkins.Jenkins(jenkinsUrl, userName, password)
        #获取外部部署项目名称
        self.jobName = sys.argv[1]
        #获取部署项目版本,即第几次部署
        self.buildNumber = self.server.get_job_info(self.jobName)['nextBuildNumber'] - 1
        #项目部署后的信息
        self.jobInfo = self.server.get_build_info(self.jobName, self.buildNumber)
        #部署结果
        self.result = '成功' if(self.jobInfo['result'] == 'SUCCESS') else '失败'
        #部署后的结果地址
        self.jobUrl = self.jobInfo['url']
        #部署时间
        self.startTime = self.getTime('startTime')
        #部署经历时间
        self.duration = self.getTime('duration')
        #获取git提交人姓名,即这个分支功能是谁做的
        self.fullName = ''
        nameList = []
        if len(self.jobInfo['culprits']):
            for item in self.jobInfo['culprits']:
                nameList.append(item['fullName'])
            self.fullName = ','.join(nameList)
        #获取分支名    
        for item in self.jobInfo['actions']:
            if item.get('lastBuiltRevision'):
                self.branchName = item['lastBuiltRevision']['branch'][0]['name']
    # 获取时间相关数据的函数
    def getTime(self,value): 
        jobTime = self.jobInfo['timestamp'] 
        startTime = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(round(jobTime / 1000)))
        duration = int(round(time.time() - round(jobTime / 1000) ))
        timeObj = { 
            'startTime': startTime,
            'duration': duration
        }
        if timeObj[value]:
            return timeObj[value]
    # 签名函数,参照官方文档
    def gen_sign(self):
        # 拼接timestamp和secret
        string_to_sign = '{}\n{}'.format(timestamp, secret)
        hmac_code = hmac.new(string_to_sign.encode("utf-8"), digestmod=hashlib.sha256).digest()
        # 对结果进行base64处理
        sign = base64.b64encode(hmac_code).decode('utf-8')
        return sign 
    # 消息发送函数
    def sendNotificate(self):
        conStr = '项目名称:{0} \n分支名:{1}\n构建编号:第{2}次构建\n开始时间:{3}\n构建持续时间:{4}秒\n构建结果:{5}\n最近git提交人:{6}'
        content = conStr.format(self.jobName,self.branchName,self.buildNumber,self.startTime,self.duration,self.result,self.fullName);
        method = 'post'
        headers = {
            'Content-Type': 'application/json',
        }
        # 飞书接收消息的格式
        json = {
            "timestamp": timestamp,
            "sign": self.gen_sign(),
            "msg_type": "interactive",
            "card": {
                "config": {
                    "wide_screen_mode": True,
                    "enable_forward": True
                },
                "elements": [{
                    "tag": "div",
                    "text": {
                        "content":content,
                        "tag": "lark_md"
                    }
                }, {
                    "actions": [{
                        "tag": "button",
                        "text": {
                            "content": "查看报告",
                            "tag": "lark_md"
                        },
                        "url": self.jobUrl,
                        "type": "default",
                        "value": {}
                    }],
                    "tag": "action"
                }],
                "header": {
                    "title": {
                        "content": self.jobName + " 构建报告",
                        "tag": "plain_text"
                    }
                }
            }
        }
        
        try:
            res = requests.request(method=method, url=url, headers=headers, json=json)
            print(res.json())
        except ValueError as e:
            print(ValueError)

      
        

assistant = Assistant()
assistant.sendNotificate()

飞书提供了很多种消息格式,包括有文本,富文本,照片,群名片等。

效果如下

脚本部署

最后得把这个脚本放到Jenkins部署的hook上去。在Jenkins的项目配置里面,增加构建步骤里面选择 Execute shell ---> 然后在下方的输入框里面编写 python notice.py $项目名

注意这个部署脚本的引用位置必须相对于Jenkins的位置,不然可能会导致文件查找不到。

最后

文章主要是记录实现一个飞书部署通知助手的过程,文章不长,但当时还是费了好大劲,也查找了网上的很多资料,最后才实现出现,也希望能够对有此需求的伙伴一些帮助。期间最大的感悟是,有机会去接触其他的语言,是一件特别有趣的事情

文章更多的是一种实现思路,因为对Jenkins 和 python的不熟悉,都是非常新手的写法,也欢迎有好的实现方式来进行交流。

最后的脚本部署写的很简单,主要是后面因为种种原因,Jenkins的部署通知功能不再需要了,原先的网址也被主公司回收,后台服务还做了迁移。Jenkins的系统和配置也不知道去哪里了[笑哭],所以也就没有截图[泪奔]。

相关推荐
沉默璇年33 分钟前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder39 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727571 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
会发光的猪。1 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客2 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记2 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安2 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼2 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
Domain-zhuo2 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
小丁爱养花2 小时前
前端三剑客(三):JavaScript
开发语言·前端·javascript