主题
husky管理git hooks
前言
Git 能在特定的重要动作发生时触发自定义脚本,其中比较常用hooks的有:pre-commit、commit-msg、pre-push 等钩子(hooks)。我们可以在 pre-commit 触发时进行代码格式验证,在 commit-msg 触发时对 commit 消息和提交用户进行验证,在 pre-push 触发时进行单元测试、e2e 测试等操作。
git Hooks
每一个本地的git托管的项目,都会有有一个.git
的目录。默认是不可见的,可以将vscode设置中设置.git
文件可见
git Hooks就是.git/hooks
文件下,保存了一些 shell 脚本,然后在对应的钩子中执行这些脚本就行了。一个还没有配置 Git Hooks 的仓库,默认会有很多.sample
结尾的文件,这些都是示例文件,若要启用某个hooks,只需要将后缀.sample
删除即可
在提交代码到至远程仓库时,.git
文件夹不会被提交到远程,这就导致一个问题,我们在本地配置好 Git Hook 后,共同开发的同学是没办法通过pull获得我们本地配置好的hooks的,若要共享我们配置好的钩子,需要采用第三方工具,比如 Husky,下文会讲到。
git hooks钩子触发时机
介绍husky
husky也叫哈士奇,是一个npm包,可以将git内置的钩子暴露出来,很方便地进行钩子的命令注入,而不需要在.git目录下自己写shell脚本了;不仅可以执行js文件作为脚本,还可以将脚本暴露出来,方便在git项目中进行管理。
husky原理
husky支持的钩子就是git hooks,可以在对应钩子指定一条shell命令,husky会自动将这个命令写入.git目录下的对应钩子脚本,当触发这个git钩子时则会执行我们写入的命令;
husky版本差异
特别注意:Husky的老板恶霸那边与新版本的变化非常大。一些老版本配置好的配置,在新版本下可能会失效。所以若出现明明配置对了,但是不起作用钩子,建议先检查一下版本,V4版本之前与之后的配置项可能完全不同,下文中,使用的时V4.2.5以及更低的版本 另外,安装husky,Node.js 版本要 >=10
node 脚本与Husky
husky本质上就是执行shell命令,因此只要是shell命令都可以在钩子中执行;由于nodeJS也可以通过命令行执行,所以直接用js来充当脚本也是可以的;
1 在 node 脚本中如何退出
当使用node脚本进行检测,希望检测不通过时阻止git进行下一步操作,即终止操作;仅仅抛出错误是不能终止命令的,只能抛出exit状态才能终止;如:
process.exit(1) // exit状态为1才能终止
2 在 node 中执行 shell 命令
有些用于检测的信息只能通过shell命令执行获取(如git相关的信息),如果想要在node中获取到这些信息,可以使用node自带的一些方法来执行;比如exec和spawn方法。
上面两个都是child_process模块里面的方法:
js
const { spawn, exec } = require('child_process')
虽然这两个方法都可以执行shell命令,但是具体用途有所不同;就作用而言,spawn方法更加广泛,可控性更强;
- 当仅仅需要执行shell命令来获取信息(文本)时,可以使用exec方法;
- 如果需要按照原格式(即包含颜色,缩进,换行等)暴露shell命令的标准输出,那么就需要用到spawn方法了;因为exec得到的标准输出已经格式化了,仅仅是普通的文本字符串;
可以将上述方法包装成Promise对象,这样更加方便进行同步调用:
/**
* 执行shell命令,返回promise;
* 这种方式不会捕获的命令输出会被格式化,即没有颜色缩进等;
* @param {string} shell shell命令
*/
function execShell (shell) {
return new Promise((resolve, reject) => {
exec(shell, (err, stdout, stderr) => {
if (err) {
process.stdout.write(stdout)
error(`命令执行错误!\n\n${stderr}`)
reject(stderr)
} else {
resolve(stdout)
}
})
})
}
/**
* 执行shell命令,返回promise;
* 这种方式不能捕获命令输出,但是可以按原格式进行输出;
* @param {string} shell shell命令
*/
function execShellOrigin (shell) {
const shellArr = shell.split(' ')
const process = spawn(shellArr[0], shellArr.slice(1), {
stdio: 'inherit' // 命令的输出按原格式进行输出
})
return new Promise((resolve, reject) => {
process.on('close', code => {
if (code) { // 根据exit code可以判断命令执行结果
reject(code)
} else {
resolve()
}
})
})
}
3 在 commit-msg 钩子中获取/修改 commit 消息
执行commit命令后,git会将commit消息存放于一个临时文件中;然后触发pre-commit钩子,pre-commit钩子成功之后就会触发commit-msg,commit-msg钩子成功后则会将临时文件中的文本作为此次commit消息进行存储;
并且commit-msg钩子会对脚本传一个参数,这个参数就是存放commit消息的临时文件的路径;所以得到这个参数,就可以读取该文件的内容,也就能得到当前commit消息了;同理,在commit-msg钩子中覆盖这个文件就能对此次commit消息进行修改了;
不过,由于在husky中的指定的commit-msg钩子命令并不是git直接执行的,因此只能通过husky间接暴露的变量$HUSKY_GIT_PARAMS来获取临时文件的地址,如:
# $HUSKY_GIT_PARAMS变量就是commit-msg钩子传递的文件路径参数
node ./hooks/commit-msg $HUSKY_GIT_PARAMS
在node脚本内部就可以利用process.argv来获取命令行参数了;
const param = process.argv[process.argv.length - 1] // 获取git commit消息临时存放文件地址
读取和写入操作既可以依靠node自带的方法,也可以利用shell命令(shell命令简单粗暴);
Husky简单配置commit-msg自定义格式
方式一
commitlint + husky
1、下载 @commitlint/cli@16.2.3 husky@1.3.1
npm i npm i @commitlint/cli@16.2.3 husky@1.3.1 -D
2、根目录下编写 commitlint.config.js文件
// commitlint.config.js
module.exports = {
rules: {
"code-count": [2, "always"]
},
plugins: [
{
rules: {
"code-count": params => {
const reg = /^%\d+[\s\S]*/;
const { subject, raw } = params;
console.log(params, subject, raw);
return [
reg.test(raw),
"ERROR: commit格式错误,需要以【%需求ID】开头,例如:'%834723 xxx功能开发完成'"
];
}
}
}
]
};
3、package.json文件下添加配置
"husky": {
"hooks": {
"commit-msg": "commitlint -e $HUSKY_GIT_PARAMS"
}
},
方式二
nodejs shell + husky 参考文章: https://zhuanlan.zhihu.com/p/391221822https://note.xiexuefeng.cc/post/husky-and-git-hooks/
1、 下载husky
js
npm i husky@4.2.5 -D
2、修改package.json
(1)添加scripts
json
"scripts": {
"postinstall": "git config core.hooksPath hooks"
},
注意: 在macx系统上面,以上脚本可能会报错,须要加上chmod 700 hooks/*
,原因是 hooks 脚本默认为不可执行,所以需要将它设为可执行
json
"scripts": {
"postinstall": "git config core.hooksPath hooks && chmod 700 hooks/*"
},
(2)添加husky
节点
json
"husky": {
"hooks": {
"commit-msg": "node ./hooks/commit-msg $HUSKY_GIT_PARAMS"
}
}
3、根目录下新建hook目录
在 commit-msg
hooks 中,我们可以对 commit 消息和用户进行校验。
/hook/commit-msg文件内容
a. sh模式
bash
#!/bin/sh
# 用 `` 可以将命令的输出结果赋值给变量
# 获取当前提交的 commit msg
commit_msg=`cat $1`
# 获取用户 email
email=`git config user.email`
# 根据需要修改正则即可
msg_re="^(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|release|workflow)(\(.+\))?: .{1,100}"
if [[ ! $commit_msg =~ $msg_re ]]
then
echo -e "\e[31m不合法的 commit 消息提交格式,请使用正确的格式: \e[0m"
echo -e "\e[31mfeat: add comments \e[0m"
echo -e "\e[31mfix: handle events on blur (close #28) \e[0m"
echo -e "\e[31m详情请查看 git commit 提交规范:https://github.com/woai3c/Front-end-articles/blob/master/git%20commit%20style.md \e[0m"
# 异常退出
exit 1
fi
# 对用户权限做判断则比较简单,只需要检查用户的邮箱或用户名就可以了(假设现在只有 abc 公司的员工才有权限提交代码)。
email_re="@abc\.com"
if [[ ! $email =~ $email_re ]]
then
echo "此用户没有权限,具有权限的用户为: xxx@abc.com"
# 异常退出
exit 1
fi
或者b. node shell模式
js
#!/usr/bin/env node
const childProcess = require('child_process');
const fs = require('fs');
const email = childProcess.execSync('git config user.email').toString().trim();
const msg = fs.readFileSync(process.argv[2], 'utf-8').trim(); // 索引 2 对应的 commit 消息文件
const commitRE = /^(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|release|workflow)(\(.+\))?: .{1,100}/;
if (!commitRE.test(msg)) {
console.log();
console.error('不合法的 commit 消息格式,请使用正确的提交格式:');
console.error('feat: add \'comments\' option');
console.error('fix: handle events on blur (close #28)');
console.error('详情请查看 git commit 提交规范:https://github.com/woai3c/Front-end-articles/blob/master/git%20commit%20style.md。');
process.exit(1);
}
if (!/@qq\.com$/.test(email)) {
console.error('此用户没有权限,具有权限的用户为: xxx@qq.com');
process.exit(1);
}
pre-commit提交前校验
eslint中文网:http://eslint.cn/
团队协作开发,每个人用的格式化工具不一致,I编码风格不一致,会导致代码提交到仓库后难以维护, 你的代码提交后,被别人拉取, 由于格式原 因,会有很多无用的改动影响, 可以用eslint对代码风格进行约束,方法有很多,由于这里介绍的是husky,所以就用husky + pre-commit来进行代码提交前的校验和自动约束pre-commit会执行在git commit之前,
所用插件及作用
用到的插件:eslint+lint-staged+husky
eslint:提供可配置的规则,用这些规则校验代码规范
eslint .
默认会检查所有的代码规范 linf-staged:可以过滤出暂存区中的代码,用eslint对这部分改动的文件进行代码检查,而不是所有,从而提高检查速度 husky:提供可配置的git hooks,并将hooks一起提交到仓库,可以将git hooks的指向从.git
更改到自定义目录
eslint中文网:http://eslint.cn/
实现目标
实现提git commit时候自动检测代码规范,自动修复代码,并且同步到远程仓库 步骤如下
步骤一:配置eslint
- 下载eslint
js
npm i eslint -D
- 生成配置文件
js
npx eslint --init
// 或者
npm init @eslint/config
接着选择配置,参考:
eslint 会有限找js配置文件。没有js会找json, 在没有才会找ymal,所以这里以js有限,可以提高查找效率
运行完上面命令后,会生成一个配置文件.eslintrc.js
js
// .eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true
},
extends: [
'standard'
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
rules: {
}
}
新建忽略文件.eslintignore
js
// .eslintignore
node_modules/*
- 配置lint命令
js
// package.json
"scripts": {
"lint":"eslint ."
},
- 运行命令lint代码
js
npm run lint
就可以看到命令行有一堆报错和警告
可以运行命令检查并修复
js
npm run lint -- --fix
但是有一些报错不能自动修复,我们可以用规则将其关闭,例如:
有声明了但是未使用的变量;
eslint规则文档地址
具体的eslint规则可以参考文档:http://eslint.cn/docs/rules/
步骤二: 配置husky+lint-staged
这里发规则继承的是
eslint-config-egg
, github地址:[https://github.com/eggjs/eslint-config-egg](https://github.com/eg> gjs/eslint-config-egg)
1.安装husky
安装husky的步骤参照上面commit-msg的步骤
2.添加配置
json
// package.json
"husky": {
"hooks": {
"pre-commit": "node ./hooks/pre-commit",
"commit-msg": "node ./hooks/commit-msg $HUSKY_GIT_PARAMS"
}
},
3. 在husky目录下创建pre-commit文件
在 pre-commit 钩子中要做的事情特别简单,只对要提交的代码格式进行检查,因此脚本代码比较少:
/hook/pre-commit文件内容
① sh脚本版本
#!/bin/sh
npm run lint-staged
# 获取上面脚本的退出码
exitCode="$?"
exit $exitCode
② nodejs版本
js
#!/usr/bin/env node
const childProcess = require('child_process');
try {
childProcess.execSync('npm run lint-staged');
} catch (error) {
console.log(error.stdout.toString());
process.exit(1);
}
4. 添加lint-staged命令
package.json文件内添加"lint-staged":"lint-staged"
和 "lint-staged"
对象
js
"scripts": {
"lint":"eslint .",
"lint-staged":"lint-staged"
},
"lint-staged":{
"*src/*.js":[
"npm run lint -- --fix",
"git add ."
]
}
- 在项目中添加
.eslintrc
文件
json
{
"extends": "eslint-config-egg",
"rules": { //自己的规则,可以覆盖继承的规则(根据团队约定配置)
"indent": ["error", 2],
"no-console":"off",
"no-unused-vars":"off"
}
}
步骤三:优化
- vscode编辑器设置保存时自动格式化
- vscode安装
eslint
插件,在编码阶段发现错误
配置vscode保存时自动修复eslint
步骤1. 下载eslint插件
步骤2. 配置保存时自动修复
按Ctrl
+ Shift
+ p
--> 输入 setting
选择 首选项:打开设置(json)
json中加上这行配置
json
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"eslint.run": "onSave"
注意: npm安装的 eslint插件 - 运行在编译阶段 vscode 里面的 eslint 插件 - 运行在编码阶段
npm 安装的 prettier 插件 - 运行在代码启动时(可以理解为编译阶段) vscode里面的prettier插件 - 运行在IDE保存阶段
要想开发时有代码检查,需要vscode安装eslint插件
相关文章
husky钩子的一些用法husky使用总结一文带你彻底学会 Git Hooks 配置husky官方文档自定义 Git - Git 钩子(官方)