持续集成 [脚本工具] 每日 git commit 汇总钉钉通知脚本实现

ELes for PPmoney · 2019年03月18日 · 最后由 ELes 回复于 2019年06月27日 · 3023 次阅读

背景

公司一直倡导测试要对开发改动的代码进行 codereview。
小的需求一般在一个项目里面几个 commit 的情况下,的确所花的 review 成本也不高。
但是一旦项目比较大型,涉及的代码仓库比较多,且前期开发提交频繁的情况下,一旦等到提测才进行 codereview,这个堆积的工作量就太大了,看着就有一种不想 review 的感觉呢。
而且这么多的 commit 的情况下,一次性光看 commit message 都觉得负担很重,谈何 review?

解决思路

既然一次过 review 的量太大,我们为何不尝试每天抽时间完成当天提交的 review 呢?
在提测之前提前介入 review,还能及时发现问题,把 bug 扼杀在开发阶段~
于是我们基于 jenkins 和钉钉机器人做了一个每日 git commit 汇总钉钉通知小工具。

  • 支持多仓库汇总
  • 支持一个仓库多分支汇总
  • 支持同时发送多个机器人消息

具体实现

所用到的工具

  • jenkins——用于定期执行 + 仓库配置
  • GitPython——一个 git 操作的 python 库
  • 钉钉机器人——发送通知

流程

  1. 从命令行中获取到 git url 以及钉钉机器人 token(支持以列表形式传入)
  2. 根据 git url 将项目有仓库克隆到本地
  3. 获取仓库中一天以内的 commit,解析获取其中的 commit message、committer、基于 giturl 拼接成的 commit 详情 URL。形成一个字典数组
  4. 拼接成 markdown 格式的文案
  5. 根据命令行中的 token,给对应机器人发送钉钉通知

效果图

效果图

核心代码

以下代码 python 版本为 2.7.10。

git 仓库操作

def getCommitMessages( repos,day_count ):
    repos_list = repos.split(";")
    summary=[]
    # 遍历列表中所有仓库
    for repo_str in repos_list:
        # 执行前清空临时文件夹
        deleteDir(tmp_path)
        repo = json.loads(repo_str)
        project_name = repo['url'].split('/')[-1]
        project_title = project_name+'-'+repo['branch']
        repo['title'] = project_title
        work_dir = os.path.join(tmp_path,project_title)
        if 'http' not in repo['url']:
            repo['error']='仓库链接仅支持http/https的格式,请修改仓库链接。当前的仓库链接为:{}'.format(repo['url'])
            summary.append(repo)
            continue
        try:
            # 克隆到本地
            git.Repo.clone_from(repo['url'], work_dir, branch=repo['branch'], progress=None)
            repository = git.Repo(work_dir)
            results=[]
            # 获取每一个n天以内的commit并打印出时间和拼接详情链接
            today = datetime.now()
            last_commit_date = time.mktime((today - timedelta(days=day_count)).timetuple())
            commits=repository.iter_commits(rev=None,paths='', max_age=last_commit_date)
            for commit in commits:
                result={}
                commit_date = datetime.fromtimestamp(commit.committed_date)
                result['date'] = str(commit_date)
                result['committer'] = commit.author.name.encode('utf-8')
                # 需要对markdown的标签进行转义
                result['message']= commit.message.replace('# ','\# ') \
                    .replace('* ',"\* ") \
                    .replace('\r\n\r\n',' >>> ') \
                    .replace('\n','  ') \
                    .replace('\r','  ') \
                    .split('Conflicts:')[0] \
                    .encode('utf-8')
                result['url']= '{}/commit/{}'.format(repo['url'], str(commit))
                results.append(result)
            repo['commits']=results
            if results == []:
                repo['error'] = '暂无更新'
        except Exception as e:
            repo['error']='仓库克隆失败,请检查是否仓库地址有误或是否有权限问题。仓库地址:{}'.format(repo['url'])
            print (str(e))
        summary.append(repo)
    return summary

转换成 markdown 格式

# 生成对应格式的的message
def summaryToMarkdown( summary ):
    message=''
    for repo in summary:
        message += '#### {}:  \n'.format(repo['title'])
        message += '>  \n'
        if repo.has_key('error') :
            message += repo['error']+"\n\n"
            continue
        for commit in repo['commits']:
            message += '* 【{} **{}**】[{}]({})  \n'.format(commit['date'], commit['committer'], commit['message'], commit['url'])
        message += '\n\n'
    return message

发送钉钉消息

# 发送钉钉消息  
def sent_dingding_message(token,content,
                          template="""## 最近24小时内git commit汇总情况如下:  \n {}"""):
    headers = {'content-type': 'application/json'}
    payload = {
        "msgtype": "markdown",
        "markdown": {
            "title": "每日git commit汇总",
            "text": template.format(content)
        }
    }
    s = requests.session()
    resp = s.post('https://oapi.dingtalk.com/robot/send?access_token={}'.format(token),
                  headers=headers,
                  data=json.dumps(payload))
    if resp.status_code != 200:
        resp.encoding="utf-8"
        print(resp.text)
        raise Exception("消息发送失败")

如何使用

  1. 将脚本上传到一个 git 仓库保存。
  2. 在 jenkins 的宿主机安装好 GitPython 依赖库 pip install gitpython
  3. 新建一个 jenkins 自由风格的 Job
    1. 在源码管理中添加脚本的仓库。
    2. 新增一个日程表,每天定时执行。
    3. 新增一个 Execute Shell 脚本,按照以下格式填写即可。 shell # 这里修改钉钉机器人token export token=token1,token2 # 按照如下格式配置需要汇总的仓库和分支,通过英文分号;隔开,同一个仓库的多个分支需要分开两条写 export repos='{"url":"http://github.com/xx/xx","branch":"develop"};{"url":"http://github.com/xx/xx","branch":"master"}' python gitCommitNotice.py
共收到 4 条回复 时间 点赞

git commit 获取 git 分支日志信息很好,现在公司正在用一套自主开发的系统

匿名 #2 · 2019年04月11日

代码能开源出来学习下吗?

ELes #3 · 2019年06月27日 Author

核心函数的实现已经在文章里面贴出了,根据流程调用函数就可以了😉

ELes #1 · 2019年06月27日 Author
Benjamin 回复

这个自主开发的系统具体是实现什么功能的呢?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册