Jenkins构建信息推送钉钉(个人定制)

简介: 公司内部持续集成用的Jenkins,办公通讯用的钉钉,代码维护用的GitLab。持续集成的构建详情在日常开发中需要频繁查看,过程是否报错,提交的概要。但是旧有的版本流程,只有记录了合入主干的时侯,输出一个签入签出的文本,所以有时候还需要借助运维大佬帮忙找为毛失败,很浪费大伙的时间,成本太高。所以我就在想,如何简化这个过程,让效率更高。当看到钉钉支持卡片化和 markdown 化推送信息的时侯,我就知道游戏开始了。


前言


公司内部持续集成用的Jenkins,办公通讯用的钉钉,代码维护用的GitLab。


持续集成的构建详情在日常开发中需要频繁查看,过程是否报错,提交的概要。


但是旧有的版本流程,只有记录了合入主干的时侯,输出一个签入签出的文本,

所以有时候还需要借助运维大佬帮忙找为毛失败,很浪费大伙的时间,成本太高。


所以我就在想,如何简化这个过程,让效率更高。


当看到钉钉支持卡片化和 markdown 化推送信息的时侯,我就知道游戏开始了。


效果图


实现的流程


旧的查阅定位


在这个东西出来之前,构建信息都需要走这么几个步骤。



当前的流程


一步到位,不需要去关注其他,也不用占用其他人的时间来帮你定位一些很基础的信息。



实现的功能


  • 提供跳转到对应的 gitlab 仓库(包含issue 这些)
  • 判断根目录是否有 changelog,有则提供跳转 gitlab 对应的 changelog 文件
  • 谁推送了,推送的行为
  • 快速跳转到对应的 jenkins-job,查看构建过程
  • 输出仓库的概要信息
  • 输出仓库的名字
  • 获取最近五次提交的 commit 概要,忽略 merge request 这些的
  • 展示构建的那次 commit 并支持跳转到 gitlab 查看该 commit 的变动记录
  • 展示构建的分支并支持跳转到 gitlab 的分支
  • 支持文档链接传入
  • 支持同时把信息推送给多个群


没有用到第三方库,都是用 node 的内置 api 实现文件读取操作及 http 请求。

实现的过程


配置读取


如何读取执行根目录的配置文件呢?


主要用到了 process.cwd 查询执行路径, 实现读取 package.json 和独立配置文件的参数


const fs = require("fs");
const path = require("path");
const process = require("process");
const jk2dtFile = path.resolve(process.cwd(), "./jk2dtrc.js");
const pkgFile = require(path.resolve(process.cwd(), "./package.json"));
let importConfig = {};
if (fs.existsSync(jk2dtFile)) {
  process.stdout.write("jk2dt配置文件存在 \n");
  const config = require(jk2dtFile);
  importConfig = config;
} else {
  if (fs.existsSync(pkgFile)) {
    process.stdout.write("jk2dt配置文件不存在,尝试从 package.json 读取 \n");
    if (pkgFile.jk2dt && typeof pkgFile.jk2dt === "object") {
      importConfig = pkgFile.jk2dt;
    } else {
      process.stdout.write("package.json也没有对应配置项,采用默认配置 \n");
    }
  }
}
module.exports = importConfig;

markdown自定义模版转换


在 markdown 里面提供一些占位符,来达到定制化的效果,最简单粗暴的模板替换的姿势


  • us-msg.md


{{TIPS_BANNER}}
{{GitRepoDesc}}
### --- {{GitRepoName}} ---
**构建分支:** {{GitRepoBranchUrl}}
{{PkgVersion}}
{{GitBuildCommitLink}}
{{GitRepoChangeLog}}
{{RepoRecentTitle}}
{{RepoRecentCommitMsg}}
{{GitRepoActionType}}
### --- Jenkins ---
**执行人:** {{PushBy}}
**构建任务:** {{JK_JOBS_NAME}}
**构建日志:** {{JK_JOBS_CONSOLE}}
**构建状态:** {{JK_JOBS_STATUS}}
**构建时间:** {{JK_JOBS_TIME}}


  • covert-md-2-str


