likes
comments
collection
share

提效:使用release-it自动管理版本号和生成CHANGELOG

作者站长头像
站长
· 阅读数 43

本文参加了由公众号@若川视野 发起的每周源码共读活动 点击了解详情一起参与。这是源码共读的第39期

没有release-it, 则代码(特别是npm包)改动后的版本号,changelog等需要手动管理。手动管理版本号有什么问题呢?第一,工作量的问题;第二,版本号定义不合理。再说说changelog,规范的changelog文档能够让开发者和使用者清晰地看到每一版本代码都发生了哪些变化(见下图),而手动维护无疑是增加人工的工作量,能够根据commit message自动生成的话,那必是科技和狠活儿啊。今天我们就简单地使用一下release-it并探究其原理。

提效:使用release-it自动管理版本号和生成CHANGELOG

图1:release-it的changelog截图

1.准备工作

1.1 阅读参考文章

建议先阅读参考文章: 还在用开发者工具上传小程序? 快来试试 miniprogram-ci 提效摸鱼 。(重点阅读第7小节)

1.2 代码环境准备

(1)首先clone一下release-itcreate-release-it的代码

(2) 在github上新建一个仓库用于自己学习做实验

(3) 把自己的仓库clone到本地,进行如下操作:

  • 执行 npm init
  • 执行 npm init release-it (稍后揭秘其原理),成功后会自动在npm scripts中增加 "release": "release-it"

提效:使用release-it自动管理版本号和生成CHANGELOG

.release-it.json中增加如下内容:

{
  "github": {
    "release": true
  },
  "git": {
    "commitMessage": "release: v${version}"
  },
  "npm": {
    "publish": false
  },
  "hooks": {
    "after:bump": "echo 更新版本成功"
  },
  "plugins": {
    "@release-it/conventional-changelog": {
      "preset": "angular",
      "infile": "CHANGELOG.md"
    }
  }
}
  • 执行 npm install git-cz -D ,并修改npm scripts 增加 "commit": "git-cz"
  • 执行 npm install @release-it/conventional-changelog -D
  • 项目根目录下新增 CHANGELOG.md 文件
  • 写一些业务代码...

1.3 运行release命令

注意:当网络不好时,运行npm run release命令会失败,如下图所示:

提效:使用release-it自动管理版本号和生成CHANGELOG

下图展示成功运行的情况:

提效:使用release-it自动管理版本号和生成CHANGELOG

上图中Changeset列出了运行 npm run release 命令后修改的文件有CHANGELOG.md,package.json, package-lock.json。我们验证一下:

CHANGELOG.md修改了:

提效:使用release-it自动管理版本号和生成CHANGELOG

package.json修改了:

提效:使用release-it自动管理版本号和生成CHANGELOG

接下来release-it会询问我们是否提交代码、打tag、推送代码、在github创建release等。

提效:使用release-it自动管理版本号和生成CHANGELOG

值得我们注意的是release-it的工作到此就完成了,真正要把包发布到npm还是要运行npm publish命令。如下图所示:

提效:使用release-it自动管理版本号和生成CHANGELOG

关于如何发布自己的npm包可以参考文章:图文结合简单易学的npm 包的发布流程,下面我们就来探究一下release-it的相关原理和源码。

2.原理与源码

2.1npm init release-it的原理

npm init的原理 :

