绿色的构建日志很会让人放松警惕。
测试通过,打包结束,命令最后没有红字。做到这里,顺手写一句「已经发布」似乎很自然。我维护 npm 包、GitHub Pages 和 macOS 应用以后,却越来越不敢这么说。
因为构建结束的地方,刚好是交付开始的地方。
源码可以完全正确,tarball 里却少了类型文件;Pages 首页可以返回 200,脚本路径却还指向站点根目录;DMG 已经上传,里面的应用也可能点开就退。它们不是罕见的离奇事故,只是发布链路上的不同阶段在各自说话。
我后来给自己定了一条有点笨、但很有用的规则。每说一句「完成」,先问手里的证据究竟能证明哪一段。
一条绿色日志,只覆盖它亲眼看见的范围
我会把交付拆成几层来看。源码层包括 lint、类型检查和测试;构建层证明目标产物能够生成;制品层检查真正要分发的 tarball、ZIP、DMG 或静态目录;远端层确认 commit、tag、Release、npm 和 Pages 已收到目标版本;用户层则从公开入口重新走一遍实际路径。
这些层之间有顺序,却不能互相冒充。
单元测试通过,是后一层的好起点,不是 npm 已经发布的证明。Release 页面上出现一个附件,也只说明文件被上传了,不说明文件里面的应用能启动。
问题常常出在我们习惯把「命令成功」理解成「目的达成」。命令只负责它自己的那一小段,产品是否真的到了用户手里,需要另一份证据。
npm 包要以安装者看到的内容为准
npm 最常见的错觉是「仓库里明明有」。README 有,声明文件有,构建目录也有,可发布白名单、入口配置和打包脚本决定了用户最后能不能拿到它们。
所以我发布前会先生成真实 tarball,而不是只看 dist。检查 exports、main、module 和 types 能不能落到包内文件,ESM、CJS、声明、README 与 LICENSE 是否都在预期位置。再建一个干净的临时项目,安装这份 tarball,真正导入一次核心 API。
这一步很像提前扮演用户。它会暴露很多源码仓库里看不见的问题,尤其是内部相对路径碰巧能工作、发布以后却缺文件的情况。
正式发布后还要再来一次。不是拿本地 tarball 代替公开版本,而是从 npm registry 下载刚刚发布的包复验,并比对 gitHead、Git tag 和远端 commit。版本号一样,不代表里面一定是同一份源码。
Pages 返回 200,最多只能说明门开着
GitHub Pages 很容易在子路径上出问题。首页能打开,不代表 CSS、JavaScript、图片和文章详情都能在 /project/ 这样的 base 下找到自己。
我会先确认 Actions 工作流真的完成,而不是排队、取消或只完成了前半段;再检查 Pages 配置指向的工作流。公开页面出现新版本的可识别内容以后,还要访问静态资源和深层路由,最后用浏览器走一遍点击、输入、切换和移动端布局。
HTTP 200 是服务器给的回答,不是浏览器对整个产品的验收。它甚至可能返回一个自定义错误页,或者一份引用旧资源的 HTML。
这也是为什么我不太喜欢用「网站已上线」概括 Pages 检查。更诚实的说法是首页已回读、资源已加载、深层路由可访问、关键交互已在浏览器验证。句子变长一点,遗漏会少很多。
DMG、ZIP 和 .app 各自只证明一部分
macOS 应用的发布链更长。以 CodexUsage 为例,Release 里同时可能有 DMG、用于更新的 ZIP 和校验文件。三个附件都在,并不等于三条使用路径都成立。
DMG 要能通过镜像校验、正常挂载和拖拽安装;ZIP 要完整解压,目录结构不能多包一层;.app 的架构、Bundle ID、版本与签名要符合预期。原始打包应用、ZIP 解出的应用和 DMG 拖出的应用,最好分别启动一次。
SHA256SUMS.txt 也要对公开下载的文件重新计算。自动更新如果依赖特定 ZIP 名称和目录结构,还要按客户端真正的更新契约验证,而不是看到压缩包能打开就算结束。
codesign 通过不能证明资源完整,unzip -t 通过也不能证明可执行文件架构正确。每个工具都很有用,只是它们没有替整条发布链作证的能力。
发布命令结束以后,我会故意换一个视角回读
推送和上传没有报错,仍然可能在网络、权限或服务端处理环节留下不确定状态。发布脚本结束后,我会重新查询远端分支 SHA、tag 指向、Actions job、Release 资产列表、npm 版本与 dist-tag,再访问 Pages 的公开内容。
这里的关键是「重新」。不要继续相信上一条命令打印的乐观结果,而是从远端状态出发问一次,平台现在究竟保存了什么。
如果是公开交付,还应该从公开入口下载,不使用本地缓存和发布前产物。开发者视角里已经存在的文件,可能从来没有真正到达用户视角。
我宁愿把完成说得具体一点
现在我会尽量避免一句笼统的「验证完成」。我更愿意写清楚,源码检查通过,tarball 已在干净项目安装,公开 npm 包已下载复验;或者 DMG 已挂载,ZIP 已解压,三个来源的应用都能启动。
这种表达有点啰嗦,但它会逼着我看见证据之间的空白。没有验证的那一段,也不能再藏进一个听起来很完整的结论里。
绿色构建日志当然还是值得开心。
只是看到它以后,我现在知道该问下一句了。
用户真的拿到了吗?