从 Commit 规范化到发布自定义 CHANGELOG 模版

前言

最近在学习 Git 提交规范、发布及生成 CHANGELOG,最后实现本身的 CHANGELOG 模版并发布到 NPM,插件地址请戳这里html

以前对 Git Commit 不是很规范,想到什么提交什么,团队中每一个人的提交方式都不一样,没有很特别的指定哪些 commit 是新功能,哪些是修复 bug,查看 commit 记录比较吃力前端

对版本的概念也不熟,使用 git tag 打版本以前,都须要先查一遍远程上的版本是多少,新增完本地 tag 以后再将 tag push 到远程仓库,这也只是完成了打版本的步骤,若是须要提供 CHANGELOG.md 文件来讲明每次版本的更新内容就比较麻烦vue

这时候就须要插件来帮咱们规范 git commit 提交、自动化发布版本,自动生成 CHANGELOGnode

本文篇幅较长,图片较多,提早预警!git

husky 钩子插件

使用 husky 来管理 git commit 以前的操做,为何要这么作,由于咱们能够在 git commit 以前再校验一次代码,防止提交「脏」代码,保证代码库中的代码是「干净」的,husky 不只仅能管理 commitgit 的钩子几乎都能管理,不过用的最多的仍是 commitpushgithub

  • 安装
npm install husky --save-dev
复制代码
  • 在 package 中配置
"husky": {
  "hooks": {
    "pre-commit": "npm run lint"
  }
}
复制代码

这里在 commit 以前,咱们先执行了 npm run lint,这是 vue-cli3 给咱们提供的命令,会根据咱们的 eslint 规则来校验代码,而且自动修复,记得先 git add 文件vue-cli

  • 使用

但这样会有一个问题,就是此次提交,我可能只修改了一个文件,好比我就修改了 a.js 的内容,但它依然会校验 src 下面全部的 .js 文件,很是的不友好。npm

致使的问题就是:每次提交代码,不管改动多少,都会检查整个项目下的文件,当项目大了以后,检查速度也会变得愈来愈json

lint-staged

解决上面的痛点就须要使用 lint-staged。它只会校验你提交或者说你修改的部分内容。gulp

npm install lint-staged -D -S

修改 package.json 配置:

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "src/*_/_.{js,vue}": ["npm run lint", "git add"]
  }
}
复制代码

如上配置,每次它只会在你本地 commit 以前,校验你提交的内容是否符合你本地配置eslint 规则,若是符合规则,则会提交成功。若是不符合它会自动执行 npm run lint 尝试帮你自动修复,若是修复成功则会帮你把修复好的代码提交,若是失败,则会提示你错误,让你修好这个错误以后才能容许你提交代码。

但这并非强制的,有些团队成员或者说刚来的新人没有在编辑器中配置或者无视命令行中提示的错误,强行提交,这时候就须要配置 pre-commit 这种强制性校验的东西,保证全部提交到远程仓库的内容都是符合团队规范的。

参考花裤衩大佬的文档 vue-element-admin

Commit 提交规范检查

在多人协做项目中,若是代码风格统1、代码提交信息的说明准确,在后期维护以及 Bug 处理时会更加方便。

Git 每次提交代码,都要写 Commit message(提交说明)

可是每一个人的提交方式不一样,没有很特别的指定哪些 commit 是新功能,哪些是修复 bug,这时须要插件来帮咱们规范化

规范 Commit message 的做用

  • 提供更多的历史信息,方便快速浏览
  • 过滤某些 commit(好比文档改动),便于快速查找信息
  • 直接从 commit 生成 CHANGELOG
  • 可读性好,清晰,没必要深刻看代码便可了解当前 commit 的做用。
  • 为 Code Reviewing(代码审查)作准备
  • 方便跟踪工程历史

在项目中安装插件:

npm i commitizen cz-conventional-changelog --save-dev
复制代码
  • 在 package 中配置
"config": {
  "commitizen": {
    "path": "cz-conventional-changelog"
  }
}
复制代码
  • 在 package 的 scripts 中配置命令
"commit": "git-cz",
复制代码
  • 使用

依赖安装完就能够开始秀操做

要先 git add . 将文件加入本地暂存区后,才能 commit

npm run commit
复制代码

注意,若是以前经过 git commit 这种方式提交代码,都要改成 git-cz

注意,若是以前经过 git commit 这种方式提交代码,都要改成 git-cz

注意,若是以前经过 git commit 这种方式提交代码,都要改成 git-cz

Commit message 格式说明

Commit message 通常包括三部分:HeaderBodyFooter

