当前位置:   article > 正文

github release_轮子改造:github-release-notes

gitee release node

8320f2c1aaa36f75a7f0c7cd7ac3eb82.png

背景

github-release-notes,以下简称 gren ,是用来一键向 github 发布 release notes 的工具。基本步骤如下:

  1. 拉取 github 仓库的 tags 记录
  2. 拉取最新两个 tag 之间的 pr(或 issue)记录,总结成 release notes
  3. 将 release notes 推送到 github 仓库,作为最新 tag 的发布日志

ff3985e1ebf703c7e519d8e39f9f32b8.png

就是这么简单。不管你是想要:

  • 一次生成所有 tag 的发布日志
  • 定制化日志来源。选择是从 pr 还是 issue 获取;根据其中的 label 生成特定格式的文案
  • 其他功能

这一切都可以一条命令来实现。是不是很棒呢?

自动化发布流程

这里顺带提一下我们组件的自动化发布流程,看看 gren 在其中扮演的角色吧~

  1. 经过测试和实际项目检验的 dev 代码,合并到 master 啦
  2. 触发 travis-ci,做两件事
  3. 发版
  4. yarn build,生成可发布的代码
  5. 部署到 github page
  6. 发布到 npm
  7. 发更新日志
  8. 用 standard-version 根据 semver 和最新的 commit-msg 来更新 package 版本并打 tag
  9. 推送新 commit 和 tag 到 github 仓库
  10. 用 github-release-notes 生成 release-log
  11. 调用钉钉服务发群通知

遇到的问题

前面说了,github-release-notes 发布日志的格式是高度可配置的。那么具体配置来源于以下这样的配置文件。

  1. // .grenrc.js
  2. module.exports = {
  3. dataSource: 'prs',
  4. groupBy: {
  5. '✨ New Features:': ['enhancement'],
  6. ' Bug Fixes:': ['bug'],
  7. ' Documentation:': ['documentation'],
  8. ' Refactors:': ['refactor'],
  9. '♻️ Tests:': ['test'],
  10. ' Performance:': ['performance'],
  11. '⚓ Dependency upgrades:': ['dependencies'],
  12. ' Chore:': ['chore'],
  13. ' Style:': ['style'],
  14. ' Hack': ['hack'],
  15. ' Breaking Changes': ['breaking-change']
  16. }
  17. }

而这个配置是放置在每个项目之内的。假设我们需要更新这份配置,比如更换 dataSource,或新增了一个 label;组件维护者得一个一个地去组件提 pr。这样既容易遗漏,也很麻烦。

思考

prettier 是允许用户将配置放在 npm 模块里,然后在 package.json 里引入

  1. // package.json
  2. {
  3. "devDependencies": {
  4. "@company/prettier-config": "1.0.0"
  5. },
  6. "prettier": "@company/prettier-config"
  7. }

这是个好主意:可以统一管理配置文件了;但还有个问题:每次更新并发布配置后依然需要手动更新每个组件。

因此,我们的方案需要解决两个问题:

  1. 所有组件共享一份 gren 配置
  2. 更新 gren 配置时不需要再去更新每个组件

实践

首先,我们需要支持远程配置。

  1. // package.json
  2. {
  3. "gren": "https://raw.githubusercontent.com/FEMessage/.github/master/.grenrc.js"
  4. }

github-release-notes 先判断 package.json 是否有 gren 这个字段;有则优先去请求网络资源。这里我们先在团队 github 中管理资源。

但 github 不是用来做 cdn 的。这样的资源请求会极容易被拒绝(加 token也不行)。听说 jsdelivr 能缓存 github 资源,那么:

  1. // package.json
  2. {
  3. "gren": "https://cdn.jsdelivr.net/gh/FEMessage/.github/master/.grenrc.js"
  4. }

问题出现在了缓存上:我们希望每次更新配置就能立即被使用。所以最后还是用发 npm 包的形式来管理。

  1. // package.json
  2. {
  3. "gren": "@femessage/grenrc"
  4. }

步骤如下:

  1. 从 package.json 的 gren 字段拿到配置包名 packageName
  2. 通过请求 https://registry.npmjs.org/-/package/@femessage/grenrc/dist-tags 拿到最新版本 version
  3. 组合后请求 https://cdn.jsdelivr.net/npm/${packageName}@${version}/index.js 拿到最新的配置

结果

最终我们一劳永逸地实现了:

  1. 统一管理配置
  2. 更新配置组件不用发版

这两个需求。另外,github-release-notes 官方仓库也已接受了 pr。

插曲——如何在 node 中同步执行异步操作

一般的异步操作可以通过 async/await 来写“同步”形式的代码;但 github-release-notes 获取配置的过程是放在 class constructor 函数中的,不能简单地用 async/await 重构。那该怎么办?

