前面讲的都是在git
提交之前的一些检查流程,然而我们git
提交信息的时候,也应该是需要规范的。直接进入主题:
目录
需安装插件清单
依赖 | 描述 |
---|---|
@commitlint/cli | git提交规范检验工具,对提交信息的 message 规范进行校验 |
@commitlint/config-conventional | 基于 conventional commits 规范的配置文件 |
czg | 交互式命令行工具生成标准化的 git commit message |
commitlint
介绍
在使用Git
提交代码时,通常都需要填写提交说明,也就是Commit Message
git commit -m '提交测试'
说白了,Commit Message
就是我们提交的时候,在-m
后面写的提交说明,在小项目中基本是随意去写这个message
的,但是当项目到了一定规模,什么东西都需要形成规范,包括这个提交Message
,不然协同开发的同事根本不知道你这次提交到底是在干嘛,只能通过点开Git Graph
查看明细,或口头阐述给对方。
当然,仅仅只是口头约束并没有实质上的作用,为了禁止不符合规范的Commit Message
的提交,我们就需要采用一些工具,只有当开发者编写了符合规范的Commit Message
才能够进行commit
。而commitlint
就是这样一种工具,通过结合husky
一起使用,可以在开发者进行commit
前就对Commit Message
进行检查,只有符合规范,才能够进行commit
。
上面我们提到过,git
最常用的钩子函数有两个,一个是pre-commit
,前面我们已经对这个阶段需要做的规范做了介绍,并且还使用了lint-staged
工具。另外一个常用的钩子函数就是commit-msg
,在这个阶段,用到的工具就是commitlint
。
安装
需要下载两个依赖包:
pnpm add @commitlint/cli @commitlint/config-conventional -D
注意:现阶段由于
stylelint
版本的问题,可能会和之前的一些包产生冲突,如果你用的是npm
,这里还是直接使用--legacy-peer-deps
忽略到依赖冲突,后面的一些安装都有可能造成这个问题,就不再重复了。
通过 npm
安装时添加 --legacy-peer-deps
注意事项
在使用 npm 安装包时,--legacy-peer-deps
参数的作用是告诉 npm 在处理依赖关系时采用旧的(legacy)方式,而不是严格按照 npm 7 及更新版本的新规则来处理对等依赖(peer dependencies)。
从 npm 7 开始,npm 引入了一些更严格的对等依赖解析规则。这些规则要求依赖包必须显式地声明其对其他包的对等依赖,否则 npm 会给出警告或错误,并且可能会拒绝安装或更新这些依赖。
使用 --legacy-peer-deps
参数可以绕过这些新规则,采用更宽松的对等依赖解析方式,允许安装那些在新规则下可能会被拒绝的依赖。
假设你要安装一个依赖包 example-package
,并且你知道这个包的依赖可能会与新的 npm 对等依赖规则冲突。你可以这样使用 --legacy-peer-deps
:
npm install @commitlint/cli @commitlint/config-conventional -D --legacy-peer-deps
这样 npm 将会使用旧的对等依赖解析方式来安装 example-package
及其依赖,而不会严格遵循 npm 7 的新规则。
注意了,使用 --legacy-peer-deps
应谨慎,因为它可能会导致依赖安装时出现不一致或错误的情况。只有在你确信使用旧规则不会影响项目稳定性或安全性时才应该使用这个参数。
依赖说明
依赖 | 描述说明 |
---|---|
@commitlint/config-conventional | 基于 conventional commits 规范的配置文件 |
@commitlint/cli | commitlint 工具的核心 |
配置
具体的规范配置可以查看: conventional-changelog/commitlint
在项目根目录下创建配置文件 commitlint.config.cjs
# 使用命令行创建文件
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.cjs
commitlint.config.cjs
内容:
module.exports = { extends: ['@commitlint/config-conventional'] };
以下是我经常使用的type-enum
,这里定义的Commit Message
就是继承了@commitlint/config-conventional规则集
,这个规则集定义了Git
提交信息定义一致性格式,使得提交信息更易于理解和自动化处理。
feat: 新增功能
fix: 修复缺陷
docs: 文档变更
style: 代码格式
refactor: 代码重构
perf: 性能优化
test: 添加测试
build: 构建相关
ci: 持续集成修改
revert: 回滚到上一个版本
wip: 开发中
chore: 构建过程或辅助工具的变动
配置commit-msg
钩子
上面的commitlint
配置好之后,我们还需要commit-msg
钩子函数触发
# V9
echo "npx --no-install commitlint --edit $1" > .husky/commit-msg
提交填写commit
信息
现在我们提交的时候,就不需要再写git commit -m "提交测试"
这种简单的message
信息了。我们只需要执行git commit
,钩子函数会自动帮我们弹出一个Vim
风格的文本输入框。
Vim
是一款经典的文本编辑器,它拥有强大的编辑和操作功能,以下是一些常用的Vim
风格操作:
i
进入插入模式,可以开始编辑文本。Esc
退出插入模式,回到命令模式。:wq
保存并退出编辑器。:q!
放弃修改并退出编辑器。dd
删除当前行。yy
复制当前行。p
粘贴复制的内容。
在Vim
文本编辑器,需要填写按规范提交。符合规范的Commit Message
的提交格式如下,包含了页眉(header), 正文(body) 和 页脚(footer) 三部分。其中,header
是必须的,body
和footer
可以忽略。
type([scope]): subject
// 空一行
[body]
// 空一行
[footer]
需要注意的是,在commitlint.config.cjs
配置了哪些类型,Commit Message
时候type
就只能哪些类型,想要更多的type
类型,可以继承extends
其他的规范集,或者自己写。
czg
上面Vim
模式,这种需要开发者一个个输入commit
信息的形式很容易出现问题,因此czg CLI
做的事情很简单,就是帮我们提供了一个交互式撰写commit
信息的插件。
-
为什么选择
czg CLI
?- 我们先来讲讲他的历史迭代。在
czg
还没问世之前,他的前身是Commitizen CLI + cz-git
。然而cz-git
一直以来都是作为Commitizen CLI
的适配器,作者Q.Ben就是为了解决这种强依赖关系,所以开发了czg
。这里简单的理解就是czg CLI = (Commitizen CLI + cz-git)
。
- 我们先来讲讲他的历史迭代。在
-
特性优点
-
🤖 OpenAI 支持. 让 AI 来辅助生成你 git commit 的描述信息
-
⚡️ 轻量级 :零依赖项 (1.31MB)。
-
🤗 简单且快速 : 无需前置配置,无需适配器,没有额外的步骤,你可以使用
npx | npm 脚本 | 全局下载...
在你的任何项目中快速启动。 -
😎 高度可定制化 : 内部包含
cz-git
的核心,继承了cz-git
的所有特性,具有相同的行为,配置加载… 你可以根据自己的需要配置的 CLI 的行为。
-
-
在过去
- 使用
cz-git
的安装使用流程npm install -g commitizen npm install -D cz-git
// package.json { "scripts": { "commit": "git-cz" }, "config": { "commitizen": { "path": "node_modules/cz-git" } } }
- 使用
-
而现在
- 使用
czg CLI
的安装使用流程npm install -g czg
// package.json { { "scripts": { "cz": "czg" } } }
- 使用
-
总结
czg
无需任何前置配置- 在原有基础上拓展
cz-git
的基础功能 - 启动速度更快
- 更轻量化,使用
npx
在项目中启动使用 - 当然,以上两种方案可以混用: 即
czg CLI
或Commitizen CLI + cz-git
-
参考
🚀 安装
pnpm add czg -D
🚀 配置package.json脚本命令
"scripts": {
//......其他省略
"commit": "git add -A && czg && git push"
}
上面在scripts
脚本中配置了commit
命令,用来替代git commit
,并且合并了git add
和git push
命令
📹 效果演示
根据上gif图演示,1、选择提交类型,2、自定义scope
,3、输入commit
。完成了一个简易的交互式提交,当然他的亮点并不是单单的提交,它结合了我之前写的规范工程篇章1~4的代码规范检查(检查暂存区文件)。
🚀 语法提示
下面我们需要对 commitlint.config.cjs
进一步配置成,在配置之前,我们需要添加 代码提示
。添加 @type
到文件中:
/** @type {import('czg').UserConfig} */
module.exports = {
// 其他代码
}
🚀 英译中
-
commitlint.config.cjs
文件内容:不推荐使用纯中文进行commit,因为终端对于中文输入的支持并不是很友好,并且在使用搜索时没有英文交互来得自然。
推荐使用中英文对照,可以很好给予团队的新人帮助。module.exports = { // 其他代码 prompt: { messages: { type: "Select the type of change that you're committing:", scope: "Denote the SCOPE of this change (optional):", customScope: "Denote the SCOPE of this change:", subject: "Write a SHORT, IMPERATIVE tense description of the change:\n", body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n', breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n', footerPrefixSelect: "Select the ISSUES type of changeList by this change (optional):", customFooterPrefix: "Input ISSUES prefix:", footer: "List any ISSUES by this change. E.g.: #31, #34:\n", confirmCommit: "Are you sure you want to proceed with the commit above?" // 中文对照 // type: "选择你要提交的类型 :", // scope: "选择一个提交范围(可选):", // customScope: "请输入自定义的提交范围 :", // subject: "填写简短精炼的变更描述 :\n", // body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n', // breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n', // footerPrefixSelect: "选择关联issue前缀(可选):", // customFooterPrefix: "输入自定义issue前缀 :", // footer: "列举关联issue (可选) 例如: #31, #I3244 :\n", // confirmCommit: "是否提交或修改commit ?" }, types: { { value: "feat", name: "feat: | A new feature", }, { value: "fix", name: "fix: | A bug fix", }, { value: "docs", name: "docs: | Documentation only changes", }, { value: "style", name: "style: | Changes that do not affect the meaning of the code", }, { value: "refactor", name: "refactor: | A code change that neither fixes a bug nor adds a feature", }, { value: "perf", name: "perf: | A code change that improves performance", }, { value: "test", name: "test: | Adding missing tests or correcting existing tests", }, { value: "build", name: "build: | Changes that affect the build system or external dependencies", }, { value: "ci", name: "ci: | Changes to our CI configuration files and scripts", }, { value: "revert", name: "revert: | Reverts a previous commit", }, { value: "wip", name: "wip: | Work in process", }, { value: "chore", name: "chore: | Other changes that don't modify src or test files", } // 中文对照 // { value: "feat", name: "特性: | 新增功能" }, // { value: "fix", name: "修复: | 修复缺陷" }, // { value: "docs", name: "文档: | 文档变更" }, // { value: "style", name: "格式: | 代码格式(不影响功能,例如空格、分号等格式修正)" }, // { value: "refactor", name: "重构: | 代码重构(不包括 bug 修复、功能新增)" }, // { value: "perf", name: "性能: | 性能优化" }, // { value: "test", name: "测试: | 添加疏漏测试或已有测试改动" }, // { value: "build", name: "构建: | 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)" }, // { value: "ci", name: "集成: | 修改 CI 配置、脚本" }, // { value: "revert", name: "回退: | 回滚 commit" }, // { value: "wip", name: "开发: | 正在开发中" }, // { value: "chore", name: "其他: | 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)" } }, } }
🚀 加入emoji
-
要想实现直观、美观的
git
追踪流,添加emoji
图标:module.exports = { // 其他代码 prompt: { types: { { value: "feat", name: "feat: ✨ A new feature", emoji: "✨" }, { value: "fix", name: "fix: 🐛 A bug fix", emoji: "🐛" }, { value: "docs", name: "docs: 📚 Documentation only changes", emoji: "📚" }, { value: "style", name: "style: 🎨 Changes that do not affect the meaning of the code", emoji: "🎨" }, { value: "refactor", name: "refactor: 🌈 A code change that neither fixes a bug nor adds a feature", emoji: "🌈" }, { value: "perf", name: "perf: ⚡️ A code change that improves performance", emoji: "⚡️" }, { value: "test", name: "test: 🧪 Adding missing tests or correcting existing tests", emoji: "🧪" }, { value: "build", name: "build: 📦 Changes that affect the build system or external dependencies", emoji: "📦" }, { value: "ci", name: "ci: 🎡 Changes to our CI configuration files and scripts", emoji: "🎡" }, { value: "revert", name: "revert: ⏪️ Reverts a previous commit", emoji: "⏪️" }, { value: "wip", name: "wip: 🕔 Work in process", emoji: "🕔" }, { value: "chore", name: "chore: 🔨 Other changes that don't modify src or test files", emoji: "🔨" } // 中文对照 // { value: "feat", name: "特性: ✨ 新增功能", emoji: "✨" }, // { value: "fix", name: "修复: 🐛 修复缺陷", emoji: "🐛" }, // { value: "docs", name: "文档: 📚 文档变更", emoji: "📚" }, // { value: "style", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" }, // { value: "refactor", name: "重构: 🌈 代码重构(不包括 bug 修复、功能新增)", emoji: "🌈" }, // { value: "perf", name: "性能: ⚡️ 性能优化", emoji: "⚡️" }, // { value: "test", name: "测试: 🧪 添加疏漏测试或已有测试改动", emoji: "🧪" }, // { value: "build", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" }, // { value: "ci", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" }, // { value: "revert", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" }, // { value: "wip", name: "开发: 🕔 正在开发中", emoji: "🕔" }, // { value: "chore", name: "其他: 🔨 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "🔨" } }, useEmoji: true, emojiAlign: "center", } }
🚀 自定义配置scopes
光有 emoji
还不够!有时候为了其他明了展示你的修改区域范围,或给刚入职的萌新定位,我们还需要加入scopes
,就是项目业务区,它能够使Git Graph
更明确修改位置,配置:
// commitlint.config.cjs
const fs = require('node:fs')
const path = require('node:path')
const packages = fs.readdirSync(path.resolve(__dirname, "src"), { withFileTypes: true }).filter(dirent => dirent.isDirectory());
module.exports = {
rules: {
'scope-enum': [2, 'always', [...packages]]
}
}
🚀 最终效果展示
结合之前篇章1~4的代码格式化、语法校验等lint
通过之后,才会正式提交到远端仓库
后续
🌈 上面提到,最后一个动作是 git push
命令,将本地代码更新到 远端仓库
。
那么它实际并未完成整个前端工作流程,那么我们还需要借助husky
来处理自动化构建动作,触发它能做些什么呢?如:
方式一:push触动build并上传到ftp服务
- 配置
husky
监听git hooks
(push),实现自动build
本地构建。- 通过
nodejs:fs.readFileSync
识别.git/HEAD
工作区(即分支名),如果是测试分支
或者生产分支
,则构建,否则不触发构建动作。
- 通过
- 构建完成通过
ftp
插件实现文件传输。- 配置
ftp
账号信息,供ftp
插件调用; - 根据分支规则上传到对应
ftp
服务;
- 配置
方案二:push触发CI/CD工作流
- 创建CI/CD流(gitlab、github)
- 指定触发工作流的分支,如
master
; - 定义每个阶段的任务、需要的镜像依赖等
- 指定触发工作流的分支,如
- 触发动机
- 手动
push
; CR
分支合并;
- 手动
以上两种方案
第一种学习成本相对较低,主要是在开发者本地运行;
第二种学习成本相对高,自定义方面强。包括设置任务延时发布(比如你可以选择在半夜更新)、重试机制和报警(邮件通知)等等。