Header type(scope):subject type(必需)、scope(可选) 和 subject(必需)

这里有几种类型能够选择

type:用于说明 commit 的类别,规定为以下几种

feat:新功能
fix:修补 bug
docs:修改文档,好比 README, CHANGELOG, CONTRIBUTE 等等
style: 不改变代码逻辑 (仅仅修改了空格、格式缩进、逗号等等)
refactor:重构(既不修复错误也不添加功能)
perf: 优化相关,好比提高性能、体验
test:增长测试,包括单元测试、集成测试等
build: 构建系统或外部依赖项的更改
ci:自动化流程配置或脚本修改
chore: 非 src 和 test 的修改
revert: 恢复先前的提交
复制代码

scope:(可选)用于说明 commit 影响的范围

subject:commit 的简要说明,尽可能简短

Body

Body 部分是对本次 commit 的详细描述,能够分红多行

Footer

Footer 部分只用于两种状况。

  • 不兼容变更

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

  • 关闭 Issue

若是当前 commit 针对某个 issue,那么能够在 Footer 部分关闭这个 issue, 也能够一次关闭多个 issue

? Select the **type** of change that you're committing:
(type) 选择提交更改的类型
? What is the **scope** of this change (e.g. component or file name)? (press enter to skip)
(scope) 这次更改的范围是什么(组件或者文件名)
? Write a **short**, imperative tense description of the change:
(subject) 写一个简短的,命令式的变化描述
? Provide a **longer description** of the change: (press enter to skip)
(Body) 提供更改的长描述
? Are there any **breaking changes**?
(Footer) 有没有突破性的变化
? Does this change affect any open **issues**? (y/N)
(Footer) 这次更改是否有要关闭 issues
复制代码

若是当前 commit 针对某个 issues

? Does this change affect any open issues? (y/N)

选择 Y,输入 Closes #1 (表示关闭第 1 个 issues)

也能够一次关闭多个 issues : Closes #1 #2 #3

CHANGELOG 中 issues 默认的连接地址是根据 package.json 中的 repository 来生成的

若是 repository 没有,则会获取 git 中的远程仓库路径来做为前缀

以后就会进行代码格式化校验,若是代码不符合规范,一样会提交失败,必定要确保项目当前格式没问题,规范后再提交!!!

更多细节能够参考阮一峰老师的博客:Commit message 和 Change log 编写指南

自动发布版本

这里我使用 release-it 做为发布版本插件,也能够选择 standard-version

  • 安装插件
npm install --save-dev release-it
复制代码
  • 在 package 的 scripts 中配置命令
"release": "release-it"
复制代码

在项目终端输入 npm run release 就会执行操做

若是出现下图的报错信息,能够经过登陆 npm 解决

在发布版本前,必定确认是否还有文件没有提交,不然是会报错的,报错信息以下:

ERROR Working dir must be clean.
Please stage and commit your changes.
Alternatively, use `--no-git.requireCleanWorkingDir` to include the changes in the release commit (or save `"git.requireCleanWorkingDir": false` in the configuration).
复制代码

工做目录必须是干净的,请暂存 (add) 并提交 (commit) 你的更改

不推荐经过修改 git 配置来解决,由于发布一个版本就应该是没有任何更改的,稳定的才去发布,git tag 若是没有指定对应的 commitID,默认最新的 commit 上打标签,注意!!要在主分支(master)上发版本

release-it会读取本地package.json 中的 version,提示你当前版本是多少,不须要开发者使用 git tag -l 来查询当前本地版本是多少,以及这个版本作了哪些改动,它提供了几个默认选项让你选择版本号

若是你以为上面的选项不能知足你的要求,最后一个选项是本身填入版本信息(要符合规范),有关版本规范能够参考语义化版本 2.0.0

确认版本后,会自动修改 package.json 的版本信息,这是前端开发者查看当前项目版本的途径之一。以后就是询问是否要 commit,是否打 tag,是否 push 到远程仓库,能够一路回车。最后一项若是是公司项目,不须要上传到 npm 仓库,选 NO 便可,即便选了 Yes,但只要你在 package.json 中配置 privatetrue,也不会上传。如何查看发布成功? 能够输入 git tag -l 查看本地 tag,也能够 git ls-remote --tags origin 查看远程 tag

可是这只是一个 tag,没有详细信息,更新日志仍是须要开发者手动编写

这个时候就须要用到自动生成 CHANGELOG 插件了

自动生成 CHANGELOG

安装

npm i conventional-changelog-cli --save-dev
复制代码

配置 package.json

"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
复制代码

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

npm run changelog
复制代码

