__CAPGO_KEEP_0__

构建类型解释:从本地到生产

你打开一个项目,看到

,加上几个 nobody 想要触摸的 shell 脚本。然后有人说,“请在今天晚些时候为客户制作一个测试环境构建。”如果你是一个中级的移动开发者,那么这个请求通常会感到很恼人。哪个配置?哪个签名身份?哪个后端?哪个分发路径?

马丁·多纳迪厄

马丁·多纳迪厄

内容营销人员

构建类型解释:从本地到生产 build:ios:dev, build:android:qa, build:staging, build:release, build:prod你打开一个项目,看到

那種混淆通常是因為把編譯類型當作是一個平面列表。它們不是。它們是一個工作流程。每個編譯都存在於解決特定的問題在特定的點之間你的筆記本和用戶裝置。

一個編譯不只是編譯好的應用程式。它是一個為了目的、受眾和環境而組裝的應用程式版本。有些編譯存在於幫助你debug。有些存在於幫助QA安全地破壞。有些存在於讓發行工程師可以產生可信賴的物件。有些存在於讓產品團隊可以以較少風險發送變更。

如果你的本地設置仍然感到不穩定在任何事情都開始之前,先用一個適當的 Capacitor本地環境設置

來控制它。當你的基礎工具是可預測的時候,建構複雜度就變得更容易理解。

软件构建世界的纷乱

我经常看到的最常见错误是假设构建名称包含了所有信息。它们并没有。 staging 这可能意味着“发布版本,指向测试API”。在另一个仓库中,它可能意味着“可调试的QA artifact,模拟支付”。在第三个仓库中,它可能意味着“私有发布的生产签名构建”。

这就是为什么团队会陷入混乱的原因。标签只有在您了解该构建正在做什么时才有用。

有一个有用的方法来思考构建类型:

  • 本地构建 帮助个人开发者快速移动。
  • CI构建 为团队创建一个共享的真实来源。
  • Debug 和发布版本 定义应用程序如何编译和 instrumenting。
  • 发行版 定义谁接收应用程序和如何。
  • 签名版 确定平台是否会信任 artifact。
  • 基于渠道的更新 确定更改在安装后如何移动。

这些并不是竞争分类。它们叠加。

“staging build”通常是一个组合,包括风味、环境、签名和分发选择。

这就是为什么两个团队都可以说“我们需要一个 beta build”并且意味着完全不同的 artifact。

这在移动设备上最为重要,因为每一步都会增加摩擦力。原生编译、机密信息、授权、应用商店跟踪、测试者访问、环境配置和回滚都必须齐全。如果其中一个部分松散,发布过程就会变成部落知识。然后一名工程师去度假, nobody 能够干净地发布。

这类团队不会记住更多的脚本。他们将构建类型定义为质量门控。每个门控降低了不同类型的风险:code破损、配置错误、签名错误、发布错误或恢复错误。

本地构建与CI构建

本地构建是你自己制作的版本。CI构建是团队可以信任的版本。

这听起来很明显,但很多构建痛苦都是因为团队把这两者混淆起来。有人证明“它在我的机器上工作”,然后分支在CI中失败,因为本地环境隐式依赖于缓存的依赖项、手动编辑的文件或未进入自动化的签名资产。

专注的男子在现代办公室的笔记本电脑上工作,研究本地与CI开发。

本地构建

本地构建是私有的、快速的、可丢弃的。你使用它们来回答紧急问题。

屏幕是否可以渲染?本地插件是否初始化?Gradle或Xcode的更改是否破坏了编译?是否可以在日志开启时重现崩溃?

一个好的本地构建优先考虑速度而不是形式。它经常包括松散的检查、详细的日志、开发者开关和临时的仪器。那样是可以的。它的工作是快速反馈。

不起作用的是将本地构建提升到比它更重要的东西。一个本地构建永远 shouldn’t 成为发布artifact,因为它在一台笔记本电脑上编译成功了。

CI构建

CI构建速度较慢的原因在于它们从等式中移除了个人机器状态,并使构建过程可重复。

当CI健康时,它做三件事得当:

  • 从零开始重建: 它证明了项目可以在没有隐含的本地假设的情况下编译。
  • 运行团队级别的检查: 单元测试、linting 和打包规则每次都发生在同一个地方。
  • 产生可追踪的工件: 团队可以将构建与提交、branch 和 pipeline 运行关联起来。

这就是我喜欢工作坊-工厂类比的原因。您的笔记本电脑是迭代的工作台,CI是证明过程是真实的assembly line。

