Skip to main content

Husky

How to install

  • method 1
    • npx husky-init && npm install
  • method 2
    • npx husky-init && yarn
  • method 3
    • yarn dlx husky-init --yarn2 && yarn
    • Yarn 2 doesn't support prepare lifecycle script, so husky needs to be installed differently
  • Prepare husky for installation
    • npm set-script prepare "husky install"
    • should have
      // package.json
      {
      "scripts": {
      "prepare": "husky install"
      }
      }
  • install husky npm run prepare

Create a pre-commit hook

  • npx husky add .husky/pre-commit "npm test"

  • you should get

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

    npm test
  • change it to something like the following

  • code:clean and test:coverage are scripts written in the package.json

    // package.json
    {
    "scripts": {
    "prepare": "husky install",
    "code:lint": "eslint --ext .js,.jsx,.ts,.tsx \"src/\"",
    "code:format": "prettier --write .",
    "code:clean": "npm run code:lint --fix; npm run code:format"
    }
    }
    #!/bin/sh
    . "$(dirname "$0")/_/husky.sh"

    npm run code:clean && npm run test:coverage && node ./branchLint.js

Create a commit-msg hook

  • npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

  • you should get

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

    npx --no-install commitlint --edit ""
  • change it to something like the following

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

    echo 'husky:hooks:commit-msg' && node ./commitLint.ts

Create a commit lint to manage the commit message

  • commitLint.js
const fs = require("fs");
const path = require("path");
const pjson = require("../package.json");

const projectAcronym = pjson.name;
const ruleCommit = `\\[((${projectAcronym})-(\\d)+)\\]:\\s\\w`;

const regExpCommit = new RegExp(ruleCommit, "g");

const msg = `
# Invalid commit message!
#
# Please use the following rules:
# '[required key]: <required message>'
#
# - [required key]: '[${projectAcronym}-{key}]: ', when the {key} is a valid JIRA issue key
# - <required message>: '{msg}', when the {msg} is a clear commit message
#
# For example:
# '[${projectAcronym}-1234]: Commit message'
`;

const commitPath = path.resolve(
process.env.HUSKY_GIT_PARAMS || ".git/COMMIT_EDITMSG"
);
const commitMsg = fs.readFileSync(commitPath, "utf8");

if (!regExpCommit.test(commitMsg)) {
console.error(msg);
process.exit(1);
}

Create a branch lint to manage the branch names

  • branchLint.js
const fs = require("fs");
const path = require("path");
const pjson = require("../package.json");

const projectAcronym = pjson.name;
const ruleBranch = `^(feature|bugfix|improvement|library|prerelease|release|hotfix)\/${projectAcronym}-(\\d+)[a-z0-9._-]+$`;

const regExpBranch = new RegExp(ruleBranch, "g");

const msg = `
# Invalid branch name!
#
# Branch names in this project must adhere to this contract:
# ${ruleBranch}
#
# Your commit will be rejected.
# Please use the following rules:
# '[required key1]/{required key2}-<required name>'
#
# - [required key1]: '{key1}/', when the {key1} is a type of branch of either
# - feature | bugfix | improvement | library | prerelease | release | hotfix
# - {required key2}: '${projectAcronym}-{key2}-', when the {key2} is a valid JIRA issue key
# - <required name>: '{msg}', when the {msg} is a clear branch name
#
# For example:
# 'feature/${projectAcronym}-01-feature-branch-name'
`;

const branchNamePath = path.resolve(
process.env.HUSKY_GIT_PARAMS || ".git/HEAD"
);

const branchName = fs
.readFileSync(branchNamePath, "utf8")
.split("ref: refs/heads/")[1]
.split("\n")[0];

if (!regExpBranch.test(branchName)) {
console.error(msg);
process.exit(1);
}