gulp + conventional-changelog-core 自动生成组件库 CHANGELOG

57 阅读2分钟

本文基于 gulp 的自动化构建能力,结合 conventional-changelog-core 的规范日志生成特性,搭建一套组件库发版时 CHANGELOG 自动更新的方案。该方案可实现版本发布流程与日志更新的无缝联动,既保证了 CHANGELOG 的规范性与完整性,又大幅简化发版操作流程,有助于组件库的高效、标准化迭代。

前提条件

该方案基于规范的 git 提交消息格式:

<type>[optional scope]: <subject> 

[optional body] 

[optional footer(s)]

如何配置规范的 commitlint 可以参考这篇文章 配置Commitlint与Husky实现Git提交消息规范化-开发者社区-阿里云

安装 gulp 和 conventional-changelog-core

考虑到 node 12 版本兼容性(主要是项目中有些依赖实在不兼容高版本 node),我这里选择了 gulp@^4.0.2conventional-changelog-core@^4.2.1

npm i gulp@^4.0.2 conventional-changelog-core@^4.2.1

配置 .conventional-changelog.js

这里提供一份我项目中使用的配置供参考,详细配置可以自行查看 官方文档

// .conventional-changelog.js

const moment = require("moment")

// 配置模板,可以按需调整模板

// https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-writer#maintemplate
const mainTemplate = `
{{> header}}

{{#each commitGroups}}
{{#each commits}}
{{> commit root=@root}}
{{/each}}
{{/each}}

{{> footer}}
`

// https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-writer#headerpartial
const headerPartial = `
## {{#if isPatch~}} <small>
  {{~/if~}} {{version}}
  {{~#if title}} - {{title}}
  {{~/if~}}
  {{~#if date}} ({{date}})
  {{~/if~}}
  {{~#if isPatch~}} </small>
  {{~/if}}
`

// 最多显示的发布版本个数
const maxReleaseCount = 15
let releaseCount = 0

module.exports = {
  // options
  // https://www.npmjs.com/package/conventional-changelog-core/v/4.2.1?activeTab=readme
  preset: "angular",
  outputUnreleased: false,
  title: 'CHANGELOG',
  config: {
    gitRawCommitsOpts: {
      // 配置起始提交日志的 commit sha
      from: "xxx",
    },
    parserOpts: {
      // https://github.com/conventional-changelog-archived-repos/conventional-commits-parser
      mergePattern: /^Merge branch '(.*)' into (.*)$/,
      mergeCorrespondence: ["id", "source"],
    },
    writerOpts: {
      // https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-writer#api
      groupBy: "type",
      commitGroupsSort: ["feat", "fix", "perf", "docs", "style", "refactor", "test", "chore", "revert"],
      commitsSort: ["scope", "subject"],
      noteGroupsSort: "title",
      notesSort: "title",
      mainTemplate,
      headerPartial
    },
  },
  transform: function (commit, cb) {
    // 配置发版消息的匹配格式并提取版本号,这里可以根据项目实际情况进行调整
    const subjectReleasePattern = /elements@(\d+\.\d+\.\d+)/
    const headerReleasePattern = /Update version (\d+\.\d+\.\d+)/
    if (commit.subject && subjectReleasePattern.test(commit.subject)) {
      commit.version = subjectReleasePattern.exec(commit.subject)[1]
    } else if (commit.header && headerReleasePattern.test(commit.header)) {
      commit.version = headerReleasePattern.exec(commit.header)[1]
    } else if (/^(chore|docs|test|doc)/.test(commit.header) && !/^chore\(element-ui\)/.test(commit.header)) {
      // 忽略部分提交日志信息,这里可以根据项目实际情况进行调整
      cb(null, null)
      return
    }
    if (commit.version) {
      releaseCount++
    }
    if (releaseCount > maxReleaseCount) {
      cb(null, null)
      return
    } else if (releaseCount === 1) {
      commit.title = 'latest'
    }
    
    // 设置日期格式
    if (commit.committerDate) {
      commit.date = moment(new Date(commit.committerDate)).format("YYYY-MM-DD")
    }
    // 设置更新日志的文本模板,主要是提取信息并添加 markdown 格式,这里也可以按需加入一些 emoji 表情丰富日志
    let header = ''
    if (commit.type) {
      header += `**${commit.type}`
    }
    if (commit.scope) {
      header += `(${commit.scope})`
    }
    if (header.length) {
      header += `:** ${commit.subject.replace('@', '-')}`
      commit.header = header
    }
    cb(null, commit)
  },
}