生成 CHANGELOG.md 文件

CHANGELOG.md 的头部加上自从上次发布版本以来的变更。显示 feat、bug、doc 等类型

生成的 CHANGELOG 不会按 commit 上传的时间顺序排序,有人给官方提交了 issues,等待官方解决。。👉传送门

能够打开 GitHub/GitLub 仓库,将更新日志生成的内容对应的填入 tag 版本中就能够了

深刻 conventional-changelog 源码

这里先展现最终生成的 CHANGELOG 效果图

起源背景以下:

测试:”须要在 CHANGELOG 中生成 commit 对应的提交人,这样问题定位就知道去找谁负责”

我:“😎 这个简单~配置下参数就能够了”

我觉得只要配置一下参数,毕竟 CHANGELOG 插件是支持自定义参数的,当我看到文档懵逼了,没有提供这两个参数,显示提交人和提交人邮箱,咋办,翻 issue。。。这个需求应该有人提 😏,根据 #351 知道了使用自定义配置须要从外部传入自定义配置文件

新建一个配置文件,在命令后面带上 -n <文件路径>

{
  "scripts": {
    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 -n ./changelog-option/index.js"
  }
}
复制代码

解决了使用自定义配置问题,如何增长 CHANGELOG 生成更多的提交信息,仍是翻 issue... #349

结合起来大概是这样,试试效果:

format 里面有 authorName 和 authorEmail 字段,运行 npm run changelog 后依旧没有效果 😖 只能翻源码了

VSCode 调试 node_modules 第三方插件

配置 launch.json,调试 conventional-changelog-cli 插件下的 cli.js 文件,这里保持和 package 中的 changelog 一致,传入参数 "-p", "angular", "-i", "CHANGELOG.md", "-s", "-r", "0", "-n", "./changelog-option/index.js"

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch Program",
      "cwd": "${workspaceFolder}",
      "program": "${workspaceFolder}\\node_modules\\conventional-changelog-cli\\cli.js",
      "args": [
        "-p",
        "angular",
        "-i",
        "CHANGELOG.md",
        "-s",
        "-r",
        "0",
        "-n",
        "./changelog-option/index.js"
      ]
    }
  ]
}
复制代码

全局搜索传入的配置项字段:gitRawCommitsOpts,打个断点调试一下

发现做者是将用户传入的 gitRawCommitsOpts 作个合并,最后传入到 conventionalChangelog 方法中去执行,继续查看该方法

插件由 conventional-changelog-cli 跳转到 conventional-changelog

判断用户是否有传入 preset 预设参数,调试的时候使用了 angular 的配置 (-p angular)

这里 return 了一个 conventionalChangelogCore 方法,依旧点击跳转

conventional-changelog-core

做为插件的核心库,最核心的地方就是 mergeConfig 这个方法

在 core 中将配置传入 mergeConfig 中进行合并

format 默认只有 hashgitTagscommitterDate,没有须要的 authorNameauthorEmail,fromTag 是咱们最后一次提交的 tag,merges: false 表示在 CHANGELOG 中不会生成 merges 分支的信息

mergeConfig 是一个 Promise,求值后将配置传给了 gitRawCommits,涉及了 pipe 导流来传递数据

gitRawCommitsOpts 配置传入 gitRawCommits 方法中,继续点进去看,又会跳转到 git-raw-commits 插件

git-raw-commits

经过 git-raw-commits 的 GitHub 官网 README,发现这个插件是从本地 git 仓库中获取提交记录,以前传入的 format 就是 git-log 中的参数

hash:哈希值

gitTags:标签

committerDate:提交时间

authorName:提交人

authorEmail:邮箱
复制代码

更多 git-log 细节能够去看 git 官方文档,也能够在 git log 后面带上咱们配置的 format 进行格式化

直接使用 git log 查看 git 提交记录中,也有 Author 的 name 和 email 字段

看到这里,肯定了 git-raw-commits 插件只是从 git 中读取数据并格式化,那么确定有个插件是将这些数据写成 CHANGELOG.md 文件,因而继续翻 conventional-changelog-core ,发现 conventional-changelog-writer,这个插件先不看,还记得以前使用的是 angular 的预设吗,能够去看 conventional-changelog-angular 插件

好的项目从文件名就知道各个文件是作什么的,将不一样插件的配置单独拆分,这点值得学习

conventional-changelog-angular

writer-opts.js 就是写入 CHANGELOG 的配置,最后会将配置传给 conventional-changelog-writer,这里来匹配咱们以前 commit message 的信息

