通过探索可以学得更多,而不是指令。
We learn best by discovery, not instruction.
– 《程序员的思维修炼 | 开发认知潜能的九堂课》
写在前面
最近总想尽快调研完husky的项目,然后尽快确定项目中可以集成的git-hook管理工具。之前已经探究了pre-commit
,再看完这个项目,就可以确定方案了。
安装 & 卸载
执行环境
1 | node -v |
安装
- 安装husky
1 | npm install -D husky |
- 在项目中安装husky
1 | npx husky install |
执行之后可以再package.json的devDependencies看到husky的配置。并在根目录增加一个.husky/
的目录。(之后会讲解.husky/_/husky.sh
)
1 | .husky |
查看.git/config
,可以看到配置中修改了core.hooksPath
指向为.husky
。这就是husky 6.0.0
的实现原理:替换.git/hooks
的目录为自定义目录,且该目录会提交到远程仓库。
1 | $ cat .git/config |
在知道可以修改core.hooksPath
之前,我都是直接创建换一个软连接来实现该效果的rm .git/hooks && ln -s .husky .git/hooks
。
- 添加husky install到package.json scripts中
1 | #(npm 7.x中才有效) |
执行之后会在package.json的script中会增加一个prepare。
卸载
1 | npm uninstall husky |
6.0.0版本做了很大的修改,且和之前的版本不再兼容。主要使用到了2016年git提供的新特性 core.hooksPath
,允许用户指定自己的githooks目录。官方在Why husky has dropped conventional JS config给出了如下的说明。表示虽然可以直接修改core.hooksPath
的指定路径将hooks放到仓库中,但husky提供了更多便捷的功能。
“Why still use husky if there’s
core.hooksPath
?”Husky provides some safe guards based on previous versions feedbacks, user-friendly error messages and some additional features. It’s a balance between going full native and a bit of user-friendliness.
添加husky hook
1 | npx husky add .husky/pre-commit "npm test" |
执行之后会增加文件.husky/pre-commit
(其中的注释是我另外添加的)。
1 |
|
当然你也可以直接在.husky/
创建特定的git hook,毕竟husky
只是帮你重新指定了hooksPath。
.husky/_/husky.sh
下面将讲解一下.husky/_/husky.sh
以学习husky的设计原理。
.husky/_/husky.sh
1 |
|
从前文可以知道,通过husky
生成的hook都会在开始都会通过. "$(dirname "$0")/_/husky.sh"
执行一遍该文件,因此该文件会影响这些hook的行为。
1 |
|
从文件中,我们可以得知如下信息:
- 设置
HUSKY_DEBUG=1
开启husky的debug
1 | # 直接在终端设置变量值,也可以在~/.bash_profile中设置为全局环境变量 |
设置
HUSKY=0
跳过hooks的执行,使用方法同HUSKY_DEBUG
。可以通过
~/.huskyrc
在hook执行前执行一些命令,该文件可以自己创建。该文件也是通过
. ~/.huskyrc
执行的,如果在该文件中exit 0
或者exit 1
都会直接结束hook,前者表示正常执行,后者表示执行失败。
husky.sh
有如下的执行流程:
- 执行hook;
- 执行
husky.sh
,此时husky_skip_init=
,因此执行判断内部代码。 husky.sh
中设置husky_skip_init=1
并通过sh -e "$0" "$@"
再一次执行当前hook,此时因为husky_skip_init=1
跳过判断内部代码,执行hooks剩余代码。
从代码上来看,这个有点绕的流程是为了获取到hook执行的结果,并在debug模式下输出。
自定义本地脚本
还是和之前的pre-commit一样,这里需要为开发者提供一个自定义本地hooks的功能。利用husky/_/.husky.sh
会执行~/.huskyrc
脚本的特性,可以在~/.huskyrc
中进行功能拓展。
.huskyrc
1 |
|
Makefile
1 | install-husky-hooks: |
在非node项目中执行husky
1 | mkdir new-project |
这样做虽然也没有问题,可以正常的使用husky定义的hook,毕竟hooks也仅仅重新指定了core.hooksPathd
到.husky/
。但一个非node项目中包含了一个node_module
,package.json
和package-lock.json
总觉得有些奇怪。
vue项目中的 yokie
按照Vue Cli的网站说明。在安装之后,@vue/cli-service
也会安装 yorkie,它会让你在 package.json
的 gitHooks
字段中方便地指定 Git hook:
1 | { |
yorkie
fork 自 husky
并且与后者不兼容。
yokie显示的语法为husky 6.0.0之前的版本的写法,husky 6.0.0之后就没有支持了。
总结
在了解到husky
时,我觉得很厉害,可以简单地通过一个配置文件添加git hooks。但因为我本身是做后端开发的,所以多少又有点失望,毕竟没法很好集成到后端项目中。在这次进行学习了解之后,又觉得husky好像也没有做什么,和我之前写的gromithook/git-hooks
的思路也基本相同。当然,他确实有解决了提交hook到仓库统一团队开发的hook,也可以取巧地实现开发人员本地自定义hook的功能。但还是没有之前的pre-commit
那样给人带来惊喜。
无中央hooks仓库,复用靠拷贝: 之前开发的时候,因为团队中有多个repo,但都需要集成相同的hook,这时候如果使用husky,那么就需要将这些hook拷贝到每个项目 中。如果使用pre-commit,则没有这个烦恼,因为pre-commit是一个远程hook仓库+config文件的配置方式,只需要修改配置文件即可实现多个项目使用相同的hooks。
每个hook只能定义一个: 还有一处是husky令人感到失望的,每个hook只能定义一个文件,如果要在一个阶段做多种任务,那么久必须将这些任务都写到一个hook中。
综上,如果不需要在多个repo中共享hook,且hook任务比较简单,那么可以考虑选择husky,否则就是用pre-commit。这里我首推pre-commit。
相关文章
推荐
- 本文章源码 Donespeak/menubook
- 在Git项目中使用pre-commit统一管理hooks
- 定义全局Git Hooks和自定义Git Hooks
- 通过Git Hook关联Tapd和Commit
参考
- husky @typicode.github.io/husky
- husky @github.com
- Why husky has dropped conventional JS config 说明6.0.0版本修改的原因
- npm-scripts @docs.npmjs.com
- Husky原理解析及在代码Lint中的应用 讲解了husky 6.0.0 之前版本的配置和原理
- 记一次gitHook带来的思考🤔 同时用到husky和yorkie导致的问题,简单来说就是旧版本的husky和yorkie都会直接修改.git/hooks中脚本
- yorkie @github.com
- Vue Cli/cli-service/git-hook