从commit校验失效问题探究husky原理

一、背景

之前创建的项目,发现代码 commit 提交的时候没有了任何校验,具体表现:

  • 一是 feat fix 等主题格式校验没有了
  • 二是代码 lint 不通过也能提交

尝试解决这个问题,并深入了解husky的实现原理,将相关的一些知识点分享下。

二、lint配套工具

完整的一套代码规范工具,可能包含:husky + lint-staged + eslint + stylelint + prettier

1. git 钩子:

git 本身提供多个生命周期钩子,常见的例如 pre-commit、commit-msg、pre-push 等。

pre-commit 钩子:

在 git commit 命令执行之前触发,配合指定的脚本命令可用来执行代码 lint 校验、代码格式化、跑测试用例等任务,以确保在提交代码之前达到一定的质量标准。

commit-msg 钩子:

是在 git 编辑提交信息之后、提交之前触发,可用于 commit 信息的格式校验。

2. husky:

husky 是一个用来简化 Git 钩子管理的工具。它可以帮助你在 Git 的生命周期事件(如 pre-commit、pre-push 等)中轻松地添加脚本,并且与常用的 js 工具和任务管理器(npm、yarn)集成良好。

3. lint-staged:

lint-staged 用于结合 husky 和 pre-commit,能做到只对 Git 暂存区(git add . 之后)的文件 运行校验任务,从而实现只处理改动的文件,提升commit效率,在大型项目上效果更加明显。

4. eslint:

eslint 是用于对 js 和 jsx 做代码校验的工具,通过静态分析代码来快速地发现和修复一些代码语法问题,帮助前端开发规范代码、提高效率。

5. stylelint:

stylelint 是用于对 css 样式 做代码校验的工具,同上述 eslint 类似,帮助前端开发在编写 css 及 less等预处理器代码时避免犯错,提升开发效率。

6. prettier:

prettier 是目前最常用的前端代码格式化工具,支持多项规则配置,帮助统一代码风格。适当的配置后可以与上述 eslint 和 stylelint 兼容。

三、husky原理

疑问:

  • git 是全局命令,而 husky 只是项目里引入的一个插件,它是怎么和全局的 git 命令产生联系的?
  • 我们通过 git commit -m “xxx” 提交代码时,husky 做了什么?

(以下以 mac os 下 husky v9 版本示例)

1. husky初始化:

1)配置方式:
  • 一般会在项目 package.json 的 scripts 里配置下 prepare 命令(“prepare”: “husky”),用于执行 husky 的初始化。prepare 是 npm 的钩子,在项目 npm i 时会自动执行 npm run prepare。
2)解析husky命令:
  • 执行 husky 命令 实际就是执行 node_modules/.bin/husky 文件,里面没啥东西,主要是三块,自动修改 husky init 命令、错误命令输出处理、执行 index.mjs,显然核心代码在 index.mjs 里。
  • 找到 node_modules/husky/index.mjs 文件,解析下里面的核心代码动作:
    • 执行命令 git config core.hooksPath .husky/_ ,创建目录 .husky/_
    • .husky/_ 里写入 .gitignore 内容为 *
    • 复制 node_modules/husky/husky 文件内容到 .husky/_ 的 h 文件
    • 遍历 l 数组,即支持的所有 git 钩子,复制到 .husky/_ 下同名文件,内容统一为 . “${0%/*}/h”
    • .husky/_ 里创建文件 husky.sh,内容为空。
3)解析说明:
  • 一般来说项目都是用 git 来作为代码版本控制工具,每个项目在 git init 初始化后,根目录都会生成一个 .git 隐藏文件夹,里面是 git 的配置文件 及 代码记录信息。

  • husky 在执行初始化命令后,会自动修改 git 的 hooks 目录,具体表现为在项目 .git 文件夹下的 config 文件里新增了 core.hooksPath 配置,指向项目的 .husky/_ 目录,这样执行 git commit 命令时就会自动寻找项目 .husky/_ 下的钩子文件。

    • 未指定时,默认的 hooks 目录是 .git/hooks
    • 在这里插入图片描述
  • 然后在 .husky/_ 目录下自动生成 git 钩子文件 和 一些执行脚本。
    *

2. husky执行流程:

(以项目 pre-commit 钩子为例)

1)开始运行 commit
  • 修改项目文件,运行 git add .
  • 运行 git commit -m “xxx”,开始流程。
2)读取 hooks 目录配置
  • git工具会去项目的 .git 目录 config 文件里读取 core.hooksPath 目录配置,指向了.husky/_ 目录
3)寻找 pre-commit 业务脚本
  • 首先在 .husky/_ 目录下找到 pre-commit 钩子文件,即 .husky//pre-commit,并执行,实际执行是指向了同目录下的 h 文件,即 .husky//h
    • ${0%/*}:0表示当前执行的脚本路径,%/* 表示从字符串末尾匹配 /*,保留前面部分
  • 执行 h 文件,里面基本是统一读取 husky 配置 以及 做了错误校验,然后实际执行是指向了上一级目录下的同名文件,即 .husky/pre-commit,这个就是我们要寻找的业务侧钩子脚本。
    • ${0##*/}:##/* 表示最大化匹配 */,保留后面部分
