Commit 规范化及 CHANGELOG 生成

公司关于代码提交流程一直都很规范,大家一直都遵守规则,平时也没过多关注这方面的细节,直到前一阵子使用的 custom-config 失效了,需要自行处理 changelog,这才注意到协同开发中不仅仅要有代码规范,git commit同样很重要,所以写一篇文章记录下。

1. Commit 规范

关于 commit 规范,业内实践有很多,我们这次主要讨论 angular 方案。而自动生成 commit 方法有很多,这里介绍两种,一种是 git commit.template 提供提交模板,一种是通过 commitizen 这类工具自动生成。

1.1 Commit Template

这种方式比较简单,就是提供一份模板,然后通过 git config commit.template <模板文件> 设置 commit 模板,这样每次执行 git commit 后,默认的 commit message 就会显示成设置的模板。

1.2 Commitizen

先安装 commitizen

npm i commitizen

安装完成后进行初始化

npx commitizen init cz-conventional-changelog --save-dev --save-exact

需要进行 commit 提交时,则执行如下命令,当然也可以写入 npm scripts:

npx cz

通过以上方式,可以实现提交内容格式保持一致。

2. Commit 检查

对于 commit 的校验,方式比较多,这里介绍使用 commitlint 进行 commit 检查。

npm install --save-dev @commitlint/{cli,config-conventional}
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

需要进行 commit 检查时,则执行如下命令:

git log -1 --pretty=format:%s | npx commitlint

对于 commit 的检查,我们一般通过 gitlab hooks 在提交 mr 时做检查。

3. CHANGELOG

我们在多人协同开发的时候,会遇到打包发布版本时,需要收集更新了什么内容、修复了什么 bug,但是如果我们每次都是手动收集,那么在合入 changelog 的时候就很容易出现遗漏,所以我们需要能自动生成 changelog 的方法,这里我们介绍 conventional-changelog。

npm i conventional-changelog-cli

在需要生成 changelog 的时候,可以执行下面命令:

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

上面使用的 commit 标准虽然是 angular,但是通过 -p 参数,你也可以按你项目的实际 commit 标准选择 preset。

同时,如果你生成 commit 的标准不在支持范围之内,又或者对生成内容不太满意,还可以通过 changelog.config.js 实现自定义:

module.exports = {
  // 使用的提交消息模板
  parserOpts: {
    headerPattern: /^\[(\w+)(-\w+)?\]\s(.+)$/,
    headerCorrespondence: ['type', '_scope', 'subject'],
  },
  writerOpts: {
    transform: (commit) => {
      commit = { ...commit };

      // 更改type
      if (commit.type === 'feature') {
        commit.type = '✨ Features | 新特性';
      } else if (commit.type === 'bugfix') {
        commit.type = '🐛 Bug Fixes | Bug 修复';
      } else if (commit.type === 'enhance') {
        commit.type = '⚡ Performance Improvements | 性能优化';
      } else if (commit.type === 'revert' || commit.revert) {
        commit.type = '⏪ Reverts | 回退';
      } else if (commit.type === 'docs') {
        commit.type = '📝 Documentation | 文档';
      } else if (commit.type === 'style') {
        commit.type = '💄 Styles | 风格';
      } else if (commit.type === 'refactor') {
        commit.type = '♻ Code Refactoring | 代码重构';
      } else if (commit.type === 'test') {
        commit.type = '✅ Tests | 测试';
      } else if (commit.type === 'build') {
        commit.type = '👷‍ Build System | 构建';
      } else if (commit.type === 'ci') {
        commit.type = '🔧 Continuous Integration | CI 配置';
      } else if (commit.type === 'chore') {
        commit.type = '🎫 Chores | 其他更新';
      }

      // 短hash
      if (typeof commit.hash === 'string') {
        commit.shortHash = commit.hash.substring(0, 8);
      }

      return commit;
    },
    commitsSort: (commit1, commit2) => {
      const time1 = commit1.committerDate;
      const time2 = commit2.committerDate;
      if (time1 < time2) {
        return 1;
      } else if (time1 > time2) {
        return -1;
      }
      return 0;
    },
  },
};

其中的 parserOpts 和 writerOpts 可以参考:

4. 参考资料