关于git:在Git项目中使用husky统一管理hooks

10次阅读

共计 5122 个字符,预计需要花费 13 分钟才能阅读完成。

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

We learn best by discovery, not instruction.

—《程序员的思维修炼 | 开发认知潜能的九堂课》

写在后面

最近总想尽快调研完 husky 的我的项目,而后尽快确定我的项目中能够集成的 git-hook 管理工具。之前曾经探索了pre-commit,再看完这个我的项目,就能够确定计划了。

装置 & 卸载

执行环境

node -v
# v16.4.0
npm -v
# 7.18.1
git --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/sh
if [-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 0
fi

从前文能够晓得,通过 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/bash

readonly 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
    fi
fi

Makefile

install-husky-hooks: 
ifeq ($(wildcard .husky/_/husky.sh),)
    npx husky install
endif
ifeq ($(wildcard ~/.huskyrc),)
    ln -s $(PWD)/.huskyrc ~/.huskyrc
else
    @echo "Fail: ~/.huskyrc already exist, please handle it manually."
endif
ifeq ($(wildcard .gitignore),)
    touch .gitignore
endif
ifeq ($(shell grep -c .husky/customized .husky/.gitignore),0)
    echo "\n# 疏忽.husky/customized 中开发人员自定义脚本 \n.husky/customized" >> .husky/.gitignore
endif

在非 node 我的项目中执行 husky

mkdir new-project
cd new-project
git init
npm init
npm install -D husky
npx 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
正文完
 0