自动生成 changelog.md,做一名有追求的工程师

3,720 阅读4分钟

前面写过四篇文章,各自讲述了Git Hooks 与 Huskycommitlint + Husky 规范 git commit 日志根据 commit message 自动生成 changelog如何自定义 conventional-changelog,这篇文章是将这些知识整合起来,应用到为自动生成 changelog


commitlint + Husky 规范 git commit 日志

规范 commit 日志的好处

  • 团队形成一致的代码提交风格,更好的提高工作效率
  • 规范的 commit message 有助于团队其它人员 review, 还可以有效的输出 CHANGELOG, 对项目十分重要
  • 成为一名有追求的工程师

安装 & 配置 commitlint


安装 commitlint cliconfig-conventional

npm install --save-dev @commitlint/{config-conventional,cli}

# For Windows:

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

创建 commitlint.config.js 配置 commitlint

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

安装 & 配置 husky

安装 husky

npm install husky --save-dev

启用 Git hooks

npx husky install

添加 commit-msg hook

运行以下命令创建 commit-msg hook

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


# 有时上面的命令在某些命令解释器中不起作用

# 你可以尝试下面的命令在commit-msg文件中写入 npx --no -- commitlint --edit $1

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

# or

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

提交 commit,发现 commitlint 已经生效,不符合 commitlint 规范的 git commit 的将被停止

Tip

如果你遇到如下报错,删除node_modules并执行 npm install 重新安装即可

.husky/_/husky.sh: No such file or directory

commitlint 规范

提交格式

git commit -m <type>(scope?): <subject>

Examples

git commit -m 'chore: run tests on travis ci'

git commit -m 'fix(server): send cors headers'

git commit -m 'feat(blog): add comment section'

注意,英文冒号 + 空格


常用的 type 类别

type:用于表明我们这次提交的改动类型,是新增了功能?还是修改了测试代码?又或者是更新了文档?总结以下 11 种类型:

  • build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交
  • ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交
  • docs:文档更新
  • feat:新增功能
  • fix:bug 修复
  • perf:性能优化
  • refactor:重构代码(既没有新增功能,也没有修复 bug)
  • revert:回滚某个更早之前的提交
  • style:不影响程序逻辑的代码修改(修改空白字符,补全缺失的分号等)
  • test:新增测试用例或是更新现有测试
  • chore:不属于以上类型的其他类型(日常事务)

更多的 type 说明 你可以参阅这里 commitlint type-enum

scope:作用域,可选。用于标识此次提交主要涉及到代码中哪些模块。支持多作用域(可使用分隔符:"/"、"" 和 ","

subject:一句话描述此次提交的主要内容,做到言简意赅。


自定义 commitlint 规范

commitlint.js.org/#/reference…

在这里插入图片描述

我们可以通过修改 commitlint.config.js 来自定义我们的提交规范,如:

rule配置说明:rulename和配置数组组成,如:'name: [0, 'always', 72]',数组中第一位为level,可选0,1,2,0为disable,1为warning,2为error;第二位为是否启用,可选always|never,第三位该rule的值。具体配置例子如下:

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'test', 'upd', 'feat', 'fix', 'refactor', 'docs', 'chore', 'style', 'revert',
    ]],
    'type-case': [0],
    'type-empty': [0],
    'scope-empty': [0],
    'scope-case': [0],
    'subject-full-stop': [0, 'never'],
    'subject-case': [0, 'never'],
    'header-max-length': [0, 'always', 72],
  },
};

根据 commit message 自动生成 changelog

安装依赖

npm install conventional-changelog conventional-changelog-cli conventional-changelog-custom-config compare-func --save-dev

或者

yarn add conventional-changelog conventional-changelog-cli conventional-changelog-custom-config compare-func --save-dev

自定义 changelog

在根目录创建的 changelog-option.js 文件

const compareFunc = require('compare-func')

module.exports = {
  writerOpts: {
    transform: (commit, context) => {
      let discard = true
      const issues = []
      
      commit.notes.forEach(note => {
        note.title = 'BREAKING CHANGES'
        discard = false
      })
      if (commit.type === 'feat') {
        commit.type = '✨ Features | 新功能'
      } else if (commit.type === 'fix') {
        commit.type = '🐛 Bug Fixes | Bug 修复'
      } else if (commit.type === 'perf') {
        commit.type = '⚡ Performance Improvements | 性能优化'
      } else if (commit.type === 'revert' || commit.revert) {
        commit.type = '⏪ Reverts | 回退'
      } else if (discard) {
        return
      } 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 | 其他更新'
      }

      if (commit.scope === '*') {
        commit.scope = ''
      }

      if (typeof commit.hash === 'string') {
        commit.hash = commit.hash.substring(0, 7)
      }

      if (typeof commit.subject === 'string') {
        let url = context.repository
          ? `${context.host}/${context.owner}/${context.repository}`
          : context.repoUrl
        
        if (url) {
          url = `${url}/issues/`
          // Issue URLs.
          commit.subject = commit.subject.replace(/#([0-9]+)/g, (_, issue) => {
            issues.push(issue)
            return `[#${issue}](${url}${issue})`
          })
        }

        if (context.host) {
          // User URLs.
          commit.subject = commit.subject.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_, username) => {
            if (username.includes('/')) {
              return `@${username}`
            }
            return `[@${username}](${context.host}/${username})`
          })
        }
      }

      // remove references that already appear in the subject
      commit.references = commit.references.filter(reference => {
        if (issues.indexOf(reference.issue) === -1) {
          return true
        }
        return false
      })
      return commit
    },
    groupBy: 'type',
    commitGroupsSort: 'title',
    commitsSort: ['scope', 'subject'],
    noteGroupsSort: 'title',
    notesSort: compareFunc
  }
}

添加脚本

修改 package.json 中 scripts 字段

{
  "scripts": {
     "changelog": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0  -n ./changelog-option.js"
  }
}

在这指定配置文件位置,我们放在了根目录,也可以指定其他地方*

*配置项说明:

  • -p custom-config 指定风格*
  • -i CHANGELOG.md 指定输出的文件名称*
  • *-s -r 0 指定增量更新,不会覆盖以前的更新日志
  • -n ./changelog-option.js 指定自定义配置*

添加 pre-commit hook

npx husky add .husky/pre-commit "npm run changelog"

运行完该命令后我们会看到 .husky/ 目录下新增了一个名为 pre-commit 的 shell 脚本。也就是说在在执行 git commit 命令时会先执行 pre-commit 这个脚本。pre-commit 脚本内容如下

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run changelog

自动生成 CHANGELOG.md 文件

执行 git commit 的时候会自动生成


手动生成 CHANGELOG.md 文件

直接运行下面的命令即可

npm run changelog

在这里插入图片描述

如果typefeatfixperfrevert,则该 commit 将肯定出现在 Change log 之中。其他情况(docschorestylerefactortest)由你决定(通 commitlint.config.js 配置)。


如何使用


第一步,提交 commit

按照上面的 git commit 提交格式提交代码


第二步,用命令升级版本号

注意,版本的log使用 npm version <newversion> 才会生成新的log,手动修改 package.json version 不会有新的版本log

我们使用如下命令来升级项目版本号

命令作用执行结果version
npm version patch升级修订号首次执行2.0.0-0 -> 2.0.0
再次执行2.0.0 -> 2.0.1
npm version minor升级次版本号2.0.1 -> 2.1.0
npm version major升级主版本号2.1.0 -> 3.0.0


参阅