npm init <package-spec> (same as `npx <package-spec>)
npm init <@scope> (same as `npx <@scope>/create`)

aliases: create, innit

运行npm init release-it相当于npx create-release-it,而npx的作用是从本地或远程 npm 包运行命令,所以npx create-release-it 作用是运行create-release-it这个包的命令,而package.json文件中bin 字段是命令名到本地文件名的映射。我们看一下create-release-it源码中的package.json文件:

提效:使用release-it自动管理版本号和生成CHANGELOG

如上图所示,执行npx create-release-it的本质其实是执行create-release-it下的index.js文件。

下面我看一下index.js文件中都做了什么事情。

2.2 create-release-it的核心逻辑

提效:使用release-it自动管理版本号和生成CHANGELOG

从整体上看index.js中定义了一个异步的IIFE,在这个函数中会询问开发者问题,根据开发者的选择做一些事情。比如安装release-it、修改npm scripts、增加.release-it.json文件或者为package.json增加release-it选项。我们详细看一下这些都是如何实现的(注意:为了按功能划分以下的代码分析打乱原有的代码顺序):

2.2.1 修改npm scripts

const PACKAGE_CONFIG = 'package.json';

let manifest = {};
let hasManifest = false;
let isManifestChanged = false;

try {
  manifest = JSON.parse(await readFile(PACKAGE_CONFIG));
  hasManifest = true;
} catch (err) {}


if (hasManifest) {
  manifest.scripts = manifest.scripts || {};
  if (!('release' in manifest.scripts)) {
    manifest.scripts.release = 'release-it';
    isManifestChanged = true;
  }
}

if (isManifestChanged) {
  await writeFile(PACKAGE_CONFIG, JSON.stringify(manifest, null, '  ') + EOL);
}

以上代码的主要逻辑就是(1)读取package.json文件,转对象;(2)在scripts中增加release命令,内容为release-it ;(3) 将改变后的内容写回package.json文件。

2.2.2 创建.release-it.json文件或者为package.json增加release-it选项

const RELEASE_IT_CONFIG = '.release-it.json';

if (hasManifest) {
  questions.push({
    type: 'select',
    name: 'config',
    message: 'Where to add the release-it config?',
    choices: [
      { title: '.release-it.json', value: RELEASE_IT_CONFIG },
      { title: 'package.json', value: PACKAGE_CONFIG }
    ],
    initial: 0,
    hint: ' '
  });
}

if (isConfigChanged && (!answers.config || answers.config === RELEASE_IT_CONFIG)) {
  await writeFile(RELEASE_IT_CONFIG, JSON.stringify(config, null, '  ') + EOL);
}

if (answers.config === PACKAGE_CONFIG) {
  manifest['release-it'] = config;
  isManifestChanged = true;
}
if (isManifestChanged) {
  await writeFile(PACKAGE_CONFIG, JSON.stringify(manifest, null, '  ') + EOL);
}

根据用户的交互回答,决定是创建.release-it.json文件或者为package.json增加release-it选项。交互问题中设置了两个选项,如果用户选择的是.release-it.json则新建一个.release-it.json文件,并将配置内容写入;如果用户选择的是package.json则在package.json中增加release-it选项,然后写回package.json文件。

2.2.3 为项目安装release-it

import { execa } from 'execa';

await execa('npm', ['install', 'release-it', '--save-dev'], { stdio: 'inherit' });

主要是靠execa执行npm install命令来安装的release-it的。

以上就是create-release-it做的工作,相当于前期的准备工作,下面我们简单研究一下执行 npm run release 时都做了什么以及如何实现的。

2.3 npm run release的原理

如上所述,当我们的项目集成了release-it之后,则package.json中的scripts增加了release命令:

提效:使用release-it自动管理版本号和生成CHANGELOG

当执行npm run release时就是在执行 npx release-it命令 , 也就是执行release-it这个npm包的命令。release-it的package.json文件如下:

提效:使用release-it自动管理版本号和生成CHANGELOG

通过其bin选项可知执行的是bin目录下的release-it.js文件:

提效:使用release-it自动管理版本号和生成CHANGELOG

release-it.js文件中主要执行lib下cli.js导出的方法,cli.js文件如下:

提效:使用release-it自动管理版本号和生成CHANGELOG

通过cli.js文件我们知道当我们执行relase-it命令并且不带version、help参数的时候则执行的是index.js中定义的runTasks方法。

index.js文件中runTasks有几行关键代码如下所示:

const name = await reduceUntil(plugins, plugin => plugin.getName());
const latestVersion = (await reduceUntil(plugins, plugin => plugin.getLatestVersion())) || '0.0.0';
const changelog = await reduceUntil(plugins, plugin => plugin.getChangelog(latestVersion));

这几行代码和项目名称、版本以及changelog有关,更具体的分析较为复杂,本文不做深入研究,感兴趣的同学可以自己深入研究学习。

3.收获与总结

通过参与本次源码共读活动,有如下收获:

(1)了解到npm init的原理

(2)学会使用release-it管理版本号和生成changelog,并了解其原理

(3)参照create-release-it可以自己开发cli工具