通过摸索能够学得更多,而不是指令。
We learn best by discovery, not instruction.
-- 《程序员的思维修炼 | 开发认知潜能的九堂课》
写在后面
最近总想尽快调研完husky的我的项目,而后尽快确定我的项目中能够集成的git-hook管理工具。之前曾经探索了pre-commit
,再看完这个我的项目,就能够确定计划了。
装置 & 卸载
执行环境
node -v# v16.4.0npm -v# 7.18.1git --version# git version 2.24.3 (Apple Git-128)
装置
- 装置husky
npm install -D husky# + husky@6.0.0
- 在我的项目中装置husky
npx husky install
执行之后能够再package.json的devDependencies看到husky的配置。并在根目录减少一个.husky/
的目录。(之后会解说.husky/_/husky.sh
)
.husky├── .gitignore # 疏忽 _ 目录└── _ └── husky.sh
查看.git/config
,能够看到配置中批改了core.hooksPath
指向为.husky
。这就是husky 6.0.0
的实现原理:替换.git/hooks
的目录为自定义目录,且该目录会提交到近程仓库。
$ cat .git/config[core] ... hooksPath = .husky
在晓得能够批改core.hooksPath
之前,我都是间接创立换一个软连贯来实现该成果的rm .git/hooks && ln -s .husky .git/hooks
。
- 增加husky install到package.json scripts中
#(npm 7.x中才无效)npm set-script prepare "husky install"
执行之后会在package.json的script中会减少一个prepare。
卸载
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’score.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
npx husky add .husky/pre-commit "npm test"
执行之后会减少文件.husky/pre-commit
(其中的正文是我另外增加的)。
#!/bin/sh# . 指令为source,示意不产生新的shell,在以后shell下执行命令,共享上下文,相似将两个文件拼接到一起# 执行 .husky/_/husky.sh. "$(dirname "$0")/_/husky.sh"npm test
当然你也能够间接在.husky/
创立特定的git hook,毕竟husky
只是帮你从新指定了hooksPath。
.husky/_/husky.sh
上面将解说一下.husky/_/husky.sh
以学习husky的设计原理。
.husky/_/husky.sh
#!/bin/shif [ -z "$husky_skip_init" ]; then debug () { # 当 HUSKY_DEBUG 存在值为1时,开启debug [ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1" } readonly hook_name="$(basename "$0")" debug "starting $hook_name..." # 如果 HUSKY=0,则疏忽hook if [ "$HUSKY" = "0" ]; then debug "HUSKY env variable is set to 0, skipping hook" exit 0 fi # 如果存在~/.huskyrc,则执行 if [ -f ~/.huskyrc ]; then debug "sourcing ~/.huskyrc" . ~/.huskyrc fi # 设置husky_skip_init=1,再次执行hook时不再进入该代码块 export readonly husky_skip_init=1 # 再次执行以后hook,其中的$0为hook门路,$@为所有参数 sh -e "$0" "$@" exitCode="$?" if [ $exitCode != 0 ]; then # 打印hook执行失败信息 echo "husky - $hook_name hook exited with code $exitCode (error)" exit $exitCode fi exit 0fi
从前文能够晓得,通过husky
生成的hook都会在开始都会通过. "$(dirname "$0")/_/husky.sh"
执行一遍该文件,因而该文件会影响这些hook的行为。
#!/bin/sh# . 指令为source,示意不产生新的shell,在以后shell下执行命令,共享上下文,相似将两个文件拼接到一起# 执行 .husky/_/husky.sh. "$(dirname "$0")/_/husky.sh"npm test
从文件中,咱们能够得悉如下信息:
- 设置
HUSKY_DEBUG=1
开启husky的debug
# 间接在终端设置变量值,也能够在~/.bash_profile中设置为全局环境变量HUSKY_DEBUG=1# 执行中指定变量值HUSKY_DEBUG=1 git commit -m "this is msg"
- 设置
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
#!/bin/bashreadonly CUSTOMIZED_HOOK="$(dirname "$0")/customized/$(basename "$0")"if [ -f "$CUSTOMIZED_HOOK" ];then debug "husky - run customized hook - $CUSTOMIZED_HOOK" sh -e $CUSTOMIZED_HOOK $@ resultCode="$?" if [ $resultCode != 0 ]; then echo "husky - $CUSTOMIZED_HOOK hook exited with code $resultCode (error)" exit $resultCode fifi
Makefile
install-husky-hooks: ifeq ($(wildcard .husky/_/husky.sh),) npx husky installendififeq ($(wildcard ~/.huskyrc),) ln -s $(PWD)/.huskyrc ~/.huskyrcelse @echo "Fail: ~/.huskyrc already exist, please handle it manually."endififeq ($(wildcard .gitignore),) touch .gitignoreendififeq ($(shell grep -c .husky/customized .husky/.gitignore),0) echo "\n# 疏忽.husky/customized中开发人员自定义脚本\n.husky/customized" >> .husky/.gitignoreendif
在非node我的项目中执行husky
mkdir new-projectcd new-projectgit initnpm initnpm install -D huskynpx husky install
这样做尽管也没有问题,能够失常的应用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:
{ "gitHooks": { "pre-commit": "lint-staged" }, "lint-staged": { "*.{js,vue}": [ "vue-cli-service lint", "git add" ] }}
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