-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Engineering): 🎈 add blog about git-hooks/commitlint/standard-ver…
…sion
- Loading branch information
Showing
11 changed files
with
306 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
--- | ||
title: 代码检查 | ||
group: | ||
title: 项目规范 | ||
order: 5 | ||
order: 0 | ||
--- | ||
|
||
在团队越来越庞大之后,每一个成员的代码风格也是不一样的,为了能够让仓库的代码风格统一,往往需要一些工具来帮我们完成这个事情,目前用的比较多的工程化方法就是`husky+lint-staged` | ||
|
||
## git hooks | ||
|
||
`git hooks` 主要能够在某些特定行为发生时触发某些自定义程序。 | ||
|
||
### 常见的 git hooks | ||
|
||
**客户端** | ||
|
||
- `pre-commit` hook 在运行 `git commit` 命令时且在 `commit` 完成前被触发 | ||
- `commit-msg` hook 在编辑完 `commit-msg` 时被触发,并且接受一个参数,这个参数是存放当前 `commit-msg` 的临时文件的路径 | ||
- `pre-push` hook 在运行 `git push` 命令时且在 `push` 命令完成前被触发 | ||
|
||
**服务端 hook** | ||
|
||
- `pre-receive` 在服务端接受到推送时且在推送过程完成前被触发 | ||
- `post-receive` 在服务端接收到推送且推送完成后被触发 | ||
|
||
更多的 `git hooks` 可查阅 [git 官方文档](https://git-scm.com/docs/githooks) | ||
|
||
### 设计缺陷 | ||
|
||
但是由于原生的 `git hooks` 都是存放在 `.git/hooks` 文件中,由于 `.git` 文件夹是隐藏文件夹,不会被 `Git` 所追踪,那么就需要每一位团队成员分别维护一份,还要考虑同步问题,的确有些困难,所以有了新的解决方案——`husky` | ||
|
||
## husky | ||
|
||
### 什么是 husky | ||
|
||
[husky](https://github.com/typicode/husky) 是常见的 `git hook` 工具,能够解决原生 `git hook` 无法版本追踪的问题。 | ||
|
||
使用 `husky` 可以挂载 `Git` 钩子,当我们本地进行 `git commit` 或 `git push` 等操作前,能够执行其它一些操作,比如进行 `ESLint` 检查,如果不通过,就不允许 `commit` 或 `push` | ||
|
||
### 使用 | ||
|
||
1. 在项目中安装 husky | ||
|
||
```bash | ||
pnpm install husky -D | ||
``` | ||
|
||
2. 启用 Git 挂钩 | ||
|
||
`husky install` 命令告诉 `Git` 改为使用 `.husky` 目录 | ||
|
||
```bash | ||
// v8 | ||
pnpm husky install | ||
npm set-script prepare "husky install" | ||
|
||
// v9,将上述两条命令合并为一条 | ||
npx husky init | ||
``` | ||
|
||
要在安装后自动启用 `Git` 挂钩,编辑 `package.json` ,确保 `husky` 可以正常使用 | ||
|
||
此时的 package.json 会增加一行 script | ||
|
||
```json | ||
{ | ||
"scripts": { | ||
// ... | ||
// v8 | ||
"prepare": "husky install" | ||
// v9 | ||
"prepare": "husky" | ||
}, | ||
} | ||
``` | ||
|
||
[v9.0.1 changelog](https://github.com/typicode/husky/releases/tag/v9.0.1) | ||
|
||
### husky install 干了什么? | ||
|
||
1. 检查项目的根目录中是否存在 `.git` 目录,以确保你正在运行该命令的是一个 Git 仓库。 | ||
2. 检查项目的根目录中是否存在 `.husky` 目录,该目录用于存储 Husky 的配置和钩子脚本。如果 `.husky` 目录不存在,它会自动创建该目录。 | ||
3. 将 Husky 的 Git 钩子脚本复制到 `.husky` 目录中。这些脚本包括 `pre-commit`、`pre-push` 等钩子,它们在相应的 Git 操作之前执行。 | ||
|
||
并且会更改 `.git/config` 文件中的 `core.hooksPath` 内容,更改为对应的 `.husky/_`,那么执行对应 `git hook` 的时候就会找到对应 `hooksPath` 的内容 | ||
|
||
### husky add 干了什么? | ||
|
||
```bash | ||
// v8 | ||
npx husky add .husky/pre-commit "npm test" | ||
|
||
// v9 | ||
echo "npm test" > .husky/pre-commit | ||
``` | ||
|
||
执行完毕之后在 `.husky` 文件夹会多一个 pre-commit 的文件脚本,当执行 `git commit -m "xxx"` 就会触发对应的命令 | ||
|
||
### husky 是如何解决原生的 git hooks 的问题的 | ||
|
||
- 原生 `git hooks` 主要的问题是 git 无法跟踪 `.git/hooks` 下的文件,这个问题已经通过 `git core.hooksPath` 解决了 | ||
- 开发者需要手动设置 `git core.hooksPath`,`husky init` 命令中帮助我们设置了 `git core.hooksPath`,然后在 `package.json` 的 `scripts` 中添加 `"prepare": "husky"`,这样每次安装依赖的时候就会执行 `husky`,因此就可以保证设置的 `git hooks` 可以被触发了 | ||
|
||
## Lint-staged | ||
|
||
### 什么是 Lint-staged? | ||
|
||
在 `pre-commit` hook 中均为对当前 `commit` 的文件进行校验、格式化等,而不是对全局的文件进行校验,因此在脚本中我们需要知道当前在 `Git` 暂存区的文件有哪些,而 `Git` 本身也没有向 `pre-commit` 脚本传递相关参数,[Lint-staged](https://github.com/okonet/lint-staged) 这个包为我们解决了这个问题。 | ||
|
||
简单说就是当我们触发 `pre-commit` hook 中的脚本命令后,配合 `Lint-staged` 的配置可以只检查暂存区的文件从而避免我们每次检查都把整个项目的代码都检查一遍的尴尬情况。其次,`Lint-staged` 允许指定不同类型后缀文件执行不同指令的操作,并且可以按步骤再额外执行一些其它 `shell` 指令。 | ||
|
||
**Lint-staged 是如何知道当前暂存区有哪些文件的?** | ||
|
||
Lint-staged 内部也没有什么高级操作,它在内部运行了 `git diff --staged --diff-filter=ACMR --name-only -z` 命令,这个命令会返回暂存区的文件信息,类似如下所示的代码 | ||
|
||
```js | ||
const { execSync } = require('child_process'); | ||
const lines = execSync( | ||
'git diff --staged --diff-filter=ACMR --name-only -z', | ||
).toString(); | ||
|
||
const stagedFiles = lines.replace(/\u0000$/, '').split('\u0000'); | ||
``` | ||
|
||
### 使用 | ||
|
||
1. 安装 `lint-staged` | ||
|
||
```bash | ||
pnpm install lint-staged | ||
``` | ||
|
||
2. 配置 `lint-staged` | ||
|
||
`lint-staged` 只是做文件过滤的,不会做任何的格式化操作,均需要搭配对应的 `eslint/prettier/stylelint` 等等。 | ||
|
||
可以将 `lint-staged` 的配置写在 `package.json` 中或者 `.lintstagedrc.js` 中 | ||
|
||
```js | ||
// .lintstagedrc.js | ||
module.exports = { | ||
'*.{ts,tsx,js,jsx}': ['eslint --fix', 'prettier --write'], | ||
}; | ||
``` | ||
|
||
3. 添加命令 | ||
```js | ||
echo "pnpm exec lint-staged" > .husky/pre-commit | ||
``` | ||
|
||
当上诉步骤都配置好了之后,执行 `git commit -m ""` ,就会执行对应的 `lint-staged` 校验 | ||
|
||
![Untitled](/blog/imgs/code-inspection/Untitled.png) | ||
|
||
### 工作流程 | ||
|
||
![Untitled](/blog/imgs/code-inspection/Untitled%201.png) | ||
|
||
## 总结 | ||
|
||
本文主要讲解了 husky 出现的原因解决了什么问题,以及如何使用 lint-staged 对我们的代码进行校验 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
--- | ||
title: 规范提交记录 | ||
group: | ||
title: 项目规范 | ||
order: 5 | ||
order: 1 | ||
--- | ||
|
||
## 前言 | ||
|
||
在[前一篇文章](/engineering/code-inspection)中,主要讲解了利用 `git hooks` 做一些 `pre-commit` 代码检查的工作,主要是为了提交到仓库的代码保持统一的风格,因此采用 `git-hooks` 来做检查。 | ||
|
||
在我们提交 `commit` 信息的时候,我们还需要写 commit msg,对于 msg 来说,我们也想保持一种格式,规定好每一个变更的格式与内容。 | ||
|
||
那么就需要引入 `commitlint/commitizen` 等工具来完善该功能。 | ||
|
||
## 概念 | ||
|
||
### commitlint | ||
|
||
执行 `git commit -m ”xxxx”` 的时候,用来检查 xxx 是否符合固定格式的 | ||
|
||
### commitizen | ||
|
||
基于 Node.js 的 `git commit` 命令行工具,辅助生成标准的 `commit message`,具体的操作会交给适配器做 | ||
|
||
### adapter | ||
|
||
`commitizen` 命令行工具的交互方式插件。例如 `cz-emoji`、`cz-emoji-chinese`、`cz-conventional-changelog` 等等 | ||
|
||
## 使用 | ||
|
||
### 安装 commitizen | ||
|
||
全局安装 commitizen,这样子就能够全局使用 `cz/git-cz/git cz` 等命令了 | ||
|
||
```bash | ||
pnpm install -g commitizen | ||
``` | ||
|
||
### 安装 cz-git | ||
|
||
安装 `commitizen` 对应的适配器,目前我们选择 `cz-git` | ||
|
||
```bash | ||
pnpm install -D cz-git | ||
``` | ||
|
||
在 `packsge.json` 中添加对于 `commitizen` 的配置 | ||
|
||
```bash | ||
{ | ||
"config": { | ||
"commitizen": { | ||
"path": "node_modules/cz-git" | ||
} | ||
} | ||
} | ||
``` | ||
|
||
当我们再次运行 `git-cz` 的时候,就会出现 | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled.png) | ||
|
||
### 安装 commitlint | ||
|
||
```bash | ||
pnpm install -D @commitlint/config-conventional @commitlint/cli | ||
``` | ||
|
||
`@commitlint/cli` 是 `commitlint` 提供的命令行工具 | ||
|
||
`@commitlint/config-conventional` 是社区中一些共享的配置,是根据 Angular 提交规范预定义的规则包 | ||
|
||
定义 `commitlint.config.js` 配置 `lint` 规则,[cz-git 配置](https://cz-git.qbb.sh/zh/config/),选择 emoji 模版后运行 `git-cz` | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled%201.png) | ||
|
||
能够看到我们的提交信息都是有一定格式的 | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled%202.png) | ||
|
||
## standard-version | ||
|
||
在我们需要发版本时,往往需要生成对应的 CHANGELOG.md,如果还是一条一条的写,未必也有点太蠢了。当 commit 信息规范之后,有对应的工具来帮我们完成这个事情。 | ||
|
||
上述用到的 `conventional-changelog` 其实一个完整的[生态](https://github.com/conventional-changelog),能够 `lint message/generate changelogs/automate versioning` | ||
|
||
### **安装** | ||
|
||
```bash | ||
pnpm add standard-version -D | ||
``` | ||
|
||
添加脚本,执行 `pnpm release` 之后能够自动生成 changeLog 和 package version | ||
|
||
```bash | ||
{ | ||
"scripts": { | ||
"release": "standard-version" | ||
} | ||
} | ||
``` | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled%203.png) | ||
|
||
### 原理 | ||
|
||
1. 解析 Git 提交历史,查找符合标准格式的提交信息。`Standard-version` 期望的提交信息格式是符合 `Conventional Commits` 规范的。 | ||
|
||
2. 根据提交信息的类型(feat、fix、docs 等)和关键字(BREAKING CHANGE、MAJOR、MINOR 等),确定新版本号的增量。例如,如果提交信息中包含 `BREAKING CHANGE` Standard-version 将增加主版本号(Major);如果提交信息中包含 `feat` 类型的提交,Standard-version 将增加次版本号(Minor);如果包含 `fix` 类型的提交,将增加修订版本号(Patch)。 | ||
|
||
3. 更新项目的 `package.json` 文件中的版本号,并生成一个新的 Git 标签。新版本号将根据增量自动计算。 | ||
|
||
4. 通过 `conventional-changelog` 根据提交信息生成 `CHANGELOG` 文件,其中包含了自上一个版本以来的所有变更。 | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled%204.png) | ||
|
||
### changelog | ||
|
||
`standard-version` 会根据 `commit` 信息生成对应的 `CHANGELOG`,如果我们在 `commit` 信息中使用了 `#number` 这个形式,`#number` 会自动的变成对应仓库的 `issue` 信息 | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled%205.png) | ||
|
||
如果我们添加 `repository.url` 信息,提交对应的 `commit`,能够发现 `#number` 会自动关联上 `repository`,`commit` 信息也是如此 | ||
|
||
![Untitled](/blog/imgs/regulatory-submission-records/Untitled%206.png) | ||
|
||
在我们的业务迭代中,我们往往会使用其他的 bug 管理系统,不是放在 issue 上,希望能够替换 changelog 上的地址 | ||
|
||
`standard-version` 提供了对应的 [hook](https://github.com/conventional-changelog/standard-version?tab=readme-ov-file#lifecycle-scripts) 做生命周期上其他的处理,我们希望更改 `changelog` 需要在 `postchangelog` 上做处理 | ||
|
||
在 `package.json` 中新增如下的代码即可 | ||
|
||
```bash | ||
{ | ||
"standard-version": { | ||
"scripts": { | ||
"postchangelog": "handle changelog" | ||
} | ||
} | ||
} | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.