v2.4.14-beta.1,一直到 v2.4.14-beta.58,中间没有断号。

Tuff 的这个版本,从第一个 beta 走到稳定版,隔了七十多天。58 是我数 tag 数出来的(release 页上的 prerelease 标记靠不住,所以数的是 tag 名)。

其实更早的时候,打 beta 的节奏不是这样的。2025 年 11 月 5 号那天,一天里打了十六个 tag,十五个是 v2.4.3 的 beta;到了 v2.4.7,一个版本用了一百天左右,二十多个 beta。

一个 beta

先说一个 beta 是怎么出来的。文档里的流程是这样:先让 stage 追上 master,再用 bumpp 改两份 package.json 的版本号,提交,打上 v2.4.14-beta.N 这样的 tag,推上去。tag 一推,构建就启动,从名字里认出 beta,三个平台各出一个预发布的包。同一个提交里还要放中英文两份更新说明,应用里的更新器读的就是它们,没有东西替你生成。

文档里的流程是这样。58 个 beta 是不是都照它走的,我只抽看了几个,没有逐个核。

门槛

README 里有一句:v2.4.14 对外发布的门槛是故意定得很严的,macOS、Windows、Linux 三个平台各自都要有真实安装、从 N 到 N+1 的更新、启动健康和恢复的证据,才能发。同一份 README 还提醒,稳定版的源码,不代表三个平台一样成熟,每个平台能做什么、退到哪一步,以各自的验收证据为准。

CI 这一头,8 月中旬有一条提交,标题大意是:Linux 上,把打好的包真的启动一次,别只数它。提交说明里交代的起因,是一个挂了九个月的 issue,说 Ubuntu 24.04 上启动不了;而之前的流水线只数 .AppImage 和 .deb 的个数,不是零就算过。文件存在和文件能跑是两个说法,整个仓库以前只查过前一个。改完以后,它会真的把包启动一遍,还会分开说是产品出了错,还是 CI 的环境缺了东西,比如缺共享库、没有 X server。

stage

9 月 26 号,分支和发布的规矩被写成了一条能跑的检查,pnpm check branch-policy。长期的分支,除了放文档的 gh-pages,就两条:master 和 stage。别的活都在 task/ 开头的短分支上做,合进去就删。规矩里最要紧的一句是:beta 要从一个已经包含 master 的 stage 上切。

理由写得很直白。stage 要是落后于 master,发出去的 beta 就缺了生产线上已经有的东西,往往正好是这个 beta 要验证的那个修复;而且它不报错,要等某个测试的人拿着一个标着新版本的包,来报一个早就修掉的 bug,才知道包是旧的。

这一条只在发 beta 的时候才会拦:往 stage 推,或者打带 -beta. 的标签。推 master、开 PR、打稳定标签的时候,只提醒,不拦,因为 master 比 stage 跑得快,本来就是两次 beta 之间的常态。这是第二版。第一版每次都红,第一回拿真实的分支去跑,stage 就落后 master 四十六个提交,二十来分钟后的下一个提交才改成现在这样。文档给的理由是,一个让日常工作失败的检查,人们学会的是绕开它,这比它盯着的那个缺口代价更大。

追不上的情况,文档也写了。stage 追 master 用的是只许快进的合并,被拒绝的话,文档的解释是 stage 上有 master 没有的提交,是某一轮 beta 的版本号提交没合回去。这时候正常把 master 合进来,在下一个 beta 的说明里提一句,不要强推,因为以前几个 beta 的提交,测试的人可能还在跑。

还有一条:浅克隆算失败,不算通过。没有历史的克隆,对“是不是祖先”永远回答不是,由它得出的结论,说的是一个并不存在的仓库状态。脚本的注释里有一句原则:缺了证据,不能读成通过。

门槛、CI 和这条检查,我觉得防的是同一种东西:没有证据的通过。

规矩变成检查的时候,v2.4.14 的 beta 已经走了八周左右,离稳定版还有两个礼拜左右。为什么是那个时候,记录里没写。我猜是走了这么多轮,才看得清哪里会出岔子,不过这只是猜的。

数

2.4.5 之后,稳定的 tag 只有五个:v2.4.7、v2.4.10、v2.4.12、v2.4.13、v2.4.14。夹在中间的 v2.4.8、v2.4.9、v2.4.11,只有 beta。

后来 v2.4.15 的编号又从 beta.1 数起,我写这几行的时候,数到了 beta.8。