配置 gulp

在 gulp 入口文件 gulpfile.js 中添加一个 task,代码参考如下

const fs = require("fs")
var conventionalChangelog = require("conventional-changelog-core")
const conventionalChangelogOpts = require("./.conventional-changelog")

gulp.task("conventional-changelog", function () {
  // CHANGE_LOG 文件的路径
  const changelogPath = "./CHANGE_LOG.md"
  return conventionalChangelog(conventionalChangelogOpts)
    .on("end", () => {
      // 生成文件完成后,读取文件内容
      fs.readFile(changelogPath, "utf8", (err, data) => {
        if (err) {
          return console.error(err)
        }
        // 在内容首行添加标题
        const newData = `# ${conventionalChangelogOpts.title || 'CHANGELOG'}\n\n` + data
        // 将修改后的内容写回文件
        fs.writeFile(changelogPath, newData, "utf8", err => {
          if (err) {
            return console.error(err)
          }
        })
      })
    })
    .pipe(fs.createWriteStream(changelogPath)) // or any writable stream
})

执行任务脚本,生成 CHANGELOG

gulp 任务创建完成后,可以在命令终端执行 npx gulp conventional-changelog 自动生成 CHANGELOG,更新后的文档内容如下:

image.png

进阶:创建自动发布脚本实现 打包-发布-更新文档 全自动化执行

在实际的组件库发版实践中,打包发布还需要同步更新子版本号,这里可以使用 npm --no-git-tag-version version patch 命令来实现子版本号自动更新。同时更新日志一般都会跟随版本发布一起更新到组件文档上,那么就可以将 打包-发布-更新文档 这些环节一起写到一个执行脚本中,组件发布时直接执行脚本即可全自动完成所有流程,具体实现如下:

1. 创建 node 脚本 scripts/release.js

const { execSync } = require('child_process');
const path = require('path');

// 执行命令的函数
function runCommand(command) {
  console.log(`执行命令: ${command}`);
  try {
    execSync(command, { stdio: 'inherit', shell: true });
    console.log(`命令执行成功: ${command}`);
  } catch (error) {
    console.error(`命令执行失败: ${command}`);
    console.error(error);
    process.exit(1);
  }
}

console.log('开始执行发布流程...');

console.log('\n===== 开始执行编译 =====');
runCommand('git fetch && git pull origin master && npm run lib');

console.log('\n===== 发布到 npm =====');
runCommand('npm --no-git-tag-version version patch && npm publish');

const { version } = require(path.join(__dirname, '../package.json'));

console.log('\n===== 提交版本号 =====');
runCommand(`git add package.json package-lock.json && git commit -m "chore: elementsc@${version}"`);

console.log('\n===== 提交更新日志 =====');
runCommand('gulp conventional-changelog && git add "./CHANGE_LOG.md" && git commit -m "docs: update changelog"');

console.log('\n发布流程执行完成!');

2. 在 package.json 中添加执行脚本

{
    "name": "elements",
    "version": "1.0.0",
    "scripts": {
        "publish:patch": "node scripts/release.js"
    }
}

3. 执行 npm run publish:patch 发布版本

脚本执行成功后,自动提交版本发布日志和更新CHANGELOG,到此完成 打包-发布-更新文档 全自动化执行

image.png