Featured image of post Husky@v7使用指南

Husky@v7使用指南

为什么有这篇文章

最近我为开源项目 tkestack 的前端控制台部分添加了对 commit 的 lint,在添加过程中遇到了一些困难和疑惑,在此记录一下

v4 配置方式

安装 husky

npm i husky -D

添加配置

在 package.json 中添加如下配置

//package.json
"lint-staged": {
    "./**/*.{ts,tsx,js,jsx}": [
      "eslint",
      "prettier --write"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },

或者添加.huskyrc 文件

// .huskyrc
{
  "hooks": {
    "pre-commit": "lint-staged"
  }
}

v6+ 配置方式

第一次使用,按照 husky doc 进行配置

安装 husky

npm i husky -D

开启 Git hooks

npx husky install

执行到这里,我得到一个错误

npx husky install
.git can't be found (see https://git.io/Jc3F9)

可以推断出原因是这条命令需要在项目根目录执行。即.git所在的目录 tkestack 是一个前后端同在一个仓库的项目,目录结构如下

.
└── web
    └── console

我们现在在 console 目录,所以正确的方式应该是

cd ../../
npx husky install ./web/console/.husky

添加自动执行husky install的钩子

如果其他用户进行开发,显然我们是不希望他需要先阅读 husky 的文档,然后手动执行npx husky install的,所以我们添加一个 npm scripts 的钩子

npm set-script prepare "husky install"

由于目录的原因,对应的调整为

npm set-script prepare "cd ../../ && husky install ./web/console/.husky"

注意 ⚠️:npm set-script 只在 npm version 7.x 生效,如果你用的是 6.x 或者更低,还是手动在package.json中添加吧

现在查看package.json 应该可以看到

// package.json
{
  "scripts": {
    "prepare": "cd ../../ && husky install ./web/console/.husky"
  }
}

添加我们的 hook

npx husky add .husky/pre-commit "lint-staged"

测试一下

修改一个文件使之不符合 eslint 规范,然后我们做一次提交

git add .
git commit 'test husky'

结果居然成功提交了,显然我们的 lint 没有生效

我们检查一下现在我们的 console 目录有哪些变化,会发现多了一个.husky 目录,目录下有一个pre-commit文件,文件内容如下:

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

lint-staged

大致可以猜测到当 pre-commit 时,实际是执行了这个 shell 脚本,由于我们的 lint-staged 并不是全局安装,在这里并不能执行 所以我们在 package.json 中添加一个 script

// package.json
{
  "scripts": {
    "prepare": "cd ../../ && husky install",
    "pre-commit": "lint-staged"
  }
}

接着修改.husky/pre-commit文件

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

npm run pre-commit

再尝试一下修改一个文件并提交

还是成功了,lint 依然没有生效,

查看文档可知pre-commit这个 shell 实际执行目录是在.git所在目录,所以接着修改.husky/pre-commit文件

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

cd ./web/console
npm run pre-commit

最后再次修改文件并 commit,终于可以看到命令行执行了lint-staged,lint 失败,commit 未成功

为什么从 v4 到 v6

很显然,v6 的配置 相比 v4 复杂了很多,那么为什么 husky 官方要修改之前的配置方式呢? 要了解这个问题之前我们先看一下 husky v4 的工作方式

husky@v4 是如何运行的

首先 husky 在安装时会在 .git/hooks/ 目录安装所有可能会使用到的 hooks

比如当我们进行一次提交时,每个 git hook 检查我们有没有在 package.json 或者.huskyrc 中定义对应的钩子:

$ git commit

pre-commit (native) → husky/runner.js (node)
  → is a pre-commit defined in `.huskyrc.js`? → YES, run it

prepare-commit-msg (native) → husky/runner.js (node)
  → is a prepare-commit-msg defined in `.huskyrc.js`? → NO, do nothing

commit-msg (native) → husky/runner.js (node)
  → is a commit-msg defined in `.huskyrc.js`? → NO, do nothing

post-commit (native) → husky/runner.js (node)
  → is a post-commit defined in `.huskyrc.js`? → NO, do nothing
  • 优点: 用户可以在配置文件中添加、更新、删除 hook,husky 会自动判断执行

  • 缺点: 不管我们有没有配置 hook,所有的 git hook 都会执行一遍,包括执行 node husky/runner.js

husky@v6 是如何运行的

2016 年,Git 2.9 引入了core.hooksPath,他使用户可以自定义 git hooks 的目录,在此之前,hooks 只能放在.git/hooks/目录下,然而.git 目录是不能提交的,但是现在配置core.hooksPath之后,比如 husky 使用husky install 配置 .husky/作为 git hooks 目录,我们可以提交.husky/目录作为项目的一部分。

  • 优点:
    • 避免了创建和运行所有 git hooks
    • 现在 git hooks 和 husky 配置文件实际上是一个文件,方便管理
  • 缺点:
    • 配置复杂繁琐

参考

husky doc

Why husky has dropped conventional JS config

2021.07.21 补充

有后端同学遇到了无法提交代码的问题,具体表现是修改了后端代码,然后提交,发现触发的 husky 的 commit 钩子,经过排查是后端同学在前端目录执行了 npm install,导致挂载了 husky 的钩子 ,解决方案:

git config --unset core.hooksPath

因为 v6 版本的 husky 的原理就是设置了 core.hooksPath, 所以我们重置它就好了