其实在 writer 以前,还有一个 parser 插件,用来解析咱们提交的信息,这个 changelog 里面究竟是依赖了多少插件。。。

在用 commit 规范化插件提交的时候,就须要填写相应的信息,这里打印一下 commit 信息

发现的确有 authorNameauthorEmail 这两个字段存在,并且 conventional-changelog-writer这个插件就是将这样的对象生成 CHANGELOG.md,经过插件的 README 能够知道

如今字段也有了,可是为何不会显示呢,继续看源码,这里用到了 templates/commit.hbs 文件

看到这里也就差很少了,template 文件夹下就是生成 CHANGELOG 的模版文件,查了下 hbs(Handlebars) 是一个模版引擎。不过在 commit.hbs 中却并没有发现有用到这两个字段,想一想也是,这两个字段是我配置 gitRawCommitsOpts 传入的

说明 angular 预设一开始就没想过要生成 authorName 和 email。。。这就尴尬了,只能本身加上了,照葫芦画瓢,修改 commit.hbs 文件,在生成 commit 的 hash 值后面加上

成功啦~~!!!🎉🎉🎉

让我激动一下,可是转念一想 🤔,这种直接修改 node_modules 里插件的源码,根本无法分享啊,若是同事要用,不可能也这样去修改 conventional-changelog-angular 的源码吧,并且只要从新 npm install 安装依赖后,以前修改了也会消失

做者已经在 conventional-changelog-cli 中提供了自定义的例子

不过这个地址已经被废弃了,新地址就是咱们以前使用的 angular 预设 conventional-changelog-angular ,官方也有提供其余预设模版,如:atomeslintjQuery

这种在一个 package 中管理多个项目是用了 lerna,一个用于管理拥有多个包的 JavaScript 项目的工具。是一种比较流行的 monorepo 项目管理模式,React、Vue、Babel 都有用这种模式来管理。

前面也分析了 conventional-changelog-angular ,直接 clone 源码来改,建立 git-raw-commit.js

module.exports = {
  format:
    '%B%n-hash-%n%H%n-gitTags-%n%d%n-committerDate-%n%ci%n-authorName-%n%an%n-authorEmail-%n%ae'
}
复制代码

在 index.js 中传入并使用

'use strict'
const Q = require(`q`)
const conventionalChangelog = require(`./conventional-changelog`)
const parserOpts = require(`./parser-opts`)
const recommendedBumpOpts = require(`./conventional-recommended-bump`)
const writerOpts = require(`./writer-opts`)
// 格式化 git log 信息
const gitRawCommitsOpts = require('./git-raw-commit')

module.exports = Q.all([
  conventionalChangelog,
  parserOpts,
  recommendedBumpOpts,
  writerOpts,
  gitRawCommitsOpts
]).spread(
  (
    conventionalChangelog,
    parserOpts,
    recommendedBumpOpts,
    writerOpts,
    gitRawCommitsOpts
  ) => {
    return {
      conventionalChangelog,
      parserOpts,
      recommendedBumpOpts,
      writerOpts,
      gitRawCommitsOpts // 传入
    }
  }
)
复制代码

以后修改 commit.hbs,显示 authorName 和 authorEmail

这里有个小坑!!我用 VSCode 修改完直接保存后,若是有屡次提交,不会折行,会挤在同一行,估计是保存的格式不对,也许是我 VSCode 安装了比较多的格式化扩展,建议用 Notepad++ 去修改 commit.hbs 文件

替换 issues 路径

公司使用 redmine 来管理项目,测试人员会在 redmine 中提 issues,这里生成完 CHANGELOG 要批量替换 issues 的地址,将 GitLab 地址前缀替换成 redmine,网上说用 replace 库,可我发现这个库上传 npm 是三年前,而且已经不维护了,使用后也报错。。。因而写了一个简单的字符串替换文件,生成完 CHANGELOG 后运行该文件便可替换,支持传递参数(参考 conventional-changelog 源码,使用了 minimist 插件来获取传递的参数)

{
  "scripts": {
    "changeissueurl": "node ./changelog-option/replace.js https://gitlba.com/issues/ https://redmine.example.com/issues"
  }
}
复制代码

再把这两个脚本集成到一个 version 中,以后要发版本,生成 CHANGELOG 只要运行 npm run version 便可

发布到 NPM 仓库

这样仍是太麻烦,对于使用者来讲不须要了解太多,并且文件存在本地,不方便迁移,🤔 能不能像 angular 同样直接作成预设模版,让 conventional-changelog 直接去用咱们自定义的预设模版就好了,为了验证,仍是得翻源码,以前有调试到,可是没细看