const fs = require("fs");
const path = require("path");
function mdTemplateStr({
  TipsBanner,
  TemplateName,
  PkgVersion,
  JobInfo: {
    JOB_NAME,
    JOB_BUILD_DISPLAY_NAME,
    JOB_BUILD_URL,
    JOB_STATUS,
    JOB_END_TIME
  },
  GitInfo: {
    RepoUrl,
    RepoBranch,
    RepoName,
    RepoDesc,
    RepoBranchUrl,
    RepoChangeLog,
    RepoPushMan,
    RepoActionType,
    RepoRecentCommitMsg,
    BuildCommitMDLink
  }
}) {
  const PlacehoderVar = {
    "{{TIPS_BANNER}}": TipsBanner,
    "{{JK_JOBS_NAME}}": JOB_NAME,
    "{{JK_JOBS_TIME}}": JOB_END_TIME,
    "{{JK_JOBS_CONSOLE}}": `[${JOB_BUILD_DISPLAY_NAME}](${JOB_BUILD_URL})`,
    "{{JK_JOBS_STATUS}}": JOB_STATUS,
    "{{GitRepoName}}": RepoName,
    "{{GitRepoDesc}}": RepoDesc,
    "{{GitRepoBranch}}": RepoBranch,
    "{{GitRepoBranchUrl}}": RepoBranchUrl,
    "{{PkgVersion}}": PkgVersion ? `**打包版本:** ${PkgVersion}` : "",
    "{{RepoRecentTitle}}": RepoRecentCommitMsg ? "**提交概要:**" : "",
    "{{RepoRecentCommitMsg}}": RepoRecentCommitMsg,
    "{{GitRepoChangeLog}}":
      RepoBranch === "master"
        ? `**变更日志:** [CHANGELOG](${RepoChangeLog})`
        : "",
    "{{GitBuildCommitLink}}": BuildCommitMDLink
      ? `**构建提交:** ${BuildCommitMDLink}`
      : "",
    "{{GitRepoActionType}}": RepoActionType
      ? `**推送行为:** ${RepoActionType}`
      : "",
    "{{PushBy}}": RepoPushMan
  };
  let mdStr = fs.readFileSync(
    path.join(__dirname, `../template/${TemplateName}.md`)
  );
  mdStr = mdStr.toString();
  for (const [k, v] of Object.entries(PlacehoderVar)) {
    const re = new RegExp(k, "g");
    mdStr = mdStr.replace(re, v);
  }
  return mdStr;
}
module.exports = mdTemplateStr;

查询 changelog 文件是否存在


  • 先判断项目根目录是否存在对应的CHANGELOG.md
  • 没有再用 Linux 的 grep 查询 changlog.md(忽略大小写),用 execSync 同步执行 shell


const path = require("path");
const pkgFile = require(path.resolve(process.cwd(), "./package.json"));
const projectExecShellPath = process.cwd();
const fs = require("fs");
const { execSync } = require("child_process");
function rootExistChangelogFile() {
  const CHANGELOG = path.resolve(process.cwd(), "./CHANGELOG.md");
  try {
    if (fs.existsSync(CHANGELOG)) {
      return true;
    } else {
      return !!execSync(
        `ls -l  ${projectExecShellPath} | grep -i "changelog.md"`
      ).toString();
    }
    return false;
  } catch (error) {
    return false;
  }
}

查询npm包的版本


  • 先判断是否包含 package.name 或者 main(主入口)是否存在,这是包的必要因素
  • 然后判断是否我们考虑的分支范围
  • 最后 shell 去查询


/**
 * 获取包的dist-tags
 */
function getPackageDistTag(branch) {
  if (
    !pkgFile ||
    !pkgFile.name ||
    !pkgFile.main ||
    ["master", "dev", "develop", "next"].indexOf(branch) === -1
  ) {
    return "";
  }
  let distTag;
  switch (branch) {
    case "master":
      distTag = "latest";
      break;
    case "dev":
      distTag = "dev";
      break;
    case "develop":
      distTag = "dev";
      break;
    case "next":
      distTag = "next";
      break;
    default:
      distTag = "dev";
      break;
  }
  const execShell = `npm show ${pkgFile.name}  dist-tags.${distTag} 2>/dev/null`;
  try {
    return execSync(execShell).toString();
  } catch (error) {
    return "";
  }
}

获取最近的五次提交概要


  • 小于等于0的默认及分支不存在当做不查询
  • grep 忽略包含 "lerna|into|merge"词汇的提交概要
  • 再用 sed 来改造字符串,输出一个带换行的 markdown 格式字符串


function getLastNCommit(n = 5, branch) {
  if (n <= 0 || !branch) {
    return "";
  }
  const readLineFilterResult =
    branch === "master"
      ? 'grep -E -i -v "lerna"'
      : 'grep -E -i -v "lerna|into|merge"';
  const lineModify = "sed  's/^/> /g' |sed  's/$/\\\n/g' ";
  const execShell = `git log --oneline -${n} ${branch}| ${readLineFilterResult} |  ${lineModify}  `;
  try {
    return execSync(execShell).toString();
  } catch (error) {
    return "";
  }
}

判断accessToken是否有效


支持字符串和数组,强校验


function findValidAK(dict) {
  let tempObj = {};
  for (let [k, v] of Object.entries(dict)) {
    if (isType.isString(v) && v) {
      tempObj[k] = v;
    }
    if (
      isType.isObj(v) &&
      Array.isArray(v.success) &&
      Array.isArray(v.error) &&
      v.success.length > 0 &&
      v.error.length > 0
    ) {
      tempObj[k] = v;
    }
  }
  return tempObj;
}