如果您的团队仍然手动决定哪个脚本在哪里运行,centralize 该逻辑在自动化中。一个实用的参考是关于 使用GitHub Actions管理dev 和prod构建的指南.

实用规则: 如果QA、产品或支持需要artifact,它应该来自CI,而不是开发人员的机器。

一旦接受了这一点,构建生命周期的其余部分就变得容易了。

核心构建风味Debug vs Release

构建有很多标签,但在所有这些命名之下,通常最重要的两种风味是: debugrelease.

它们存在于开发人员和终端用户需要相反的东西。

debug构建与release构建的比较 infographic,展示了在性能和目的方面的关键差异。

Debug构建

debug构建旨在帮助人类检查行为。它通常保留更多的元数据,方便调试,并避免了可能隐藏问题的激进优化。

有一个来自建筑规范的有用的类比。规范通常分为 规范性, 性能, 专有, 和 参考标准 类型, 和一个调试构建映射得很好地到一个 规范性 方法是因为它规定了精确的工具和方法进行分析, 而一个发布构建映射到一个 性能 方法, 以及在 这项分解的构建规范类型.

在实践中, 调试构建是你想要像这样的东西:

  • 可读性诊断: 堆栈跟踪、控制台输出和符号,帮助您找到错误。
  • 开发者便利性: 模拟开关、测试菜单和功能开关,适合于非终端用户。
  • 低摩擦迭代: 更快的安装和运行周期比包装更重要。

调试构建不是“坏的”。它们是为特定目的而设计的。

发布构建

发布构建是为野外设备而设计的。这会立即改变优先级。

现在您关心的是包的完整性、启动行为、更紧密的安全姿势、更小的负载和可预测的运行时特性。您还希望减少意外的检查或滥用的入口点。

这种权衡很简单。使调试构建更容易检查的所有内容都倾向于使发布构建不适合生产环境。

我在团队中使用的决策边界是:

口味 最佳选择 它优化了什么
调试 开发、本地测试、问题复制 可见性和迭代速度
发布 beta 分发、商店提交、生产发布 稳定性、性能和信任

团队为什么仍然会这样错误

混淆的最大来源是将“环境”与“口味”混淆。

一个构建可以 针对测试环境的发布版本. 这是 QA 的常见做法,因为您希望生产环境的行为与非生产环境的数据保持一致。一个构建也可以 针对开发环境的调试版本 用于日常编码。这些是不同的轴。

很多脚本的杂乱来源于团队在包名中编码了所有可能的 combination 而不是记录矩阵。

在非开发人员测试用户界面行为时,始终将发布版本推送。将调试版本保留用于工程工作和故意的故障排除。

这一个规则消除了很多意外的复杂性。

将构建映射到发布环境

关于构建类型的讨论通常会停得太早。他们解释了本地、调试和发布,然后忽略了更困难的问题:这个构建要去哪里?

这个目的地会改变构建应该包含什么、如何签名以及谁应该接收它。

一个实际的构建工作流通常会通过几个环境,各个环境都有不同的受众和风险承受能力。如果您正在使用 Capacitor 应用程序,它也会有助于保持开发和生产应用程序行为之间的清晰分离 在 Capacitor 中因为很多“构建错误”实际上是环境映射错误。

晚间和试验鸽

这些是早期警告版本。它们是为工程师、QA或愿意容忍粗糙边缘的小型内部团队准备的。

晚间版本通常在预定的时间表或最新的主分支状态下生成。试验鸽版本是故意向狭窄的观众暴露的,旨在在更广泛的推广之前进行测试。我将它们视为学习工具,而不是稳定性的承诺。

它们在以下情况下很有用:

  • 分支是否在模块之间整合得很好?
  • 原生依赖升级是否破坏了特定设备家族?
  • 内部测试人员是否能在更广泛的beta暴露之前发现回归?

什么不起作用的是将试验鸽版本给那些期望软件精细度的人。您会得到噪音的反馈,错误的观众会将正常的更迭问题归咎于发布问题。

测试和beta

到这个阶段,产品质量比工程便利性更重要。

测试或beta版本应该感觉与真实用户接收到的内容非常接近。通常这意味着发布版本、生产环境配置(在可能的情况下)以及通过平台工具,如TestFlight或Google Play测试轨道进行控制的分发。

The audience shifts here:

  • QA验证回归测试、工作流程和验收标准。
  • 产品经理在模拟环境中检查行为。
  • 外部测试人员验证可用性、设备覆盖和边缘案例。
  • 支持或成功团队可能会预览即将到来的更改。

