详解如何使得代码提交更规范(lint-staged、commitlint、commitizen、conventional-changelog-cli)

5,411 阅读10分钟

本文主要解决以下问题:

  1. 如何在代码提交时进行代码规范检查(lint-staged)?
  2. 如何使用工具生成符合Angular commit规范的message?
  3. 如何通过gitHooks验证message是否符合规范?
  4. 如何使用工具自动生成changelog? 此文对应的github代码仓库:github.com/eyunhua/cod…

如果你想了解更详细的过程,请认真往下阅读;如果你想快速查看如何使用,请看『总结』目录。

前言

在一个多人协作的项目中,简洁清晰易懂的代码提交注释是能够快速定位问题的有效方式。commit message应该说明本次提交的目的。

对比下面这两张代码提交commit log图:

第一张:message没有实际的意义,看不出来本次提交改动的范围。

图1.png

第二张:message简洁清晰易懂,可以看出当前是新增功能、修复问题等,并可看出此次提交影响的范围。

图2.png

通过上面两张图可以看出,message写的符合规范是多么的重要!

那我们在日常工作中,仅仅通过人工去输入message,无法完全保证message提交的规范性。

接下来介绍一种社区比较流行的规范Angular 规范,并且通过工具去生成符合这种规范的message信息。

Angular commit规范

介绍

规范格式概览

<type>(<scope>): <subject> #header部分
// 空一行
<body>
// 空一行
<footer> 

Header 是必需的,Body 和 Footer 可以省略。

规范格式详解

Header

Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。

type

用于说明本次commit的类型 只允许使用下面7个标识

  • feat:新功能(feature)
  • fix:修补bug
  • docs:文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  • perf: 性能提升(提高性能的代码改动)
  • test:测试
  • build:构建过程或辅助工具的变动(webpack等)
  • ci:更改CI配置文件和脚本
  • chore:不修改src或测试文件的其他更改
  • revert:撤退之前的commit

如果type为feat、fix、perf、revert,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、test)由你决定,要不要放入 Change log,建议是不要。

scope

用于说明本次commit影响的范围,比如首页、详情页等。

subject

用于说明本次commit的简短描述,不超过50个字符。

Body

用于本次commit的详细描述,可以分成多行 举例(实际应用应说明具体改动):

more info... 

- first changes...
- second changes...

Footer

只用于下面两种情况

不兼容变动

如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。

举例:

vue-next changelog.md:github.com/vuejs/vue-n…

BREAKING CHANGE: `getTextMode` compiler option signature has changed from

  ``ts
  (tag: string, ns: string, parent: ElementNode | undefined) => TextModes
  ``

  to

  ``ts
  (node: ElementNode, parent: ElementNode | undefined) => TextModes
  ``

关闭Issue

如果当前 commit 针对某个issue,那么可以在 Footer 部分关闭这个 issue 。

Closes #123, #234

Revert(可忽视)

还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。

revert: feat(pencil): add 'graphiteWidth' option

This reverts commit 667ecc1654a317a13331b17617d973392f415f02.

Body部分的格式是固定的,必须写成This reverts commit .,其中的hash是被撤销 commit 的 SHA 标识符。

如果当前 commit 与被撤销的 commit,在同一个发布(release)里面,那么它们都不会出现在 Change log 里面。如果两者在不同的发布,那么当前 commit,会出现在 Change log 的Reverts小标题下面。

代码规范检查:lint-staged

检测暂存区的代码是否符合规范

介绍

在代码提交之前,进行代码规则检查能够确保进入git库的代码都是符合代码规则的。但是整个项目上运行lint速度会很慢,lint-staged能够让lint只检测暂存区的文件,所以速度很快。

Install

npx mrm@2 lint-staged

此命令将根据项目的package.json依赖项中的代码质量工具安装和配置huskylint staged,因此,请确保在此之前安装(npm install—save dev)并配置所有代码质量工具,如PrettierESLint

运行完上述命令后,将会在package.json中出现:

"lint-staged": {
    "*.{js,vue}": [ // 对不同的后缀文件,运行不同的检查命令
      "vue-cli-service lint",
      "git add"
    ],
    "*.{ts}": [ // 对不同的后缀文件,运行不同的检查命令
      "xxx"
    ]
}

husky中使用

.husky目录下的pre-commit钩子脚本中,添加如下脚本:

npx lint-staged

yorkie中使用

package.json中,添加如下脚本:

{
    "gitHooks": {
        "pre-commit": "lint-staged"
    }
}

配置完脚本后,在我们执行git commit -m 'xxx'提交代码时,就会自动触发pre-commit钩子进行代码规范检查。

commit-msg检查方式一:脚本

package.json文件:

"gitHooks": {
    "commit-msg": "node build/verify-commit-msg.js"
}

build/verify-commit-msg.js文件:

返回非0值则退出

/* eslint-disable */
const chalk = require("chalk");
const msgPath = process.env.GIT_PARAMS;
const msg = require("fs")
    .readFileSync(msgPath, "utf-8")
    .trim();
// ([A-Za-z0-9]+-[A-Za-z0-9]+)+ 
const commitRE = /^(build|chore|ci|docs|feat|fix|wip|perf|refactor|revert|style|test|temp|)(\(.+\))?: .{1,50}/;
if (!(commitRE.test(msg) || msg.indexOf("Merge") === 0)) {
    console.error(
        `  ${chalk.bgRed.white(" ERROR ")}
  [${chalk.red(msg)}] 是 ${chalk.red("无效的提交消息格式")}
  ${chalk.red("自动生成更新日志需要正确的提交消息格式 例如:")}

  ${chalk.green("issue-1 feat(模块): 预发布环境增加 A 模块")}
  ${chalk.green("issue-2 fix(文案): 修复错误文案")}`
    );
    process.exit(1);  // 返回非0值得退出
}
/* eslint-enable */

commit-msg检查方式二:commitlint

检查git commit -m 'xxx'的message是否符合规范

Install

项目内安装@commitlint/cli@commitlint/config-conventional

npm install --save-dev @commitlint/cli @commitlint/config-conventional

Configure

可以在commitlint.config.js.commitlintrc.js.commitlintrc.json.commitlintrc.yml文件或package.json中的commitlint字段中定义配置。

当前以commitlint.config.js为例:

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

在husky中使用

添加.husky/commit-msg钩子,内容为npx --no-install commitlint --edit $1

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

在yorkie中使用

package.json

"gitHooks": {
    "commit-msg": "commitlint --edit"
 }

Test

经过安装commitlintcommitlint.config.js配置项,并且在hooks钩子中配置之后,就可以在提交时校验message是否符合规范了。

输入不符合规范的message,将会出现如下的错误提示: image.png

仅仅通过人工输入message,显然是不可靠的,commitlint提供了一个可以生成message的工具@commitlint/prompt-cli

生成message方式一:commitlint

commitlintcomitizen提供了两种方式:

方式一:@commitlint/prompt-cli

Install

npm install --save-dev @commitlint/prompt-cli

Usage

package.json中加入一个npm run script

"scripts": {
    "commit": "commit"
}

执行

git add .
npm run commit

运行npm run commit将会出现如下图:

根据提示一步一步输入commit message image.png

方式二:@commitlint/cz-commitlint

npm介绍

Install

npm install --save-dev @commitlint/cz-commitlint commitizen

Usage

package.json

{
  "scripts": {
    "commit": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "@commitlint/cz-commitlint"
    }
  }
}

执行

git add .
npm run commit

运行npm run commit将会出现如下图:

根据提示一步一步输入commit message

image.png

prompt

可以在commitlint.config.js配置文件中进行rulesprompt相关的规则,详细请查看官方文档戳这里

检查历史提交message

npx commitlint -- --from HEAD~1 --to HEAD --verbose

生成message方式二:Commitizen

Commitizen是一个撰写合格 Commit message 的工具。

全局安装commitizen

npm install -g commitizen

项目初始化commitizen

在项目目录里,运行下面的命令,使其支持 Angular 的 Commit message 格式

// 使用npm包cz-conventional-changelog进行初始化
commitizen init cz-conventional-changelog --save --save-exact

cz-conventional-changelog介绍

执行完上述命令后,会往package.json文件中的devDependencies中加入cz-conventional-changelog包,并且自动增加config配置项,如下:

"devDependencies": {
    "cz-conventional-changelog": "^3.3.0"
},
"config": {
   "commitizen": {
       "path": "./node_modules/cz-conventional-changelog"
    }
}

之后,凡是用到git commit命令,一律改为git cz。这时就会出现选项,用来生成符合格式的commit message。

image.png

我们也可以在package.json文件中的script中配置命令:

"scripts": {
    ...
    "commit": "git cz"
},

之后在执行git commit时,直接运行npm run commit即可!

完整版生成message示例

如果想生成Breaking changes,请在Are there any breaking changes?的时候选y

image.png

生成changelog

介绍

如果你的所有commit都符合Angular commit规范,那么发布新版本时,就可以通过脚本自动生成changelog。

示例:vue-next v3.1.0 changelog

生成的changelog文档包括以下几个部分:

  • Features(对应type: feat)
  • Bug Fixes(对应type: fix)
  • Code Refactoring (对应type: refactor且breaking changes为y)
  • Performance Improvements(对应type: perf)
  • Reverts (对应type: revert)
  • BREAKING CHANGES (显示body中为BREAKING CHANGES的内容)

每个部分都会罗列相关的 commit ,并且有指向这些 commit 的链接。当然,生成的文档允许手动修改,所以发布前,你还可以添加其他内容。

