交付能力的第一道瓶颈,通常不是代码,而是你能在多短时间内产出全部平台的包。
一、一台机器,六个产物
以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。
顾帆的产品支持 macOS、Windows 与 Linux,每个平台都要出 x64 与 arm64 两个包。他的手写清单是六个产物。
最初他全部在本地构建:Mac 上打 macOS 包,开虚拟机打 Windows 包,Linux 包在容器里跑。一次完整发布要花掉一个下午,而且经常在最后一个产物上失败,只能从头再来。
迁移到 CI 后,时间降到四十分钟,但账单也来了:六个组合、每个组合装依赖与编译,每月 CI 分钟数远超预期。
矩阵不是「都跑一遍」这么简单。 哪些组合可以交叉编译、哪些必须原生、哪些可以只出一个通用包,决定了成本结构。
二、矩阵里哪些组合是必需的
| 目标 | 能否交叉编译 | 建议 |
|---|---|---|
| macOS x64 | 可在 arm64 上交叉,但签名与公证更顺 | 用 macOS runner |
| macOS arm64 | 同上 | 与 x64 同机出通用包 |
| Windows x64 | 可在 Linux 交叉,但签名与安装器易出问题 | 用 Windows runner |
| Windows arm64 | 生态尚不完整 | 按需求决定是否出 |
| Linux x64 | 容器即可 | 用 Linux runner |
| Linux arm64 | 可交叉 | 按用户群决定 |
macOS 两个架构优先合并为通用包。 通用包体积更大,但只需一次签名、一次公证、一次验收,维护成本明显低于两个独立产物。
通用包也有代价。 体积接近两个架构之和,下载更慢;用户量小、包体大的产品,拆成两个包反而更合适。
Windows 建议用原生 runner。 交叉编译能出二进制,但安装器、签名工具与运行库检测在交叉环境下更容易出现难以复现的失败。
原生 runner 的排队时间要计入。 高峰期等待可能比构建本身更长;固定发布窗口时,把排队算进前置时间。
Linux 是最适合容器化的目标。 依赖可从镜像固定,构建可重复;arm64 是否要出,取决于是否有真实用户。
容器镜像要固定版本。 用 latest 标签会让同一份代码在不同时间产出不同结果;镜像摘要比标签更可靠。
每个组合都要有明确的保留理由。 说不出「谁在用这个组合」的,先停掉;矩阵每多一格,发布就多一次可能失败的机会。
三、CI 成本与前置时间
CI 账单由三部分构成:矩阵格数、单次时长与重跑次数。
月 CI 成本 = 矩阵格数 × 单次分钟 × 月发布次数 × 单价
示例:6 格 × 25 分钟 × 4 次/月 × 0.008 美元/分钟
月成本 ≈ 4.8 美元(不含缓存与存储)
前置时间 = 三平台产物全部可下载的时间 − 打 tag 的时间
重跑率比单价更影响成本。 一次失败重跑等于双倍开销;失败集中在依赖安装与签名步骤时,优先做缓存与预检。
失败要按阶段归类。 依赖安装、编译、签名、上传四类失败的处理方式不同;混在一起看,只会得出「CI 不稳定」。
缓存要分层。 依赖缓存、构建缓存与工具链缓存分开管理;把所有东西塞进一个缓存键,命中率反而下降。
并行度受限于密钥与配额。 签名步骤通常不能并行太多,因为共用同一份证书;把不签名的编译与签名拆成两个阶段,可以缩短总时长。
密钥轮换要有窗口。 换证书当天不要发布新版本;先确认旧证书仍可用,再安排切换。
前置时间要按最慢的一格算。 五个组合二十分钟完成、一个组合五十分钟,用户看到的仍是五十分钟。
产物命名要可检索。 版本、构建号、平台、架构四段式命名,比「final_v2_new」更容易在半年后找到。
夜间构建不能替代发布构建。 定时构建可以发现依赖漂移,但发布必须由明确的 tag 触发,并保留当时的完整日志。
四、密钥、产物与可复现
CI 里最危险的东西是签名密钥,最容易丢的东西是产物。
| 项目 | 要求 | 失败后果 |
|---|---|---|
| 密钥注入 | 加密变量或密钥服务,按环境隔离 | 泄露后可被冒名签名 |
| 密钥权限 | 仅发布任务可读,PR 不可读 | 任意分支都能产出可信包 |
| 产物保留 | 至少保留最近若干版本与校验和 | 回滚时找不到旧包 |
| 校验和 | 每个产物生成并公开 | 用户无法验证下载完整性 |
| 可复现 | 固定工具链与依赖版本 | 同 tag 两次构建结果不同 |
密钥不能出现在 PR 构建里。 外部贡献者提交的代码若能读取签名密钥,等于把发布权交出去;发布任务应只对受保护分支与 tag 开放。
只读密钥也要隔离。 构建只需读取依赖凭证,不应拥有发布权限;按任务拆分权限,泄露面更小。
产物要保留校验和。 用户下载后能核对哈希,是直装分发最基本的信任手段;校验和本身也要放在可长期访问的位置。
校验和要与发布公告一起发。 单独放在构建产物里,用户找不到;放在下载页与更新日志,才有人核对。
可复现构建要设边界。 完全可复现成本很高;一个人的产品至少做到「固定工具链版本、锁定依赖、记录构建环境」,让同一次发布可以解释。
构建日志要归档。 出问题时,日志是唯一能还原「当时到底跑了什么」的证据;只保留成功日志不够,失败日志同样要留。
五、行动清单与复算验收
- 列出所有目标平台与架构,为每一格写明用户理由
- 决定哪些组合合并为通用包,哪些停用
- 把签名步骤与编译步骤分离,密钥只在发布任务可见
- 为依赖、工具链与构建产物分别配置缓存
- 记录连续三次发布的矩阵耗时与失败格
- 为每个产物生成校验和,并与安装包一起发布
- 固定工具链与依赖版本,记录构建环境
- 归档成功与失败的构建日志,至少保留最近五个版本
验收标准:从打 tag 到三平台全部可下载,时间可预测且失败格可定位。 只回答「大概半小时」的,说明还没量过。
月 CI 成本 = 格数 × 单次分钟 × 月发布次数 × 单价
前置时间 = 最后一个产物可下载 − tag 时间
失败重跑率 = 重跑次数 ÷ 总构建次数
重跑率高于 20%:先修最常失败的那一格
前置时间波动超过 50%:说明有步骤在等待外部资源
使用边界与实操验收
本文只讲构建矩阵与 CI 成本,不涉及签名与公证的具体步骤(第 02、03 篇)、安装包格式(第 04 篇)与版本回滚(第 09 篇)。
多产品共享构建与部署底座属于共享底座:账号、计费、邮件与部署的复用边界 ;发布产物的长期归档属于代码与仓库:多远程、组织所有权、镜像与归档 。
本文也不主张「所有平台都要原生构建」。用户量极小的平台可以只提供交叉编译产物,并在下载页注明已验证范围;关键是把「验证过什么」说清楚,而不是假装全都验过。
验证以发布记录为准:连续五次发布,记录每格的耗时、失败原因与重跑次数。若某一格长期拖慢整体,优先考虑合并或停用,而不是加机器。
记录时把「排队时间」与「构建时间」分开。合并统计会让你误以为加机器有用,实际瓶颈在调度。
关键参考与核验范围
本文的矩阵与成本为教学演示,实际配额与单价请以所用 CI 服务为准。核验日期:2026-10-24。
- GitHub Actions documentation :托管 runner 的规格、矩阵策略与密钥管理说明。
- Reproducible Builds :可复现构建的定义、收益与常见障碍。
上述两条只用于说明公开文档入口,不构成对任何 CI 服务的推荐。配额、镜像与计费方式会调整,实施前请核对当期文档。
系列导航
- 上一篇:安装包与更新器:dmg、MSIX、AppImage 与增量更新的验收
- 下一篇:崩溃上报与版本遥测:发布后 72 小时的止损阈值
- 回到目录:一个人把软件交付到用户手里:签名、安装、更新与审核
- 相关阅读:共享底座:账号、计费、邮件与部署的复用边界 、代码与仓库:多远程、组织所有权、镜像与归档
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。