错误在于将beta视为“仅仅是另一个调试构建”。如果您的测试人员正在评估真实用户流程,则需要像发布一样的条件。

私有分发构建

有些应用程序需要从公共商店用户群中完全排除,或者需要先到达更窄的群体。

这包括客户专用构建、内部员工应用程序、受管制的工作流程、现场操作工具和企业独有的分发。

这些通常需要更严格的控制谁可以安装应用程序以及哪个后端。

  • 这也是命名变得危险的地方。团队经常说“企业构建”,但实际上他们真正意味着几种不同的东西:
  • 一个私有签名的内部应用程序
  • 客户专属品牌物品
  • 供利益相关者审阅的预生产发布候选版本

它们是不同的运营模型。请在管道和命名中将它们保持分开。

生产

生产版本是对公众的承诺。它们将发送到应用商店、Google Play商店或您的用户所接受的等效认证渠道。

到这个阶段,构建应该是乏味的。这是赞扬。

您希望生产版本是可复现的、正确签名的、在发布条件下测试过的,并且与回滚计划相关联。您不希望最后一分钟的手动编辑、机器特定的hack或“我们将在下一个构建中修复它”的妥协。

这是快速查看版。

软件构建类型及其特征

构建类型 受众 配置 发布方式
本地开发者 个人开发者 通常调试,快速迭代,局部环境设置 直接从本地机器安装
CI 验证 工程团队 可重复的自动化构建,共享检查 CI 构建存储
晚间或 Canary 内部测试者,选定团队成员 早期集成状态,有限的发布 内部发布工具
测试或预览版 QA、产品、外部测试者 通常是发布类的非公开环境映射 TestFlight、Play测试轨道、私有链接
即时或企业 内部员工、客户、受限组 受控配置、目的地特定签名 私有发布渠道
生产 公众用户 最终发布配置、商店准备签名 App Store 或 Google Play

正确的构建类型是匹配用户风险耐受度的类型。大多数发布错误都是由于团队忽视了这一对齐而导致的。

Code 签名的关键作用

一个构建文件本身在移动设备上并没有什么意义。平台需要证明它来自可信的来源,并且在创建后没有被修改过。这个证明是 code 签名.

如果您曾经有过一个编译完美但拒绝安装、上传或启动的构建,签名很可能是问题所在。

一台显示 Python 源代码 code 的计算机屏幕,带有 Code 签名的覆盖文本。

签名实际上证明了什么

对于一个移动团队,code 签名有三个作用。

  • 真实性: 它将应用程序与开发者或组织联系起来,证明了它是由他们生产的。
  • 完整性: It helps prove the artifact hasn’t been tampered with since signing.
  • Authorization: 特别是在 Apple 平台上, 它还控制应用程序在哪里和如何运行。

很多开发者都被第三点迷惑了。 签名不仅仅是身份认证,还包括权限。

同样的应用程序 code 可以根据是否在设备上本地运行、向测试者分发、内部部署还是提交到商店而需要不同的签名材料。

签名如何随着目的地而改变

这是保持整个过程清晰的思维模型: 签名遵循分发.

本地开发环境使用一个身份和权限集。 通过 TestFlight 发送的 beta 版本使用另一个。 内部分发路径可能需要不同的配置文件。 公开商店发布有其自己的签名期望和审查兼容的打包。

一旦签名发生变化,artifact 的允许目的地也会随之改变。

通常情况下,一个有条理的设置包括:

  • CI 中存储签名资产: 不在个人笔记本上。
  • 清晰的分离目标: 开发、私有测试、企业、商店发布。
  • 旋转和访问控制: 尤其是在承包商或多个产品团队共享基础设施时。
  • 审计性: 你需要知道哪个管道使用了哪个签名身份。

如果你的团队在Capacitor应用中发布了Web更新,那么还有一个签名层需要考虑。这篇关于Capacitor更新器__CAPGO_KEEP_1__签名的 end-to-end security for Capacitor updater code signing 有用,因为它将原生二进制信任与更新包信任分开。

签名问题通常不是来自加密。它们来自于不清楚的所有权、手动处理和隐藏了应用了哪个身份的构建管道。

将签名材料视为生产基础设施,因为那就是它的本质。

orchestrating Releases with CI/CD and Update Channels

当团队成熟时,挑战不在于了解各种构建类型,而在于不需要人类猜测来协调它们。

CI/CD 中的协调工作

来自 https://capgo.app 的截图

