Implementation of custom specification verification of front-end project with husky

It is easy to forget to adjust the version number synchronously when submitting a Bug repair. If it is omitted, it is easy to cause problems such as missing updates. Therefore, it is planned to modify the version number into a hard detection specification.

The implementation effect is similar to that of ESlint detection. Git automatically performs code detection after submission. If it does not meet the specifications, it will report an error and withdraw the submission. If it meets the specifications, it will pass the submission.

analysis

The following problems should be solved:

  • Method of triggering detection

    Since I think of ESlint, my first idea is to add a custom plug-in to ESlint. But after thinking about it carefully, because it detects non JavaScript files, and it is not the logic detection of code. It is just to check whether the corresponding files have been modified before submission. In fact, it is not a very suitable scenario.

    The most suitable thing is to use Git hooks directly, and ESlint is to use husky to call detection in related hooks.

    I wrote an article before Husky7 + commitlint + lint staged record Therefore, the process is familiar. You only need to add files with relevant hook names in the. husky folder, and then write shell scripts directly.

    A simple script is not conducive to maintenance, so it is intended to be written in command line like ESlint, which is also convenient for adding functions later.

  • How do I know about document changes

    This is Git's own function. You only need to use git diff to compare the package.json file, and the comparison information will be output.

    However, the text is not easy to analyze, and no relevant plug-ins can be found at once. Therefore, peggy wrote a relevant parser to convert the text into json data for easy parsing.

  • Terminate submission after submission failure

    The shell script returns 0 for success and non-0 for failure. Node.js provides the related method process.exit(1).

    If you don't understand the way, you can actually turn lint staged to find the required answer. After all, the ready-made examples are there.

  • Provide a way to actively bypass submissions

    Since it is added to the hook, you can't delete the hook and submit it when you don't need it. You can only do skip detection in the submission logic.

    The most appropriate is to carry the specified keyword in the body of the submitted message. When the program recognizes the keyword, it can skip the detection logic.

Key logic

Provide command line call command

Create an empty project, add the bin field, and add relevant call commands for our program

"bin":{
    "check": "./dist/index.js"
}

The corresponding check is the call name of the command line, and the corresponding value is the JavaScript file to be executed:

#!/usr/bin/env node
console.log('hello')

So you can print out hello.

Of course, this is not enough. The command-line program may also carry parameters or other functions, so it can cooperate with Commander.js to assist in processing command-line commands.

Since it is written together with the business code, only general examples can be provided:

import { Command } from "commander";

const program = new Command();
program
    .command("change")
    .option(
      "--commitMsgPath <filePath>",
      "When commit message Skip current command detection when the specified string is contained in the"
    )
    .description("Check whether the version number has changed")
    .action(async ({ commitMsgPath }: { commitMsgPath: string }) => {
      try {
        let msg: string[];
        if (commitMsgPath) msg = readGitMessage(commitMsgPath);

        const flag = await checkVersion(msg);
        if (!flag) throw new Error("Please modify the version number before submitting");
      } catch (e) {
        console.error(`${DATA.NAME}: ${e.name} ${e.message}`);
        process.exit(1);
      }
    });
    
program.parse(process.argv);

Call git diff to judge the data

The simple git library can be used to execute the GIT command. It has rich functions, but unfortunately, the return data of diff is plain text, which is not convenient to process. Use what you wrote before Simple diff format parser , processed into readable json data.

The rest is very simple. Pass the returned diff data into the parser, and then judge whether there is a corresponding keyword version in the parser. If yes, it means that the version number has been modified. More detailed version number verification can be done if necessary.

import simpleGit, { SimpleGit } from "simple-git";
const GitDiffParser = require("../lib/gitDiffParser");

/**
 * Check whether the version number changes
 * @param msg
 */
export const checkVersion = async (msg?: string[]) => {
  let flag = false;

  const git: SimpleGit = simpleGit({
    baseDir: process.cwd(),
    binary: "git",
  });

  // Check whether there is specified text in git message. If yes, skip version detection
  if (msg && msg.some((item) => item === DATA.SKIP_MESSAGE_KEY)) return true;

  // Judge whether the version number has changed
  const diff = await git.diff(["--cached", "package.json"]);
  if (diff) {
    const result = <GitDiffParserResult>(
      GitDiffParser.parse(diff, { REMOVE_INDENT: true })
    );

    result.change.forEach((item) => {
      item.content.forEach((content) => {
        if (content.type === "+" && content.text.includes(`"version"`))
          flag = true;
      });
    });
    return flag;
  }
  return flag;
};

Detection message provides a way to skip detection

It was originally written in the pre commit hook, but after checking several git commands to get the message, we can't get the current message.

Therefore, you can only switch to the commit MSG hook and get the message through the $1 variable. Moreover, what is returned is not the text of the message, but the file path of the message, so you need to read the file content by yourself.

/**
 * Read git message message file
 */
export const readGitMessage = (filePath: string) => {
  try {
    const msg: string = fs.readFileSync(
      path.join(process.cwd(), filePath),
      "utf-8"
    );
    return msg.split(/\s/).filter((item) => item);
  } catch (e) {
    return undefined;
  }
};

This is called in the checkVersion process, compared with the predetermined key, if it is the same, it will go back to true directly, so that the version checking logic is skipped.

Commit MSG script

When added to the project, you can directly invoke the command through npx to execute the detection logic. The message address is passed into the command as a parameter (the previously configured parameter of. Option ("-- commitmsgpath < filepath >", "skip the current command detection when the commit message contains the specified string").

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx commitlint --edit "$1"
npx check change --commitMsgPath "$1"

Keywords: Javascript Front-end

Added by txhoyt on Mon, 01 Nov 2021 13:29:52 +0200