西安企业网站设计哪家专业,网站安全建设方案报告,企信网企业信息查询平台官网,ml域名注册前言
通常情况下#xff0c;一个 git 仓库就是一个项目#xff0c;只需要配置一套 git hooks 脚本就可以执行各种校验任务。对于 monorepo 项目也是如此#xff0c;monorepo 项目下的多个 packages 之间#xff0c;它们是有关联的#xff0c;可以互相引用#xff0c;所以…前言
通常情况下一个 git 仓库就是一个项目只需要配置一套 git hooks 脚本就可以执行各种校验任务。对于 monorepo 项目也是如此monorepo 项目下的多个 packages 之间它们是有关联的可以互相引用所以当成一个项目也没问题。
但是也有一种情况一个 git 仓库下的多个项目之间是彼此独立的比如 git 仓库下存在前端项目、后端项目、文档项目等等。这时候就需要为每个项目配置不同的 git hooks 脚本了因为不同的项目有可能校验规则不一样。
本文主要探讨一下如何为不同的项目配置 git hooks 脚本。
PS配置 git hooks 脚本使用 huksy。
方案一每个项目下都配置一套 git hooks 脚本
假设仓库拥前后端两个项目
frontend
backend那么我们需要在每个项目下安装 husky同时要在 package.json 中配置一下 prepare 脚本这里以前端项目为示例
# package.json
{scripts {prepare: cd .. husky install frontend/.husky}
}然后按照 husky 文档创建 pre-commit 和 commit-msg 钩子文件
#!/usr/bin/env sh
. $(dirname -- $0)/_/husky.sh# pre-commit
cd frontend
npx lint-staged#!/usr/bin/env sh
. $(dirname -- $0)/_/husky.sh# commit-msg
cd frontend
FORCE_COLOR1 node scripts/verifyCommitMsg.mjs $1上面展示的是前端项目的 git hooks 创建过程后端项目按照同样的过程创建即可。目前仓库的目录结构如下
frontend.husky- pre-commit- commit-msg
backend.husky- pre-commit- commit-msg运行一段时间后发现这个方案有问题那就是每次触发的 git hooks 脚本都是前端项目的后端项目提交代码根本不触发 git hooks。排查问题后发现是 git 仓库的配置引起的打开 .git/config 文件
[core]hooksPath frontend/.husky上面 hooksPath 路径对应的就是 git hooks 的目录位置目前 git 只支持指定一个目录作为 git hooks 的位置。所以第一个方案不靠谱达不到我们想要的效果。
方案二只在根目录下配置一套 git hooks 脚本
第二个方案是将 git hooks 放在项目根目录下统一在根目录里执行各个子项目的校验脚本。这个方案有以下几个步骤
修改 husky 安装位置
在每个项目下安装 husky 时要把 git hooks 钩子目录设置在根目录
# package.json
{scripts {prepare: cd .. husky install .husky # 放到根目录}
}同时 .git/config 文件也要修改一下
[core]hooksPath .husky # 改为根目录在 git hooks 中进行各个子项目的校验操作
这里以 commit-msg 作为示例编写一个脚本
#!/usr/bin/env sh
. $(dirname -- $0)/_/husky.sh# 拿到所有改动的文件名
changedFiles$(git diff --cached --name-only --diff-filterACM)# 判断目录是否改动
isBackendChangedfalse
isFrontendChangedfalsefor file in $changedFiles
doif [[ $file frontend/* ]]thenisFrontendChangedtrueelif [[ $file backend/* ]]thenisBackendChangedtruefi
done# 改动的目录需要执行校验命令
# $1 $2 代表传给函数的第一个、第二个参数
execTask() {echo root $1 commit-msgcd $1FORCE_COLOR1 node scripts/verifyCommitMsg.mjs $2
}if $isFrontendChanged
thenexecTask frontend $1 # 使用 让任务在后台执行task1$! # 保存任务 id
fiif $isBackendChanged
thenexecTask backend $1 task2$!
fiif [[ -n $task1 ]]; thenwait $task1
fiif [[ -n $task2 ]]; thenwait $task2
fiecho All tasks finished.上面脚本的逻辑是这样的
每次 git 提交代码时判断一下当前所有改动的文件是属于哪个项目文件发生改动的项目需要执行校验任务每个校验任务都使用子进程去执行等待所有校验任务执行结束后输出 All tasks finished.
pre-push 脚本编写
与 pre-commit 和 commit-msg 不同在 pre-push 钩子中需要通过其他方式来拿到发生改动的文件大家直接看代码
#!/usr/bin/env sh
. $(dirname -- $0)/_/husky.sh# 判断目录是否改动
isFrontendChangedfalse
isComponentTemplateChangedfalse
isComponentAttrPanelChangedfalse# 获取远程仓库的名字和 URL
remote$1
url$2# 定义一个空的 git 哈希值
z400000000000000000000000000000000000000000# 这个循环从 stdin 读取数据这些数据是 git 在调用 pre-push 钩子时传递的。
# 每一行数据包括 4 个字段本地引用名本地最新的提交哈希值远程引用名远程最新的提交哈希值。
while read local_ref local_sha remote_ref remote_sha
do# 这段代码检查是否正在删除一个引用例如删除一个分支。如果是那么本地的 sha 值将被设置为一个空哈希值。if [ $local_sha $z40 ] then# Handle delete:else# 这段代码确定要检查哪些提交。如果远程的 sha 值是一个空哈希值那么我们正在创建一个新的引用所以我们需要检查所有的提交。# 否则我们正在更新一个已经存在的引用所以我们只需要检查新的提交。if [ $remote_sha $z40 ] then# New branch, examine all commitsrange$local_shaelse# Update to existing branch, examine new commitsrange$remote_sha..$local_shafi# 这个循环对每一个包含在 range 变量中的提交执行 git rev-list 命令这个命令会返回一系列的提交哈希值。# 然后对每个提交我们使用 git diff-tree 命令来找到在那个提交中修改的文件。这些文件的名字被存储在 files 变量中。for commit in $(git rev-list $range); do# 拿到所有改动的文件名files$(git diff-tree --no-commit-id --name-only -r $commit)for file in $filesdoif [[ $file frontend/* ]]thenisFrontendChangedtrueelif [[ $file component-attr-panel/* ]]thenisComponentAttrPanelChangedtrueelif [[ $file component-template/* ]]thenisComponentTemplateChangedtruefidonedonefi
done# 改动的目录需要执行校验命令
execTask() {echo root $1 pre-pushcd $1npm run type-check
}if $isFrontendChanged
thenexecTask frontend # 使用 让任务在后台执行task1$! # 保存任务 id
fiif $isComponentTemplateChanged
thenexecTask component-template task2$!
fiif $isComponentAttrPanelChanged
thenexecTask component-attr-panel task3$!
fiif [[ -n $task1 ]]; thenwait $task1
fiif [[ -n $task2 ]]; thenwait $task2
fiif [[ -n $task3 ]]; thenwait $task3
fiecho All tasks finished.测试一段时间后发现第二个方案没发生什么问题完全满足需求。