conventional-changelog-preset-loader

预设只要按照 ${scope}conventional-changelog-${name} 这种命名规范就能够被 require,scope 是由于 npm 仓库不能同名,能够加上本身的用户名做为做用域,例如:@zsh/conventional-changelog-angular,以后在配置中改成 -p @zsh/angular 就可使用自定义的预设了

感谢 conventional-changelog !很少说,起一个 npm 项目,将以前的文件都放进去,再次对模版进行优化。

TODO:

  • authorName 和 authorEmail 不必定是必需的,可配置
  • issues 替换地址更为简便
  • 给 Title 新增 emojis 🚀

由于这里获取的 commit 是字符串格式,能够在 commit.hbs 模版中设置一个值,以后再根据用户的配置来进行替换,这样 authorName 和 authorEmail 就不是必需的,默认禁用,须要手动设置开启

issues 和 emojis 比较简单就不说了,相信你们若是认真的看到这里,彻底能够作你本身的预设~

因为是第一次发布到 npm 上,没什么经验,效果很差还望见谅 🏃‍♂️,npm 地址:conventional-changelog-custom-config

使用

npm install conventional-changelog-custom-config --save-dev
复制代码

经过在 package.json 中配置参数的形式来定制 CHANGELOG,不填配置则会按照 angular 的预设模版生成 CHANGELOG,具体配置以下:

{
  "scripts": {
    "changelog": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0"
  },
  "changelog": {
    "bugsUrl": "https://redmine.example.com/issues/",
    "emojis": true,
    "authorName": true,
    "authorEmail": true
  }
}
复制代码

bugsUrl

Type: string Default: false

当你须要将 issues URL 替换成其余 URL 时,使用该参数,例如使用 redmine 管理项目, bugsUrl: 'https://redmine.example.com/issues/'

若是不填 bugsUrl 则会根据 package.json 中的 repositoryrepository.url 来做为 issues URL

{
  "repository": {
    "type": "git",
    "url": "https://github.com/example"
  }
}
复制代码

若是 repository.url 也没有,则会获取 git 中的远程仓库路径来做为前缀,conventional-changelog-core 源码地址

若是你使用了第三方的协做系统(例如 bitbucket), 推荐你使用这个插件 conventional-changelog-angular-bitbucket

emojis

Type: boolean Default: false,emojis types 参考 gitmoji

Commit Type Title Description Emojis
feat Features A new feature
fix Bug Fixes A bug Fix 🐛
docs Documentation Documentation only changes 📝
style Styles Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 💄
refactor Code Refactoring A code change that neither fixes a bug nor adds a feature ♻️
perf Performance Improvements A code change that improves performance ⚡️
test Tests Adding missing tests or correcting existing tests
build Build Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm) 👷
ci Continuous Integrations Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs) 🔧
chore Chores Other changes that don't modify src or test files 🎫
revert Reverts Reverts a previous commit

authorName

Type: boolean Default: false

在 CHANGELOG 中生成用户名

authorEmail

Type: boolean Default: false

在 CHANGELOG 中生成邮箱

更多节能够看 README,码字不易,开源不易,以为不错点给个 ⭐️ 吧~😝 感谢感谢~

总结

总结这一整套折腾过程,代码风格规范 ----> commit 规范 ----> version 规范 ----> 生成 CHANGELOG ---> 自定义 CHANGELOG ---> NPM 发布预设

前四个步骤看看 README 就直接上手,没什么难点,自定义 CHANGELOG 稍微麻烦些,对外开放 conventional-changelog-cli 收集用户配置,如 -p angular -s -r 0 -n config.js,若是有预设模版,好比 angular,就将 conventional-changelog-angular 中的配置传入 conventional-changelog-core 中 merge,以后经过 git-raw-commits 从本地 git 中获取 log 数据,conventional-changelog-parser 来解析用户提交的信息,将两种数据整合成一个对象,传入 conventional-changelog-writer 生成 CHANGELOG.md 文件

刚开始安装插件比较多,一旦配完就是一两个命令的事,感兴趣的也参照我这篇文章,本身写一套模版用, npm 上也有不少预设模版能够用,axetroy 大佬写了个 VSCode 扩展 列入了 conventional-changelog 官方推荐 ,文章位置,人也在掘金 😏

本文篇幅较长,不免会有错误和不足的地方,但愿大佬们留言指正,以避免误人

参考文档

git commit 、CHANGELOG 和版本发布的标准自动化

Commit message 和 Change log 编写指南

conventional-changelog-core

git-log

package.json

vue-element-admin

VSCode Debugging

相关文章
相关标签/搜索