我们参考了 request-from-url 库(像 require 本地仓库一样 require 远程仓库)的实现方案,使用 child_process 库,将网络请求放在子进程中进行来阻塞主进程,从而使得主进程的代码可以以同步执行的方式书写。

  1. // 子进程获取 npm 包版本
  2. const packageName = process.argv[2];
  3. const url = `https://registry.npmjs.org/-/package/${packageName}/dist-tags`;
  4. require('https').get(url, res => {
  5. const buffers = [];
  6. res.on('data', (data) => {
  7. buffers.push(data);
  8. }).on('end', () => {
  9. const { latest } = JSON.parse(Buffer.concat(buffers).toString());
  10. const result = [null, latest];
  11. process.stdout.write(JSON.stringify(result));
  12. });
  13. });
  14. // 主进程同步执行代码
  15. const { spawnSync } = require('child_process');
  16. const { stdout } = spawnSync('node', ['get-package-latest-version.js', gren], {
  17. cwd: __dirname
  18. });
  19. const [errMsg, version] = JSON.parse(stdout.toString());

善后

用脚本批量更新组件 & 提交 pr

  1. const components = [
  2. 'v-img',
  3. 'vue-sfc-cli',
  4. 'el-data-table',
  5. 'el-form-renderer',
  6. 'upload-to-ali',
  7. 'el-select-area',
  8. 'data-list',
  9. 'v-editor',
  10. 'el-semver-input',
  11. 'el-number-range',
  12. 'el-data-tree',
  13. 'excel-it',
  14. 'log-viewer',
  15. 'count-down',
  16. 'img-preview',
  17. 'create-nuxt-app',
  18. 'element',
  19. 'vant',
  20. 'direct-mail'
  21. ]
  22. function exec(cwd, wholeCommand) {
  23. if (Array.isArray(wholeCommand)) {
  24. wholeCommand.forEach(c => exec(cwd, c))
  25. return
  26. }
  27. const [command, ...args] = wholeCommand.split(' ').map(arg => arg.replace(/%s/g, ' '))
  28. const {stdout, stderr} = require('child_process').spawnSync(command, args, {cwd})
  29. const errMsg = stderr.toString()
  30. if (errMsg) {
  31. console.error(`stderr:n${errMsg}`)
  32. // process.exit(1) // 许多用 stderr 来传递普通消息的……
  33. }
  34. }
  35. function addGrenToPackageJson(cwd) {
  36. const packageJsonPath = `${cwd}/package.json`
  37. const packageJson = require(packageJsonPath)
  38. packageJson.gren = '@femessage/grenrc'
  39. require('fs').writeFileSync(
  40. packageJsonPath,
  41. JSON.stringify(packageJson, null, 2) + 'n'
  42. )
  43. }
  44. function work() {
  45. components.forEach(c => {
  46. console.log('component:', c)
  47. const cwd = `/Users/donald-work/projects/${c}`
  48. const branch = 'chore-gren'
  49. exec(cwd, [
  50. 'git checkout dev',
  51. 'git pull upstream dev',
  52. `git checkout -b ${branch}`,
  53. 'rm .grenrc.js',
  54. 'yarn remove github-release-notes',
  55. 'yarn add -D @femessage/github-release-notes',
  56. ])
  57. addGrenToPackageJson(cwd)
  58. const commitMsg = 'chore(deps):%supgrade%sgren'
  59. exec(cwd, [
  60. `git commit -am ${commitMsg}`,
  61. `git push --set-upstream origin ${branch}`,
  62. `hub pull-request -b femessage:dev -m ${commitMsg}`,
  63. ])
  64. })
  65. }
  66. work()

补充:为组件提交 pr 的流程

1、从 femessage/{component} 组件仓库 fork 一份到自己的仓库

2、从自己仓库拉代码到本地,基于 dev 分支切功能分支或 hotfix 分支,修改代码

3、push 分支,向 femessage 仓库提交 pr

4、写好 pr 的 title 和 content

  • title 格式与 commit 规范相同:feat: 某某功能。这样机器人 auto-add-label 就会自动打 label。不同 label 的 pr 会被 gren 归类,生成 release-log

a4f86385bee7886750a666d9757da099.png
  • content 可以参考模板填写内容
  1. why,需求场景是什么,或解决了什么 issue
  2. how,如果实现方式比较有技巧,那可以简单讲解一下,方便 maintainer 早点合 pr
  3. test,这个其实也很简单。你本地调试完肯定会跑跑看。
  4. 如果是新功能,截下新功能的图
  5. 如果是修复 bug,则需要截下修复前和修复后的对比图
  6. docs,如果是补充文档内容的 pr,则在这里文字或截图说明一下

02735740af35993237676415c94736a5.png

5、等待被添加为 contributors

f7811b1b8ad245bb6d6451a7c747a83a.png

鸣谢

  • Han,完成了初期迭代版本,找到了同步请求资源的方案
  • EVILLT,提供了批量更新组件依赖 & 提 pr 的脚本方案
  • levy,推动了整个方案的进行

参考

  • github-release-notes 官方概念详解
  • 揭秘vue-sfc-cli: 组件研发利器
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/888836
推荐阅读
相关标签
  

闽ICP备14008679号