husky: 借助 Git Hooks 提升你提交的代码质量

636 阅读4分钟

导读:husky + lint-staged 的组合已经成为前端项目使用ESLint、Prettier 做代码格式化和有效检查的标准工具链。不过这个往往是作为脚手架代码的一部分创建的,我们不知道这些工具的运用原理和配置方式,本文会向大家通俗、简单地说明,知其所以然。

有很多工具可以自动化我们的代码任务,比如:使用 ESLint 检查语法问题、使用 Prettier 格式化代码。但并不是团队中的每个人都会记得在每次提交时运行这些命令,而不运行这些指令有可能造成某些协作或代码问题,这就是 husky 的能力所在—— 通过注册 Git Hook的方式帮助我们在项目流水线上插入一些中间处理逻辑,比如检查你的提交信息,运行测试,检查代码等.……

什么是 Git Hooks?

Git Hooks 是一种脚本,可以在 Git 生命周期的特定事件中运行。这些事件包括提交的不同阶段,例如提交之前(pre-commit)和提交之后(post-commit)。

Git Hooks 非常有用,允许开发人员运行自定义的代码任务,甚至可以通过自动化其他脚本来执行这些任务以强制执行某些标准规范。

什么是 husky?

husky 是一个工具,允许我们轻松地管理 Git Hooks 并在这些阶段运行我们想要的脚本。

它通过项目中的 .husky 目录中的文件工作,配置 husky 来运行我们指定的脚本。之后,由 husky 负责管理在 Git 生命周期阶段中我们脚本的运行了。

实战练习

我们将建立一个简单的项目测试 Git Hooks。为了方便,我将选择 Next.js 创建项目,并使用 Prettier 演示 Git Hooks 的功能。

为了测试 Git Hooks, 我们将添加一个简单的命令行语句来查看 husky 是否正常工作。但我们还将尝试添加 Prettier——Prettier 是一个工具,用于自动格式化我们的代码。而借助 Next.js,你可以在不做任何意外更改的情况下进行测试。

撰写本文时,husky 目前版本是 v8,我们会基于这个版本进行测试。

第 0 步:创建项目

$ npx create-next-app@latest

√ What is your project named? ... my-husky-project
√ Would you like to use TypeScript? ... No / Yes
√ Would you like to use ESLint? ... No / Yes
√ Would you like to use Tailwind CSS? ... No / Yes
√ Would you like to use `src/` directory? ... No / Yes
√ Would you like to use App Router? (recommended) ... No / Yes
√ Would you like to customize the default import alias? ... No / Yes        
Creating a new Next.js app in D:\fe-projects\my-husky-project.

Using npm.

Initializing project with template: default-tw 


Installing dependencies:
- react
- react-dom
- next
- typescript
- @types/react
- @types/node
- @types/react-dom
- tailwindcss
- postcss
- autoprefixer
- eslint
- eslint-config-next

...

added 352 packages, and audited 353 packages in 29s

133 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
Initialized a git repository.

Success! Created my-husky-project at D:\fe-projects\my-husky-project

$ cd my-husky-project
$ pnpm run dev

浏览器访问:http://localhost:3000,成功创建好了项目。

1690678783894.png

第 1 步:安装 husky

husky 即可以手动也可以自动安装pnpm dlx husky-init && pnpm install)。不过,为了达到学习目的,我们采用手动安装的方式。

  1. 安装 husky
$ pnpm install husky --save-dev
  1. 配置 Git Hooks
$ npx husky install
husky - Git hooks installed