纯粹的类型判断


/**
 *
 * @param {*} obj - 对象
 * @return  {boolean} - 布尔值
 * @description - 判断是否为Promise
 */
function isThenable(obj) {
  return (
    !!obj &&
    (typeof obj === "object" || typeof obj === "function") &&
    typeof obj.then === "function"
  );
}
function isString(o) {
  //是否字符串
  return Object.prototype.toString.call(o).slice(8, -1) === "String";
}
function isNumber(o) {
  //是否数字
  return Object.prototype.toString.call(o).slice(8, -1) === "Number";
}
function isObj(o) {
  //是否对象
  return Object.prototype.toString.call(o).slice(8, -1) === "Object";
}
module.exports = {
  isThenable,
  isString,
  isObj,
  isNumber
};

源代码及npm包


注意事项


  • 仅支持linux/unix/macos,调用了一些很常见的命令行, 如 grep,ls 。
  • 和 jenkins 高度耦合,很多基础信息都是从 jenkins 内置临时环境变量拿的
  • 仓库信息基本所有基础信息支持覆写,默认从 jenkins 提供的临时环境变量构建的Git仓库信息

repo && npm

Github:jk2dt  | npm:jk2dt

目录
相关文章
|
移动开发 测试技术 开发工具
【钉钉免登录】(详解)钉钉接口,H5微应用,钉钉免登录及获取当前用户信息
【钉钉免登录】(详解)钉钉接口,H5微应用,钉钉免登录及获取当前用户信息
760 1
【钉钉免登录】(详解)钉钉接口,H5微应用,钉钉免登录及获取当前用户信息
|
2月前
|
人工智能 数据可视化 API
10 分钟构建 AI 客服并应用到网站、钉钉或微信中测试评
10 分钟构建 AI 客服并应用到网站、钉钉或微信中测试评
90 2
|
3月前
|
人工智能
10 分钟构建 AI 客服并应用到网站、钉钉或微信中简说
10 分钟构建 AI 客服并应用到网站、钉钉或微信
|
2月前
|
人工智能 运维 负载均衡
10 分钟构建 AI 客服并应用到网站、钉钉或微信中
《10分钟构建AI客服并应用到网站、钉钉或微信中》的解决方案通过详尽的文档和示例代码,使具有一定编程基础的用户能够快速上手,顺利完成AI客服集成。方案涵盖高可用性、负载均衡及定制化选项,满足生产环境需求。然而,若文档不清晰或存在信息缺失,则可能导致部署障碍。实际部署中可能遇到网络、权限等问题,需逐一排查。云产品的功能、性能及操作配置便捷性直接影响解决方案效果,详尽的产品手册有助于快速解决问题。总体而言,该方案在各方面表现出色,值得推荐。
|
5月前
|
jenkins 机器人 测试技术
jenkins接入钉钉api接口自动化测试报告自动发送
该教程介绍了如何在Jenkins上实现接口自动化测试的持续集成,并将可视化报告发送至钉钉工作群。首先,确保准备好了自动化脚本。接着配置Jenkins:安装Git插件,设置源代码管理(如Git)和触发器(定时或推送)。使用Post build task插件处理构建后的报告,读取Allure的prometheusData.txt文件以获取测试结果。最后,安装Ding Talk插件,配置钉钉机器人,通过 Dingtalkchatbot 库发送测试报告信息到钉钉群。整个流程包括Jenkins的定时构建、Git仓库的监听以及自动化报告发送到钉钉的通知。
84 1
|
5月前
|
JSON 分布式计算 DataWorks
DataWorks产品使用合集之能否支持从结果表取出示警信息并且打通钉钉进行告警
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
12月前
|
安全 小程序 API
在钉钉中,自己企业内系统的一些信息,想要通过上下游组织中的外部群进行传达,一般采取怎样的方式处理?
在钉钉中,自己企业内系统的一些信息,想要通过上下游组织中的外部群进行传达,一般采取怎样的方式处理?
105 0
|
JSON API 数据格式
在钉钉中,你可以使用API来获取文件或文件夹的信息
在钉钉中,你可以使用API来获取文件或文件夹的信息
184 2
|
运维 jenkins 机器人
Jenkins配置项目集成钉钉通知
Jenkins配置项目集成钉钉通知
134 0
|
3月前
|
jenkins 持续交付 开发者
自动化部署:使用Jenkins和Docker实现持续集成与交付
【8月更文挑战第31天】本文旨在为读者揭示如何通过Jenkins和Docker实现自动化部署,从而加速软件开发流程。我们将从基础概念讲起,逐步深入到实际操作,确保即使是初学者也能跟上步伐。文章将提供详细的步骤说明和代码示例,帮助读者理解并应用这些工具来优化他们的工作流程。