面试官问:天天在用Git,那你知道Git Hooks是什么吗?
前言git 我们应该不陌生了,险些天天工作都会用到。
本日要为大家先容的是 git 里面的 hooks ,也就是 git 钩子。
git 钩子是什么呢?它可以用来干什么呢?为什么说它是强盛的武器呢?带着这些问题,我们一起来看看, git 钩子它到底是个啥。
Git Hooks 简介
Git hooks 是 git 版本控制系统的一个功能,它答应我们在特定的 git 事件发生时执行自定义脚本。
这些事件可以是提交 (commit) 、推送 (push) 、归并 (merge) 等等。通过使用 Git hooks ,我们可以在这些事件发生前或发生后执行特定的操作,比如自动化测试、代码风格检查、部署到服务器等等。
Git hooks 是存储在 .git/hooks 目次下的可执行文件,这些文件的名称对应着差别的 git 事件。当 git 执行相关事件时,相应名称的钩子脚本就会被执行。
.git 是一个隐藏文件夹,在 mac 或者 linux 系统下,可以使用 ls -a 来查看,进入到.git/hooks目次下,可以看到有如下文件:
https://i-blog.csdnimg.cn/blog_migrate/50451974e671814e55f432c1ec59e725.png
下面详细先容一下 git 系统中常用钩子的执行时机以及用途:
[*]pre-commit :在执行提交 (commit) 之前运行。通常用于执行代码风格检查、静态代码分析、单位测试等操作,以确保提交的代码质量。
[*]prepare-commit-msg:在提交 (commit) 消息被编辑之前运行。通常用于修改或扩展提交消息,例如添加自动化生成的信息、验证提交消息格式等。
[*]commit-msg :在提交 (commit) 消息被创建后,但提交动作尚未完成时运行。通常用于验证提交消息的格式、内容等是否符合规范。
[*]post-commit :在提交 (commit) 完成后运行。通常用于发送通知、更新文档、执行某些特定的后续处理等操作。
[*]pre-rebase :在执行变基 (rebase) 操作之前运行。通常用于执行一些预检查,例如确保变基操作不会产生冲突或导致代码质量下降。
[*]post-checkout :在检出 (checkout) 完成后运行。通常用于执行一些与工作目次切换相关的操作,例如更新依赖、清理临时文件等。
[*]post-merge :在归并 (merge) 完成后运行。通常用于执行一些与归并操作相关的操作,例如重新构建项目、更新子模块等。
[*]pre-push :在执行推送 (push) 之前运行。通常用于执行一些预检查,例如运行测试、检查代码质量等。
[*]pre-receive(服务端钩子) :在远程仓库吸收到推送 (push) 之前运行。通常用于执行一些服务端的预检查,例如验证提交的代码是否符合特定规范。
[*]update(服务端钩子) :在推送 (push) 到远程仓库但尚未完成更新时运行。通常用于执行一些与分支更新相关的操作,例如验证提交的代码是否符合特定规范、是否有权限更新等。
[*]post-receive(服务端钩子) :在远程仓库吸收到推送 (push) 并完成更新后运行。通常用于执行一些服务端的后续处理,例如自动化部署、更新文档等。
Husky 工作流原理揭秘
在大抵相识了这些钩子的执行时机以及作用之后,我们来学习一个在前端工程化中常用到代码规范技术选型: Husky+lint-staged+eslint 。
首先先容一下这套技术是用来干嘛的——告急用来在提交代码的时间自动触发 eslint 代码扫描,用来校验并统一整个项目的代码规范。
下面分别先容这三个工具在这套流程中起到的作用:
[*]eslint :这个大家都比力熟悉,告急是用来做代码扫描的
[*]lint-staged :是一个用于在提交前运行 Linter 的工具,它只会检查即将提交的文件,而不是整个项目。
[*]Husky :答应我们自定义一些 git 钩子
所以整个流程就是,使用 Husky 来自定义一些 git 钩子,然后配置 lint-staged 调用 eslint 去扫描代码,此时仅仅扫描待提交的文件而不是整个项目。
husky是怎么注入钩子的
首先我们先来安装一下 husky , npm i husky --save-dev ,这里安装的是 ^9.0.11 这个版本,然后根据官方文档的提示,我们执行一下 npx husky init。
此时会发现项目下多了一个 .husky 文件夹,以及 package.json 中多了一条命令。
https://i-blog.csdnimg.cn/blog_migrate/19122ce672b0c87e10ab02eb73adb0cc.png
当我们执行npx husky init的时间,实际上执行了下面的这段代码。
https://i-blog.csdnimg.cn/blog_migrate/b967d480f085603e4fe9979940ebbcee.png
它调用了 index.mjs ,以及在 .husky 目次下新建了一个 pre-commit 文件。我们先来看 index.mjs 。
https://i-blog.csdnimg.cn/blog_migrate/e76f302e902077e1a0cffa4ece95ad62.png
下面重点来看这段代码
let { status: s, stderr: e } = c.spawnSync('git', ['config', 'core.hooksPath', `${d}/_`])
它告急执行了git config core.hooksPath ${d}/_这个系统调用,这个系统调用的意思是,在执行 git 钩子时,不去调用.git/hooks目次下,而去调用 ${d}/_ 这个目次,而这个目次就是 .husky 下的 _ 目次
数组 l 告急是一些 git 钩子的名称, pre-commit 、 commit-msg 等,然后在 .husky 目次下新建了一个 _ 文件夹,以及在这个文件夹下新建了几个钩子文件,这几个钩子文件的内容都填入了下面的内容,下面的内容意思是当调用这个脚本的时间,调用同目次下的 h 脚本。
#!/usr/bin/env sh
. "${0%/*}/h"
也就是说,当我们 commit 的时间, git 会帮我们调用 pre-commit 钩子,然后 husky 设置了这些钩子的执行目次—— .husky/_。
所以当我们 commit 的时间,实际上调用了.husky/_目次下的 pre-commit 脚本,然后 pre-commit 脚本又调用了 h 脚本。
我们再来看 h 脚本:
https://i-blog.csdnimg.cn/blog_migrate/9360cf1f9c04b3d8e898d912476e22c7.png
可以看到 h 脚本执行了这个命令
sh -e "$s" "$@"
然后我们告急关注这两句:
h="${0##*/}"
s="${0%/*/*}/$h"
这两句的意思是调用上一级目次的脚本名称,也就是当我们 commit 的时间,会调用 .husky 目次下的 pre-commit 脚本。
https://i-blog.csdnimg.cn/blog_migrate/74f435291002efb451e8c07bb0685ecd.png
可以看到终极调用结果与我们的分析无误,由于我们没有配置 test 这个命令 所以执行报错。
https://i-blog.csdnimg.cn/blog_migrate/b7eb76f3b01bb10eb2283859471f78a8.png
至此,已经把 husky 注入 git 钩子的告急流程叙述完毕。
自定义pre-commit举行eslint扫描
下面我们来试试自定义一个 pre-commit 钩子,告急是在 pre-commit 钩子文件中调用须要执行的命令。
我们盼望在 commit 的时间调用 eslint 来做代码检查,所以可以先安装一下:npm i eslint --save-dev。
然后 eslint 的配置文件 .eslintrc.cjs 内容如下:
https://i-blog.csdnimg.cn/blog_migrate/358e191b92360fd6ee80dd4a2eb73ca1.png
这个时间只须要在 .husky/pre-commit 中填入以下内容:
npx eslint .
就可以在提交代码的时间自动调用 eslint 举行代码检查:
https://i-blog.csdnimg.cn/blog_migrate/359f25e00df75939b778c5da6127415c.png
lint-staged是怎么工作的
lint-staged 是一个用于在 git 暂存区中运行代码检查工具的工具,通常与 ESLint、Prettier、Stylelint 等代码检查工具一起使用,以确保只对暂存区中的文件举行检查,避免不须要的全局检查。
首先来安装一下 lint-staged :
npm install lint-staged --save-dev
然后在 package.json 中配置一下 lint-staged 须要调用的脚本
"lint-staged": {
"*.(js|jsx|tsx)": "eslint"
},
最后在 pre-commit 钩子中配置一下调用 lint-staged :
npx lint-staged
https://i-blog.csdnimg.cn/blog_migrate/ef8ca5ad1c8ab6a7ad05ae634b7e60a2.png
可以看到此时提交的时间已经使用 lint-staged 去共同 eslint 做代码检查。
lint-staged 也是通过 git 命令去获取暂存区的文件,比如可以通过 git diff --cached --name-only 如许的命令去获取。
https://i-blog.csdnimg.cn/blog_migrate/927b904c3bef501663e7d59e9e81d7bd.png
获取到暂存区的文件之后,再调用相应的 linter 去扫描对应的文件。比如 eslint 就扫描 js/ts 文件, stylelint 扫描 css/less 等文件。
Git Hook 实现自动化部署
下面再来先容一个使用 Git Hook 做自动化部署的示例,在我们推送代码的时间,自动帮我们构建而且把产物推送到服务器上。
这种方式用来部署测试情况没有问题的,但是一定不能用在部署生产情况上。
相比于 Jenkins+Git服务端钩子 而言,这种方式更加的简朴方便,无需过多的情况配置。
但是缺点也比力显着:打包是在本地,会占用本地的资源;团队协作时打包情况很容易差别等,大概会导致一些意想不到的问题。
我们告急用到的是 pre-push 这个钩子,它在你执行git push命令之前被触发。这个钩子答应你在数据推送到远程仓库之前执行一些自定义的操作。
我们可以在这个钩子中编写一些脚本来执行想要的操作,如果这些操作乐成完成,Git就会继续推送数据到远程仓库;如果这些操作失败, Git 会中断推送过程,并显示相应的错误信息。
以下的自动化部署实用于你的部署流程是走 ftp 部署,即把打包后的 dist 目次传输到 nginx 或者其他 web 服务器目次下。
我们在 .huksy 目次下新建一个 pre-push 文件,然后填入以下内容
#!/bin/bash
path='你的项目路径'
cd "$path"
rm package-lock.json
rm -rf node_modules
npm i
npx vite build && node "$path/scripts/publish.cjs"
[*]进入到项目目次中
[*]删除并重装依赖
[*]构建并执行发布脚本
然后来看发布脚本:
const fs = require("fs");
const path = require("path");
const archiver = require("archiver");
const { sshConfig } = require("./config.cjs");
/* ------------------ 请配置服务器信息 --- start --------------------- */
const zipName = "test.zip";
const remotePath = `/www/wwwroot/test-${+new Date()}`; // 要上传到服务器的目标路径
const originRemotePath = "/www/wwwroot/test";
const distPath = "../dist"; // 要压缩的文件夹路径
/* ------------------ 配置服务器信息 --- end --------------------- */
const output = fs.createWriteStream(zipName); // 压缩后的文件
const archive = archiver("zip");
output.on("close", function () {
console.log(`${archive.pointer()} total bytes`);
console.log(
"archiver has been finalized and the output file descriptor has closed."
);
});
archive.on("error", function (err) {
throw err;
});
archive.pipe(output);
const directoryPath = path.join(__dirname, distPath);
archive.directory(directoryPath, false);
archive.finalize();
const Client = require("ssh2").Client;
const conn = new Client();
conn
.on("ready", function () {
console.log("服务器连接成功");
conn.exec(`mkdir ${remotePath}`, (err) => {
if (err) throw err;
conn.sftp(function (err, sftp) {
if (err) throw err;
const readStream = fs.createReadStream(zipName);
const writeStream = sftp.createWriteStream(remotePath + "/" + zipName);
readStream.pipe(writeStream);
writeStream.on("close", function () {
console.log(`File ${remotePath} 上传 完成`);
// 解压
conn.exec(
`rm -rf ${originRemotePath} && cd ${remotePath} && unzip -o ${zipName} && mv ${remotePath} ${originRemotePath} `,
function (err, stream) {
if (err) throw err;
stream
.on("close", function (code, signal) {
console.log("部署 完成");
// 删除本地压缩包
fs.unlinkSync(zipName);
conn.end();
})
.on("data", function (data) {
console.log("解压中: " + data);
});
}
);
});
});
});
})
.connect(sshConfig);
[*]根据你的服务器 ssh 配置去连接服务器
[*]把打包产物 dist 压缩成一个压缩包
[*]把这个压缩包传输到你服务器的资源目次下,比如 nginx 的静态资源目次
[*]解压这个压缩包并删除旧的打包产物,这就完成了整个发布
其中 ssh 配置大抵长成这个样子:
module.exports.sshConfig = {
host: "hostname",
port: 22,
username: "username",
password: "password",
};
https://i-blog.csdnimg.cn/blog_migrate/528412030ffa1217c8a67cbcf64ed5db.png
至此,我们已经完成在提交代码的时间自动构建以及推送产物到测试情况中,完成了部署。
最后
以上就是本文的全部内容,先容了 git 钩子以及它的一些实际用途。如果你以为故意思的话,点点关注点点赞吧~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]