husky install 指令会做两件事情:创建 .husky/_/husky.sh 文件;配置 Git Hooks 目录地址——git config core.hooksPath .husky(默认地址是 .git/hooks

  1. 每次 install 后/ publish 前自动检查 husky Git Hooks 的配置,编辑 package.json 文件
$ npm pkg set scripts.prepare="husky install"

第 2 步:如何配置 husky 来运行 Git Hooks

husky 支持几乎所有由 Git 定义的 Git Hooks,因此我们可以在 Git 事件流中非常灵活地进行操作。

husky 使用 husky add <file> [cmd](不要忘记在此之前运行 husky install)指令添加 Hook。

$ npx husky add .husky/pre-commit "npm test"
husky - created .husky/pre-commit

上述我们定义了 pre-commit Hook,会在每次提交(git commit)前执行 npm test 脚本。

npm pkg set scripts.test="echo 'test successful'"

提交修改:

$ git add .
$ git commit -m "Keep calm and commit"

查看执行效果。

1690680562525.png

可以看到,我们成功的在提交前执行了 npm test 脚本!另外,如果 npm test 执行失败,提交会自动中止,这有效保证了我们提交的有效性。

当然,不止 pre-commit,常用的还有 pre-push,就是在推送到远程仓库前要做的事情。篇幅限制,这里就不多赘述了,大家可以自行实验。我这里简单举个例子。

$ npx husky add .husky/pre-push "npm run build"

第 3 步:如何使用 husky 和 Prettier 格式化代码

真实项目中,为了风格统一,往往会使用 Prettier 来自动格式化代码。而 Prettier 就是这样一款工具,可以让你轻松地格式化代码,让代码看起来像是由一个人编写的,避免协作上的心智负担。

提醒:运行 Prettier 将自动格式化你的所有代码,一旦将其应用为 Git Hook,它将自动化这个过程。

首先安装 Prettier 依赖,package.json 中添加 format scripts:

$ pnpm install -D prettier
$ npm pkg set scripts.format="prettier --write ."

prettier --write . 会原地原地格式化文件。执行 npm run format,会看到有修改的文件:

image.png

目前 husky 运行我们的 Prettier 命令时,我们只是格式化代码,没有将将这些更改保存为 Git 流程中的一部分。因此,我们还要使用 git add 来存储所有这些更改,将它们包含在提交中。

# 清理工作区(前一步的修改)
$ git checkout --force
# 修改 pre-commit Hook,增加 prettier 格式化操作
$ npx husky add .husky/pre-commit "npx prettier --write . && git add -A ."
# 添加到暂存区
$ git add .husky/pre-commit 

现在我们来测试下提交。

$ git commit -m "use husky to format code with Prettier"

看下执行结果:

1690688542227.png

当我们运行提交命令后,可以看到 husky pre-commit Hook 已经生效,并且格式化了我们的代码,而且格式化的代码,也作为本次提交的内容提交了!

1690688608969.png

我接下来可以做什么?

使用 lint-staged 仅对更改的文件运行格式化

我们在 pre-commit 钩子中使用 Prettier,并指定 . 表示每次运行都会作用在所有文件上。

我们还可以使用一个叫做 lint-staged 的工具,它允许我们仍然通过 husky 运行 Git Hooks,但只会作用于暂存文件。就像它的介绍——“ Run linters on git staged files”。如果我们想要用 husky 和 Prettier 来实现这个功能,配置类似下面这样:

$ pnpm install -D lint-staged

首先修改 .husky/pre-commit,让 lint-staged 接管代码格式化:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

- npm test
- npx prettier --write . && git add -A .
+ npx lint-staged

修改 package.json 文件,增加 lint-staged 字段:

"lint-staged": {
  "*": "prettier --write"
},

上面的代码表示,我们对项目暂存区中中的所有文件进行格式化。

作为 lint-staged 运行的一部分,它会自动将更改的文件附加到我们的 prettier 语句末尾。你还会注意到我们没有包含 **git add**,因为 lint-staged 也会自动为我们添加任何更改到 Git 中

Prettier 配置

Prettier 是支持配置自定义的,你可以通过 .prettierrc.json 自定义格式化的规则,或者通过 .prettierignore 忽略某些文件。类似下面这样:

.prettierrc.json

{
  "semi": false,
  "singleQuote": true,
  "overrides": [
    {
      "files": ["*.json5"],
      "options": {
        "singleQuote": false,
        "quoteProps": "preserve"
      }
    },
    {
      "files": ["*.yml"],
      "options": {
        "singleQuote": false
      }
    }
  ]
}

overrides 字段用于支持针对特定文件规则的覆盖。

.prettierignore

.husky
.prettierignore
dist/
pnpm-lock.yaml
pnpm-workspace.yaml

另外,默认情况下,Prettier 会忽略版本控制系统目录中的文件,即 ".git", ".svn" 和 ".hg":

**/.git
**/.svn
**/.hg
**/node_modules

移除 husky

除非你更换工具,否则这一步不会做。

package.json 中删除 "prepare": "husky install"

执行下面的语句即可:

$ pnpm uninstall husky && git config --unset core.hooksPath

需要留意的是,我们要恢复 core.hooksPath 的配置(由 .husky 变为默认的 .git/hooks)。

参考链接