您的管道是构建合同

可靠的管道每次都应回答以下问题:

  • 本次构建的用途是什么
  • 它使用哪种口味
  • 它接收哪些环境变量
  • 它必须通过哪些测试
  • 它使用哪种签名身份
  • artifact 将被送往哪里

That structure mirrors a good technical specification. A well-formed spec should include 目的和范围、功能需求、设计需求、技术标准、测试需求、交付需求和支持或维护需求,如本技术规范指南 中所述。同样的纪律使CI/CD更容易理解,因为管道不再是一个脚本集合,而是一个可执行的发布政策。在实践中,管道应该做出决定,而不是由手动运行它的工程师决定。分支规则、标签、审批步骤、签名上下文和部署目标都应该被编码。

有效的做法:

分支驱动意图:

  • 主分支、发布分支和标签触发不同的工作流。 明确的工件命名:
  • 口味、环境和目标在输出中可见。 推广而不是重建:
  • __CAPGO_KEEP_0__ 将验证的艺术品推进到前进,而不是重新创造它们。

什么经常失败的是“一个灵活的脚本”方法,所有人都传递自定义标志,并希望它们与商店或测试者需要的匹配。

频道添加了在二进制发布后控制

本机构建仍然粗糙。一次发布到商店后,改变Capacitor应用中的Web内容并不总是需要一个新的二进制文件。

这就是更新频道变得有用的地方。它们让团队可以将Web资产更新推送到Capacitor应用中的特定用户。对于Capacitor团队来说,一种选择是 Capgo,它发布了签名的Web包到目标频道,使您可以推送JavaScript、CSS、复制、配置和资产更改,而不必每次重建本机壳。

一个实际的模式如下:

  • CI/CD中的二进制构建: 创建、签名和分发本机应用。
  • 频道分配: 将用户或环境映射到beta、staging、生产或客户特定流。
  • 渐进发布: 将 Web 变更发送到一组用户之前进行更广泛的发布。
  • 回滚路径: 禁用或回滚一个坏的更新而不必等待商店审查。

如果您尚未设置该模型,请参阅有关在 __CAPGO_KEEP_0__ 中创建和删除更新通道的指南。 creating and deleting update channels in Capacitor 这是许多移动团队需要的战略转变。构建类型不仅仅是 artefacts。它们是控制点。CI/CD 控制如何生成二进制文件。通道控制如何发布安装后更改。

现代构建工作流的最佳实践

一个理智的构建系统是有主见的。它不会让每个开发人员自行决定发布行为。

我曾经与最强大的设置合作过,它们共享一些习惯:

selective rollout:

send web changes to one group before wider exposure.

  • 明确分开坐标轴: 味道、环境、签名目标和分发目标不应混为一谈。
  • 让CI生成团队可用的工件: 本地构建是用于开发,而不是用于信任的。
  • 尽早在发布类似条件下测试: QA和beta测试人员应该看到的行为尽可能接近实际应用。
  • 将签名资产保留在笔记本电脑外: 机密信息应在受控的具有有限访问权限的基础设施中存储。
  • 以人类可读的方式命名工件: 如果有人在几秒钟内无法识别一个文件的用途,则命名不合适。
  • 优先考虑推进而不是重建: 一旦工件经过验证,就应将其通过工作流向前推进,而不是手动重建。
  • 在发布前进行设计回滚: 商店回滚缓慢且操作繁重。 Web层回滚可以在更新Capacitor时更快,但前提是你需要事先规划好渠道和政策。

最大的思维转变是:不要问“哪个构建脚本应该运行?”而是问“我在这个阶段管理哪些风险?”这个问题会产生更好的构建系统。

如果你的工作流程能够清晰回答这个问题,那么你的发布过程就变得更容易操作、更容易审计,并且对一位资深工程师记住正确的咒语的依赖性会大大降低。


如果你的团队部署Capacitor应用并希望对发布工作流程有更紧密的控制, Capgo 值得作为该堆栈的一部分进行评估。它可以处理针对Capacitor应用内的Web资产的实时更新、支持签名包、渠道化回滚和回滚控制,这使得它在需要更快修复而不替换原生构建管道时非常有用。

Capacitor 应用程序的实时更新

当 web 层面的 bug 活跃时,通过 Capgo 将修复推送给用户,而不是等待几天的 app store 审批。用户在后台接收更新,而原生变化仍然在正常的审批路径中

立即开始

最新博客

Capgo 为您提供创建真正专业的移动应用所需的最佳见解。