发布于 

前端工程化配置(下) 规范仓库提交记录 commitlint + commitizen + cz-git + 配置

前言

  • 随着多人开发团队推进着前端工程化的不断发展,团队规范与项目系统化配套工具链条也在不断诞生。
  • lerna 或到最近兴起的 pnpm 管理 monoreo workspace
  • eslint 配合 pretter 确保团队代码格式统一性。
  • commitizen 配合 commitlintlint-stagedhusky 之间的配合,把关最后提交代码质量与 commit 信息规范。
  • 再到利用 circleci, github actiongitee go 进行CI/CD(持续集成、持续交付和持续部署)。

概念

什么是 commitlint

执行 git commit -m 'xxx' 时,用来检查 'xxx' 是否满足固定格式的工具。
为什么使用 commitlint:团队中规范了 commit 规范可以更清晰的查看每一次代码提交记录,还可以根据自定义的规则,自动生成 changeLog 文件。

什么是 commitizen

基于 Node.js 的 git commit 命令行工具,辅助生成标准化规范化的 commit message,是一个平台,具体职能交给适配器。

什么是 adapter(适配器)

更换 commitizen 命令行工具的交互方式插件。像 cz-emojicz-emoji-chinesecz-conventional-changelogcz-customizable,这些都是适配器,至于选择哪个,看个人喜好选择。

开始

本文使用 cz-git 来作为适配器,理由请看作者博客:cz-git 友好型 commitizen 的适配器

安装 commitizen

TIP

全局安装 commitizen,如此一来可以快速使用 czgit cz 命令进行启动。

1
npm install -g commitizen

在项目中使用

安装 cz-git

  1. 安装 cz-git
1
pnpm install -D cz-git
  1. package.json 添加 config 字段,为 commitizen 指定 cz-git 适配器
1
2
3
4
5
6
7
8
// package.json
{
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}
}

配置 commitlint

  1. 配置 commitlint

安装 @commitlint/config-conventional @commitlint/cli

@commitlint/cli 是 commitlint 提供的命令行工具,安装后会将 cli 脚本放置在 ./node_modules/.bin/ 目录下。
@commitlint/config-conventional 是社区中一些共享的配置,是根据 Angular 提交规范预定义的规则包,我们可以扩展这些配置,也可以不安装这个包自定义配置,commitlint 推荐我们使用 @commitlint/config-conventional 配置去写 commit

1
pnpm install -D @commitlint/cli @commitlint/config-conventional
  1. 创建 commitlint.config.js 文件

安装完成之后,在项目根目录创建 commitlint.config.js 文件,来配置 lint 规则:

cz-git 有多种模板,例如: (⇒ 配置模板),这里使用 中英对照 + Emoji 模板,虽然官方并不推荐这么干😆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// commitlint.config.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

const scopes = fs
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name.replace(/s$/, ''));

// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
.toString()
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replace(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '');

/** @type {import('cz-git').UserConfig} */
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 108],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
'subject-case': [0],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release',
],
],
},
prompt: {
/** @use `yarn commit :f` */
alias: {
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
b: 'build: bump dependencies',
c: 'chore: update config',
},
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
defaultScope: scopeComplete,
scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: true,
allowCustomIssuePrefixs: true,
messages: {
type: '选择你要提交的类型 :',
scope: '选择一个提交范围(可选):',
customScope: '请输入自定义的提交范围 :',
subject: '填写简短精炼的变更描述 :\n',
body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
footerPrefixsSelect: '选择关联issue前缀(可选):',
customFooterPrefixs: '输入自定义issue前缀 :',
footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
confirmCommit: '是否提交或修改commit ?',
},
types: [
{ value: 'feat', name: 'feat: ✨ 新增功能 | A new feature', emoji: ':sparkles:' },
{ value: 'fix', name: 'fix: 🐛 修复缺陷 | A bug fix', emoji: ':bug:' },
{
value: 'docs',
name: 'docs: 📝 文档更新 | Documentation only changes',
emoji: ':memo:',
},
{
value: 'style',
name: 'style: 💄 代码格式 | Changes that do not affect the meaning of the code',
emoji: ':lipstick:',
},
{
value: 'refactor',
name: 'refactor: ♻️ 代码重构 | A code change that neither fixes a bug nor adds a feature',
emoji: ':recycle:',
},
{
value: 'perf',
name: 'perf: ⚡️ 性能提升 | A code change that improves performance',
emoji: ':zap:',
},
{
value: 'test',
name: 'test: ✅ 测试相关 | Adding missing tests or correcting existing tests',
emoji: ':white_check_mark:',
},
{
value: 'build',
name: 'build: 📦️ 构建相关 | Changes that affect the build system or external dependencies',
emoji: ':package:',
},
{
value: 'ci',
name: 'ci: 🎡 持续集成 | Changes to our CI configuration files and scripts',
emoji: ':ferris_wheel:',
},
{ value: 'revert', name: 'revert: 🔨 回退代码 | Revert to a commit', emoji: ':hammer:' },
{
value: 'chore',
name: 'chore: ⏪️ 其他修改 | Other changes that do not modify src or test files',
emoji: ':rewind:',
},
],
useEmoji: true,
emojiAlign: 'center',
themeColorCode: '',
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlias: 'custom',
emptyScopesAlias: 'empty',
upperCaseSubject: false,
markBreakingChangeMode: false,
allowBreakingChanges: ['feat', 'fix'],
breaklineNumber: 100,
breaklineChar: '|',
skipQuestions: [],
issuePrefixs: [
// 如果使用 gitee 作为开发管理
{ value: 'link', name: 'link: 链接 ISSUES 进行中' },
{ value: 'closed', name: 'closed: 标记 ISSUES 已完成' },
],
customIssuePrefixsAlign: 'top',
emptyIssuePrefixsAlias: 'skip',
customIssuePrefixsAlias: 'custom',
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: '',
defaultIssues: '',
defaultSubject: '',
},
};

安装 husky

前端工程化配置(上)构建代码检查工作流中已经安装 husky,这里直接添加 commit hook 钩子

1
pnpx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

到这里,我们设置好了 commit hook,在 commit 阶段就会先执行 .husky/commit-msg 下面的命令,这里的 .husky/commit-msg 命令是去执行 commitlint,检查 commit message 是否符合 commitlint.config.js 文件下的配置规则,这时,如果有人 commit 不符合规范的 message 就会提示错误,退出 commit 操作

操作演示

放入暂存区(git add .)以后,执行 git cz 启动 cz-git 适配器,它会读取你的 commitlint.config.js 配置,确认提交后会先触发 pre-commit hook 执行 lint-staged 检查暂存区的代码格式是否规范并修改,之后再触发 commit-msg hook。

注意

如果你使用原生 gitcommit -m xxx 会触发 commit-msg hook,会直接打断提交,因为不符合 commitlint 规范。

最后 git push 即可。

最后,查看 github ,发现已经按照 commitlint 规范提交上去了。

参考资料

前端 Git-Hooks 工程化实践

组件库代码规范husky+lint-staged+Eslint+Prettier+Stylelint

利用husky,cz-git,@commitlint/cli规范commit message

使用commitizen来规范仓库的提交记录

husky 文档

cz-git 配置模板

vue-vben-admin 的 commitlint.config.js 配置

cz-git 友好型 commitizen 的适配器

cz-git 文档