4)执行 pre-commit 脚本
  • 执行 .husky/pre-commit 文件,这个就是真正的业务侧钩子脚本,里面内容是就是跑 lint-staged。
5)执行 lint-staged 脚本
  • 运行 lint-staged 命令时,首先是要读取 lint-staged 配置,在 .lintstagedrc 文件里(部分项目可能在 package.json 里配置的),里面就是对不同类型的文件跑不同的命令
    • 在这里插入图片描述
6)执行成功 or 失败
  • 如果命令执行成功,pre-commit 钩子就顺利执行完成了,接着会往下走流程,走 commit-msg 钩子,做 提交信息的格式校验,大致流程类似。
  • 如果执行异常,钩子会中断,不会往下走,commit 失败。如异常命令有抛出信息会在命令行输出。

四、问题解决:

回到最初提到的 bigfish 项目 commit 失效问题。

1. 问题原因:

在代码提交时有warning提示,翻译下来就是 .husky 文件夹下的两个文件是不可执行的,

其实就是 .husky 里的这两个钩子文件没有被赋予可执行权限。

  • 如果这俩文件是在Windows下创建后上传的,在mac下把项目拉下来后就不可执行了。

2. 解决方式:

方式一:

  • 在项目根目录下运行命令,给钩子文件手动赋予可执行权限,
    • chmod +x .husky/commit-msg .husky/pre-commit

方式二:(推荐)

  • 升级 husky 版本到 v9,修改 package.json 里的 preare 命令为 “husky”,然后运行 npm run prepare
    • husky v9 版本更新日志里有一项变更,通过自动设置正确的文件权限,解决了 husky v8 中的这个权限问题。

3. 全局format:

解决完后,建议执行一次 npm run format 命令,即 prettier,对全局做一次代码格式化。
因为每次 commit 校验的时候项目虽然会自动执行 format,但只对修改的文件执行,建议提前对项目所有文件 format 处理下。

五、IDE自带git工具问题:

可能有些人会遇到使用 vscode 或 在线编辑器 等IDE时,自带的 git 管理工具无法 commit,执行时会立即报错。

1. 问题原因:

可能你使用了 nvm 等 node 管理工具,使用 git 工具可能会无法 commit,原因是执行 husky 命令时识别不出 node 环境,详见链接,需要补充环境配置。

2. 解决方式:

在 .husky/pre-commit 和 .husky/commit-msg 文件内容里的 npx执行命令 之前,添加环境配置代码:

待添加的环境配置代码内容:

  • 方式一:(个人搜寻并组合出来的)
if [ -s "$HOME/.nvm/nvm.sh" ]; then
  # First load nvm and provide access to the nvm command.
  export NVM_DIR="$HOME/.nvm/nvm.sh"
  . "$(dirname $NVM_DIR)/nvm.sh"

  # Use the nvm ls command to detect the version being used.
  export NVM_DIR="$HOME/.nvm"
  a=$(nvm ls | grep 'node')
  b=${a#*(-> }
  v=${b%%[)| ]*}

  # Export the current version in your path for husky to find.
  export PATH="$NVM_DIR/versions/node/$v/bin:$PATH"
fi
  • 方式二:(官方文档里的,待验证)
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # 加载 nvm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值