如何指定 semantic release 的下一个版本

semantic release 用的好好的,突然间我们要修改版本号策略了,如何指定不同的版本号呢?

有这样一种情况,我们有一个包,是独立发版的,于是愉快的使用了 semantic-release 由于发版十分频繁,版本号已经到了 5.x。

这时突然我们发现自己的包跟其他包还需要有对应关系,而他们的包才发到了 3.x。 现在想要让我们的包也从 3.x 重新发起,遇到已经发过的,就跳过,该怎么办呢?

官方是否支持?

手动发版时,我们可以自由指定下一个版本,因此只需要指定 3.x 版本号发布即可。但使用了 semantic-release,它会自动帮我们计算下一个版本,查阅官方文档,发现明确写明了不支持指定版本号:

https://semantic-release.gitbook.io/semantic-release/support/faq#can-i-manually-trigger-the-release-of-a-specific-version

01

看来官方是不建议我们这么做了,说的也有道理,这么做确实不符合我们使用 semantic release 的初衷。但凡事总有特殊情况,只能靠我们自己找 trick 方法了。

semantic release 是如何计算下一个版本的

获取当前最新的版本号

查看 semantic-release 源码,会发现版本的计算分为两个流程。首先,拉取远程所有分支的所有 tag,并选出当前分支;

1
2
context.branches = await getBranches(options.repositoryUrl, ciBranch, context);
context.branch = context.branches.find(({name}) => name === ciBranch);

之后,对所有 tag 使用 semver 进行排序,选出最大的版本号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = ({branch, options: {tagFormat}}, {before} = {}) => {
const [{version, gitTag, channels} = {}] = branch.tags
.filter(
(tag) =>
((branch.type === 'prerelease' && tag.channels.some((channel) => isSameChannel(branch.channel, channel))) ||
!semver.prerelease(tag.version)) &&
(isUndefined(before) || semver.lt(tag.version, before))
)
.sort((a, b) => semver.rcompare(a.version, b.version));

if (gitTag) {
return {version, gitTag, channels, gitHead: gitTag, name: makeTag(tagFormat, version)};
}

return {};
};

这里有两个要点:

  1. tag 是从远程代码库拉取的,本地修改 tag 如果不推送到远程是无效的。
  2. 选的永远是最大的版本号,不是时间顺序上最近的版本号。

解析 commit 信息

之后,semantic release 会逐条解析上个版本至今的所有 commit message,在最新版本号的基础上,根据规则判断下一个版本号。

通过修改 tag 来改变 semantic-release 的下一个版本号

知道了 semantic-release 计算下一个版本号的原理,我们就可以通过修改 tag 的方式来变相控制 semantic release 生成的下一个版本号。

  • 如果我们想要发布的版本号比最新的版本号大,例如目前版本是 5.1.0,我们想要跳过 5.2.0 直接发布 5.3.0,那么可以手动生成一个名称为 v5.2.0 的 tag:
1
git tag v5.2.0 v5.3.0
  • 如果我们想要从一个低版本直接开始发起,则可以通过修改 tag 格式的方式来实现。可以看到 semantic release 默认的 tag 格式为 v${version},我们可以通过 semantic release 的 tagFormat 配置项来指定 tag 的格式,比如 nv${version}
1
2
3
4
5
{
"release": {
"tagFormat": "nv${version}"
}
}

假如我们目前的版本为 5.1.0,那么对应的会有一个名为 v5.1.0 的 tag,在修改 tag 格式为 nv${version} 后,我们手动增加一个名为 nv3.0.0 的 tag:

1
git tag nv3.0.0 v5.3.0

此时我们的下一个版本号就是从 3.0.0 的基础上开始计算了。

线下验证

如何能验证我们的修改是否生效?可以在线下以 dryRun 的形式执行 semantic-release:

1
npx semantic-release -d

改模式下只会计算下一个版本号,生成 release 信息,而不会真的发版。