conventional-changelog-cli

用来生成changelog的工具 conventional-changelog-cli介绍

安装

npm install -g conventional-changelog-cli

生成changelog

使用下述命令可生成changelog:

conventional-changelog -p angular -i CHANGELOG.md -s

上面命令不会覆盖以前的 Change log,只会在CHANGELOG.md的头部加上自从上次发布以来的变动。

如果你想生成所有发布的 Change log,需要运行下面的命令:

conventional-changelog -p angular -i CHANGELOG.md -s -r 0

为了方便使用,可以将其写入package.jsonscripts字段。

{
  "scripts": {
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
  }
}

以后,直接运行下面的命令即可。

npm run changelog

changelog生成的依据

可能会有这样的疑问,如何生成某一个版本号所对应的changelog呢?

那就涉及到了package.json中的version字段,并且要在此版本开发完成的commit上创建一个tag

这样,运行npm run changelog的时候,就会根据versiontag生成对应的版本log。

示例

1. commit log如图:

image.png 2. 生成的changelog如图:

image.png

总结

代码检查

// 安装
npx mrm@2 lint-staged
// 在package.json中配置如下脚本检查
"lint-staged": {
    "*.{js,vue}": [ // 对不同的后缀文件,运行不同的检查命令
      "vue-cli-service lint",
      "git add"
    ],
    "*.{ts}": [ // 对不同的后缀文件,运行不同的检查命令
      "xxx"
    ]
}
// husky中使用,`.husky/pre-commit`中,添加如下脚本:
npx lint-staged
// yorkie中使用,`package.json`中,添加如下脚本:
"gitHooks": {
    "pre-commit": "lint-staged"
}

验证message规范性

脚本

package.json

"gitHooks": {
    "commit-msg": "node build/verify-commit-msg.js"
}

build/verify-commit-msg.js

const chalk = require("chalk");
const msgPath = process.env.GIT_PARAMS;
const msg = require("fs")
    .readFileSync(msgPath, "utf-8")
    .trim();
const commitRE = /^(build|chore|ci|docs|feat|fix|wip|perf|refactor|revert|style|test|temp|)(\(.+\))?: .{1,50}/;
// 根据正则表达式校验提交的message是否符合团队规范
if (!(commitRE.test(msg) || msg.indexOf("Merge") === 0)) {
    console.error(
        `  ${chalk.bgRed.white(" ERROR ")}
  [${chalk.red(msg)}] 是 ${chalk.red("无效的提交消息格式")}
  ${chalk.red("自动生成更新日志需要正确的提交消息格式 例如:")}

  ${chalk.green("issue-1 feat(模块): 预发布环境增加 A 模块")}
  ${chalk.green("issue-2 fix(文案): 修复错误文案")}`
    );
    // 以非0值退出,放弃提交
    process.exit(1);
}

commitlint

// 安装
npm install --save-dev @commitlint/cli @commitlint/config-conventional
// 配置
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
// 在husky中使用
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'
// 在yorkie中使用
"gitHooks": {
    "commit-msg": "commitlint --edit"
}

生成message

@commitlint/prompt-cli

// 安装
npm install --save-dev @commitlint/prompt-cli

package.json配置script脚本:

"scripts": {
    "commit": "commit"
}
// 提交时,直接运行npm run commit 即可

@commitlint/cz-commitlint

// 安装
npm install --save-dev @commitlint/cz-commitlint commitizen

package.json

{
    "scripts": {
        "commit":"git-cz"
    },
    "config": {
        "commitizen": {
            "path": "@commitlint/cz-commitlint"
         }
    }
}
// 提交时,直接运行npm run commit 即可

commitizen

npm install -g commitizen
// 切换至项目目录
commitizen init cz-conventional-changelog --save --save-exact
// 命令行执行git cz生成commit message
git cz   // 或者package.json配置`scripts`

生成changelog

// 安装
npm install -g conventional-changelog
// 生成全部changelog
conventional-changelog -p angular -i CHANGELOG.md -s -r 0
// 生成自上次版本号变更以来changelog
conventional-changelog -p angular -i CHANGELOG.md -s

常见问题

1. gitHooks钩子验证时,仅可保留下述方式的其中一种:

  • .git/hoos/目录下,去掉.sample后缀后生效的钩子,如

image.png

  • package.json中通过gitHoos定义的钩子,可自定义通过npm包或者js文件去校验

image.png 2. 每一次版本发布记得修改package.json中的version版本,生成changelog需根据版本号去生成

参考代码库

参考文档

  1. www.jianshu.com/p/c7e40dab5…
  2. www.ruanyifeng.com/blog/2016/0…
  3. git 钩子官方介绍
  4. www.jianshu.com/p/62b5c6471…
  5. vue代码库
  6. commitlint