通过摸索能够学得更多,而不是指令。

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)

装置

  1. 装置husky
npm install -D husky# + husky@6.0.0
  1. 在我的项目中装置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

  1. 增加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’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

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

从文件中,咱们能够得悉如下信息:

  1. 设置HUSKY_DEBUG=1开启husky的debug
# 间接在终端设置变量值,也能够在~/.bash_profile中设置为全局环境变量HUSKY_DEBUG=1# 执行中指定变量值HUSKY_DEBUG=1 git commit -m "this is msg"
  1. 设置HUSKY=0跳过hooks的执行,应用办法同HUSKY_DEBUG
  2. 能够通过~/.huskyrc在hook执行前执行一些命令,该文件能够本人创立。

    该文件也是通过. ~/.huskyrc执行的,如果在该文件中exit 0或者exit 1都会间接完结hook,前者示意失常执行,后者示意执行失败。

husky.sh有如下的执行流程:

  1. 执行hook;
  2. 执行husky.sh,此时husky_skip_init=,因而执行判断外部代码。
  3. 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.jsonpackage-lock.json总感觉有些奇怪。

vue我的项目中的 yokie

依照Vue Cli的网站阐明。在装置之后,@vue/cli-service 也会装置 yorkie,它会让你在 package.jsongitHooks 字段中不便地指定 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