前端项目如何约束git规范?
如何规范组内童鞋的提交?
如何确保大家的代码在提交到远程仓库之前,都是做过ts或lint检查和格式化,都已经通过单元检测,而且大家的commit 提交的都安装提交规范包含有枚举的类型和主题,方便追踪和管理代码提交?
如何确保大家在提交之前已经合远程仓库同步?
前端项目里,通常是用husky结合git hooks来达到git规范的目的,两者结合可以做的事情有:
ts规范检测
eslint规范检测
commit message规范检查
跑单测
git远程仓库是否同步检查
如何使用Husky
1. 安装husky
npm install --save-dev husky
2. 初始化
npx husky init
这个操作会做3件事:
-
在根目录生成.husky文件夹
-
在.husky文件夹中生成pre-commit脚本:默认文件里会写npm run test
-
package.json文件中增加script: "prepare": "husky"
这样,后续在git commit的时候,就会先跑.husky/pre-commit脚本,执行里面写的任何命令,这里就是npm run test命令。
git hooks 简介
git hooks可以理解成和React hooks类似,可以在git的不同阶段去执行自定义的脚本。这些hooks分为两类,一类是客户端hooks,一类是服务端hooks。客户端hooks在我们执行commit和merge的时候触发,而服务端hooks会在我们执行git push之后当服务端接受到该pushed commit的时候会触发。
这里主要讲下客户端git hooks。
客户端git hooks介绍
- git commit工作流阶段的hooks:
pre-commit -> prepare-commit-msg -> commit-msg -> post-commit
其中常用的有pre-commit和commit-msg这两个。
2.git push工作流阶段的hooks:
pre-push
- git rebase阶段的hooks:
pre-rebase
- git merge阶段
post-merge
- git checkout阶段
post-checkout
所以,这里原理就是,把相应的git hooks脚本放在.husky文件夹下,以该git hooks的名字命名,比如commit-msg, pre-commit, pre-push, pre-rebase等,然后把需要在对应阶段执行的命令更新到对应的脚本文件中,当我们执行相应的git操作的时候就会自动去执行相应的脚本。
比如在git commit的时候,会先去执行pre-commit, 然后执行prepare-commit-msg,再执行commit-msg,最后执行post-commit。如果有任何一个失败,就不会成功commit。其它阶段也同理。
常用客户端git hooks
下面就常用的3个客户端git hooks介绍以及如何使用。
1. pre-commit
结合lint-staged包,仅针对缓存工作区的文件做相应检查,而非是全局检查,既耽误时间又没必要。
lint-staged 如何结合使用
commit之前针对修改的文件做前置检查,常见的如ts检查、eslint检查、prettier格式化、跑单测等,所有你想在commit之前做的事情。
首先,安装lint-staged包
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
npm install --save-dev lint-staged
其次,配置config文件
可以选用需要的文件格式,也可以再package.json里配置,这里以config.js为例。
注意:如有需要,需要安装相应的npm包,比如eslint需要安装eslint的包,prettier需要安装prettier的包。
格式:key为需要处理的文件,一般会用正则匹配相关类型的文件; value 是需要执行的脚步命令
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
// lint-staged.config.js
module.exports = {
"src/**/*.(ts|tsx)": () => "tsc --noEmit", // ts check
"src/**/*.(ts|tsx|js|jsx|mjs)": () => [`eslint --cache --fix`], // eslint check
"**/*": () => ["prettier --write --ignore-unknown"], // prettier
};
最后,更新.husky/pre-commit脚本
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
npx lint-staged
2. commit-msg
结合commitlint包使用
首先,安装@commitlint/cli。
可以直接extend现有的通用的配置,需要下载相应的包,比如@commitlint/config-conventional。
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
npm install --save-dev @commitlint/cli
// 按需是否安装
npm install --save-dev @commitlint/config-conventional
其次,配置config文件
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
module.exports = {
rules: {
"body-leading-blank": [1, "always"],
"body-max-line-length": [2, "always", 100],
"footer-leading-blank": [1, "always"],
"footer-max-line-length": [2, "always", 100],
"header-max-length": [2, "always", 100],
"scope-case": [2, "always", "lower-case"],
"subject-case": [
2,
"never",
["sentence-case", "start-case", "pascal-case", "upper-case"],
],
"subject-empty": [2, "never"],
"subject-full-stop": [2, "never", "."],
"type-case": [2, "always", "lower-case"],
"type-empty": [2, "never"],
"type-enum": [
2,
"always",
[
"feat",
"fix",
"hotfix",
"merge",
"docs",
"refactor",
"revert",
"style",
"test",
"chore",
"ci",
"perf",
"build",
"log",
],
],
},
};
当然,可以直接用通用的配置。
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
module.exports = {
extends: ["@commitlint/config-conventional"],
};
最后,更新.husky/commit-msg脚本
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
npx --no -- commitlint --edit
3. pre-push
一般在将本地代码提交到远程之前,可以和远程代码仓库检查下是否已经同步过远程的master主分支,如果落后master分支,则提示先同步更新远程代码之后再提交。这样可以阻止强制推送,防止代码覆盖等意外。
这里没有第三方的包,而是手写的脚本参考,所以直接更新.husky下对应的脚本文件即可。
.husky/pre-push脚本文件:
--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
gitDiffRemote(){
git fetch origin
NB_COMMITS_BEHIND=$(git rev-list --left-right --count $1...@ | cut -f1)
CURRENT_FEATURE=`git symbolic-ref --short -q HEAD`
if [ "${NB_COMMITS_BEHIND}" -gt "0" ]; then
echo "\033[0;31m ${CURRENT_FEATURE} 落后远端 $1 ${NB_COMMITS_BEHIND}个提交 \n \033[0m"
echo "\033[0;31m 请更新远端代码分支:$1 \n \033[0m"
exit 1
else
echo "\033[0;32m ✔ 当前分支已是最新 \033[0m"
fi
}
gitBranchExists(){
{
git rev-parse --verify ${1} > /dev/null 2>&1
if [ "$?" == "0" ]; then
gitDiffRemote ${1}
else
echo "\033[0;31m ⚠ 远端分支${1}不存在,跳过检验 \033[0m"
fi
} || {
echo "\033[0;31m ⚠ 远端分支${1}不存在,跳过检验 \033[0m"
}
}
echo "\033[0;32m ➜ Fetching master...\n \033[0m"
gitBranchExists "origin/master"
备注:
如果项目有成熟的单测,建议将单测也加入到git 规范里来。按项目需求,可以再pre-commit阶段或者pre-push阶段做单测检查,约束如果单测不通过不能成功commit或者push。
当然这个得看具体需求,但是好多前端项目这个很难做起来,因为一般业务需求都比较紧急,也不会单独给单测用例预留开发时间,也不会有额外时间去开发,这样单测覆盖率做的会比较差,很容易出现说单测用例未及时更新而导致无效,或者老旧单测无法通过的情况。
当然针对不经常变的utils或者固定的逻辑,建议还是用单测用例覆盖检查,这样可以防止后续因为新手不熟悉业务而改出问题来。而如何写这些成熟的测试用例,还是很考验功底的。
拓展阅读:
- git hooks文档: git-scm.com/docs/githoo…
- Husky官网: typicode.github.io/husky/