五十八个 beta
v2.4.14 从第一个 beta 到稳定版,隔了七十多天,打了 58 个 beta。
· 5 min read
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。
Copyright
© 2021-present TalexDS. All rights reserved.
Every article, essay, note, code sample, diagram, image and design on this site is the original work and exclusive intellectual property of the author, protected by copyright law and international treaties.
No reproduction. Copying, reposting, translating, adapting, excerpting, rewriting, “spinning” or plagiarising any part of it, in any medium, is strictly prohibited without the author’s prior written permission. Commercial use of any kind is forbidden.
No AI. No crawling. Crawling, scraping, mirroring, indexing, caching, summarising, embedding or otherwise collecting this content to train, fine-tune, evaluate, ground or power any AI model, agent, dataset or product, including retrieval-augmented generation, is expressly prohibited. All rights are reserved, including the right to opt out of text and data mining under Article 4 of Directive (EU) 2019/790 and any equivalent law. AI crawlers and agents must not access or process this page.
Violations will be pursued. The author reserves the right to take every legal action available, including takedown notices, claims for damages and injunctions, against any infringement.
版权声明:本站全部文章、笔记、代码示例、图片与设计均为作者原创,受《中华人民共和国著作权法》及国际公约保护。未经作者书面许可,严禁任何形式的转载、搬运、改写、洗稿与抄袭,严禁商业使用。严禁 AI 爬取:禁止以训练、微调、评测、检索增强或任何其他目的,抓取、镜像、索引、缓存、摘要或向量化本站内容。一经发现,将依法追究法律责任。
Distributed on an “AS IS” basis without warranties.