preface

Jenkins for internal continuous integration, Spike for office communications, And GitLab for code maintenance.

Build details of continuous integration need to be checked frequently during daily development, whether errors are reported during the process, and submitted profiles.

But the old version of the process, only recorded when the main stem, output a check in check out text,

Therefore, sometimes we need to use the help of operation and maintenance bosses to find out why failure is a waste of everyone’s time, and the cost is too high.

So I was thinking about how to simplify the process and make it more efficient.

I knew the game was on when I saw that Dingpin supported card and Markdown push messages.

rendering

Implementation process

Old lookup location

Before this thing comes out, there are several steps you have to take to build information.

Current process

You don’t have to pay attention to others or take up other people’s time to help you locate basic information.

Implemented functions

  • Provide jump to corresponding GitLab warehouse (including issues and so on)
  • Check whether the root directory has Changelog. If yes, the changelog file corresponding to gitlab is provided
  • Who pushed? The act of pushing
  • Quickly jump to the corresponding Jenkins-job to see the build process
  • Output the warehouse profile
    • The name of the output repository
    • Get the last five commit profiles, ignoring merge Request
    • Shows the build commit and supports jumping to GitLab to see the change record for that commit
    • Shows built branches and supports jumping to gitLab branches
    • Support document link incoming
  • Support to push information to multiple groups simultaneously

No third party libraries are used, and Node’s built-in apis are used for file reading and HTTP requests.

Implementation process

Configuration is read

How do I read the configuration file that executes the root directory? Process. CWD is mainly used to query the execution path to read package.json and parameters of independent configuration files

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 profile exists \n");
  const config = require(jk2dtFile);
  importConfig = config;
} else {
  if (fs.existsSync(pkgFile)) {
    process.stdout.write("Jk2dt config file does not exist, try reading \n from package.json");
    if (pkgFile.jk2dt && typeof pkgFile.jk2dt === "object") {
      importConfig = pkgFile.jk2dt;
    } else {
      process.stdout.write("Package. json also has no corresponding configuration item, default configuration \n is used"); }}}module.exports = importConfig;

Copy the code

Markdown custom template conversion

Provide placeholders in Markdown for customization, the simplest and most crude form of template substitution

  • us-msg.md
{{TIPS_BANNER}}

{{GitRepoDesc}}

### --- {{GitRepoName}} ---

** Build branch :** {{GitRepoBranchUrl}}

{{PkgVersion}}

{{GitBuildCommitLink}}

{{GitRepoChangeLog}}

{{RepoRecentTitle}}

{{RepoRecentCommitMsg}}

{{GitRepoActionType}}

### --- Jenkins ---

Executive :** {{PushBy}}

** Build task :** {{JK_JOBS_NAME}}

** Build log :** {{JK_JOBS_CONSOLE}}

** Build state :** {{JK_JOBS_STATUS}}

** Build time :** {{JK_JOBS_TIME}}

Copy the code
  • 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 ? ** Packaged version :**${PkgVersion}` : ""."{{RepoRecentTitle}}": RepoRecentCommitMsg ? "** Submission summary :**" : ""."{{RepoRecentCommitMsg}}": RepoRecentCommitMsg,
    "{{GitRepoChangeLog}}":
      RepoBranch === "master"
        ? '** CHANGELOG :** [CHANGELOG](${RepoChangeLog}) `
        : ""."{{GitBuildCommitLink}}": BuildCommitMDLink
      ? '** Build commit :**${BuildCommitMDLink}`
      : ""."{{GitRepoActionType}}": RepoActionType
      ? ** Push behavior :**${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;

Copy the code

Example Check whether the Changelog file exists

  • Check whether the root directory of the project has the corresponding Changelog. md
  • No one uses Linux anymoregrepThe querychanglog.md(ignore case), useexecSyncSynchronous execution 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; }}Copy the code

Example Query the NPM package version

  • Check whether package.name or main exists, which are necessary elements of the package
  • And then determine if we’re looking at the branch range
  • Finally, the shell queries
/** * get the package 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 ""; }}Copy the code

Gets the last five commit profiles

  • Default and branches less than or equal to 0 do not exist as do not query
  • withgrepIgnore contains “lerna | into | merge” submit summary of vocabulary
  • Sed transforms the string to print a markdown formatted string with a newline
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 ""; }}Copy the code

Determine whether accessToken is valid

Support string and array, strong check

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;
}
Copy the code

Pure type judgment

/** ** @param {*} obj - Object * @return {Boolean} - Boolean * @description - Check whether this is a Promise */
function isThenable(obj) {
  return(!!!!! obj && (typeof obj === "object" || typeof obj === "function") &&
    typeof obj.then === "function"
  );
}

function isString(o) {
  // Whether it is a string
  return Object.prototype.toString.call(o).slice(8.- 1) = = ="String";
}

function isNumber(o) {
  // Whether it is a number
  return Object.prototype.toString.call(o).slice(8.- 1) = = ="Number";
}

function isObj(o) {
  // Whether to object
  return Object.prototype.toString.call(o).slice(8.- 1) = = ="Object";
}

module.exports = {
  isThenable,
  isString,
  isObj,
  isNumber
};

Copy the code

Source code and NPM package

Matters needing attention

  • Support Linux/Unix/MacOS only, call some very common command lines, such as grep,ls.
  • Highly coupled to Jenkins, much of the basic information is derived from Jenkins built-in temporary environment variables
  • All the basic information can be overridden, default from Jenkins provided temporary environment variables to build Git repository information

repo && npm

Github:jk2dt  | npm:jk2dt

conclusion

It has been used internally for more than a month, during which some details have been perfected.

Have better posture or find wrong place please leave a message, will be timely corrected, thank you for reading!