跳转至正文

如何正确地为 CapacitorJS 应用进行 OTA 更新

了解如何使用 Capgo 对 CapacitorJS 和 Electron 应用进行 OTA 更新,本实战指南涵盖配置、签名、通道、CI/CD、测试和回滚。

如何正确地为 CapacitorJS 应用进行 OTA 更新

周五下午,你发现了一个结账流程的回归问题。修复代码已经在你的 Capacitor Web 层中,但 App Store 的审核窗口还要三天才能关闭。Android 用户可以更快收到新包,但为了一个仅涉及 JavaScript 的修复而强制所有客户重新安装原生构建,依然是一种浪费。

这正是团队学习如何对 CapacitorJS 应用进行 OTA 更新的实用原因。受控的空中发布可以将签名的 Web bundle 投递给符合条件的已安装二进制文件,在后台下载,并在下次启动时应用,而原生更改仍需遵循商店流程。难点不在于上传文件,而在于在实时运行的设备群中,保持版本目标、签名、发布控制、可观测性和恢复机制的一致性。

目录

为什么 CapacitorJS 团队需要 OTA 更新

一个 Capacitor 应用通常有两个发布面。原生二进制承载平台外壳、权限、插件、图标、授权项以及其他需经商店审核的能力;Web 层承载 JavaScript、CSS、HTML、路由、文案和资源。OTA 更新针对的是第二个面,因此只要改动不超出下文所述的平台和商店政策边界,团队无需重新构建原生外壳就能修复出问题的结账流程。 Capgo OTA 指南.

通过对比商店审核延迟与即时修复,说明 OTA 更新重要性的示意图。

带来的运维收益远不止速度。已安装的二进制并不会同步演进:有些客户启动的是较旧的原生 build,有些用的是最新的商店版本,每个二进制能为 Web bundle 提供的原生 API 也各不相同。针对不受支持的原生 API 编译的 bundle,即使 JavaScript diff 本身有效,也可能失败。所以 OTA 是一套兼容性系统,而不是绕过发布工程的捷径。

把更新器当作交付流水线

生产环境搭建需要清晰的步骤顺序:

  1. 安装原生桥接: 添加 updater 包并同步 Capacitor,让 iOS 和 Android 都能识别它。
  2. 为 bundle 签名: 把签名材料放在代码库之外,并让验证成为客户端流程的一部分。
  3. 指定目标 channel: 有意识地路由 staging、beta、production 或特定客户群组。
  4. 自动化交付: 让 CI 仅在测试通过后构建、签名、上传并晋级。
  5. 观察采用情况: 按渠道和二进制包跟踪下载、安装、启动、崩溃和健康信号。
  6. 快速回滚: 在服务端恢复已知可用的 bundle,并保留客户端恢复保护。

Capgo 是这个工作流的一种选择。其开源 Capacitor 更新器与云服务会将已签名的 web bundle 发布到目标渠道,在下次启动时应用,并提供版本及设备级交付信息。评估可用性与发布韧性的团队还可以查看 Capacitor 应用的可用性.

实战规则: 如果无法确定某个 bundle 针对哪个原生二进制文件,请勿将该 bundle 发布到生产环境。

当 OTA 能在不模糊 Web code 与原生 code 边界的情况下减少不必要的商店发布时,它才算成功。问题不在于你的团队能否推送 bundle,而在于你能否解释谁收到了它、他们为何符合条件、发布后发生了什么,以及当 bundle 出现异常时你将如何恢复服务。

准备工作与安装 Capgo Updater

在打开终端之前,请确认项目和发布账号已准备就绪。你需要一个 Capacitor 5 或 6 应用,并已 @capacitor/cli 初始化,Node 18 或更高版本,用于目标二进制文件的有效 Apple Developer 和 Google Play Console 账号,以及一个 Capgo 云端账号,并带有 appId 和 API 密钥。

首次安装刻意保持精简:

npm install @capgo/capacitor-updater
npx cap sync
npx @capgo/cli init

npm install 添加 JavaScript 包和原生依赖。 npx cap sync 是很多人会跳过的步骤,而漏掉它会导致原生 bridge 未链接。JavaScript 层也许能编译通过,但运行时随后会调用已安装的二进制文件中不存在的 bridge。安装后请运行同步,并在原生插件配置变化时再次运行。

初始化程序会向你的 capacitor.config.ts 注入更新设置、Web 端点和默认 channel。不要盲目接受这个补丁。打开文件,明确核对应用身份和更新契约:

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  version: '1.0.0',
  autoUpdate: true,
  updateUrl: '',
  capgo: {
    channel: 'staging'
  }
}

生成的具体结构可能因项目和 CLI 版本而异,但关键值相同。 appId 必须与已安装的二进制文件一致, version 必须描述与 Native Build 的关系, autoUpdate 必须反映你的发布策略, updateUrl 必须指向你的二进制文件所信任的服务,且 capgo 代码块必须指定初始 channel。

构建 Release 前先验证

打开 Xcode 或 Android Studio 前,先运行诊断命令:

npx @capgo/cli doctor

结果正常时应能确认 CLI 可用、项目配置可读、检测到 updater 包、所需的应用标识符存在,并且认证能访问已配置的账号。它不应报告缺少 native sync、缺少 app identity 或 updater 配置无效。

截图来自 https://capgo.app/docs/img/cli-doctor.png

让第一个 Native Build 刻意保持简单。把它安装到 iOS 和 Android 真机上,联网启动,关闭再重新打开,并确认 updater 能检查且不影响正常启动流程。 Capacitor updater 安装工作流 当你需要将项目配置与预期的 plugin 设置对比时很有用。

安全发布你的首个 OTA Bundle

把第一个 bundle 当作发布路径测试,而不是功能上线。准备 artifact 前,先创建或轮换签名密钥:

npx @capgo/cli key create

把私钥放在 secrets manager 里。不要提交它、存进项目归档,或在 CI 输出中暴露。native trust path 需要的是公开的验证材料。只有签名任务才能访问私钥。

在以下位置修改 JavaScript 端的版本号: package.json 或你的 Web 发布元数据。只修 Web 的话,保持 native 版本字段不变。OTA bundle 无法添加 native code,也无法更改已安装二进制声明的能力,所以改 native 版本只会掩盖兼容性问题,而不是改善它。

把签名后的产物上传到目标 channel:

npx @capgo/cli bundle upload --channel production

上传成功只能说明服务器接受了请求。请阅读命令输出,核实 bundle identifier、目标 native 版本约束、签名 checksum 和 channel 分配。这些字段标识产物,并说明已安装的二进制是否符合接收条件。

将 Dashboard 视为发布门禁

bundle 详情页应能回答四个发布问题:

  • 最低 native 版本: 能运行此 bundle 的最旧二进制是哪个?
  • 最高原生版本: 哪些较新的二进制文件被有意排除?
  • 通道: 哪些受众可以发现该 artifact?
  • 发布比例: 符合条件的受众中有多少比例可以接收?

生产环境曝光起始时间: 5%,然后定义扩展所需的依据。检查下载和安装成功率、冷启动是否正常、无崩溃会话,以及变更流程中的 JavaScript 错误。在 Capgo dashboard 中,将 artifact 标识和交付控制项放在一起显示。

截图自 https://capgo.app/docs/img/bundle-detail.png

使用 debug build 走一遍设备路径。 getCurrent() 报告当前活跃的 bundle,并且 notifyAppReady() 确认新 bundle 已达到健康状态:

import { CapacitorUpdater } from '@capgo/capacitor-updater'

const current = await CapacitorUpdater.getCurrent()
console.log(current)

await CapacitorUpdater.notifyAppReady()

只有在 App 初始化到足以通过健康检查后,才调用 readiness。如果始终未确认 readiness,自动恢复机制可能会在下次冷启动时将该 bundle 标记为失败。这项保护措施能保护生产环境用户,但也可能让不完整的测试看起来像交付问题。在扩大曝光范围之前,请记录每台测试设备的活跃 bundle 和启动结果。

Channels、Rollouts 和 CI/CD 自动化

Channels 是已发布的 bundle 与已安装设备之间的路由层,也是你的发布闸门。staging channel 应指向已知的 native binary,beta channel 应服务受控用户群,而 production 只有在之前的 channel 通过冒烟测试后才能推进。

使用明确的名称创建 staging 路径:

npx @capgo/cli channel create staging
npx @capgo/cli bundle assign <bundle-id> --channel staging

将该 channel 固定到测试设备所用的 native 版本。测试人员应运行生产环境将支持的同一二进制文件,而不是带额外 plugin 或不同配置的本地开发构建。smoke suite 通过后,直接 promote 已测试的 artifact,而不是再上传一个略有差异的 bundle。

你的 channel map 应放在 capgo.config.json 并像应用 code 一样进行审查。保持 channel 名称稳定,明确预期的 native 兼容范围,并将生产环境 promote 设为明确的 CI 操作。对于同时管理功能曝光(feature exposure)和交付曝光(delivery exposure)的团队,这些 feature flag 管理技巧 提供了一种实用方式,将部署权限与面向用户的激活分开。

将 promote 接入 CI

实用的 GitHub Actions 设计有两条路径:

  • Pull requests: 构建 web 层,用受限的 preview 凭据签名,并发布到临时 preview channel。pull request 关闭时,销毁或让该 channel 过期。
  • Tagged main releases: 运行单元测试,构建 production bundle,校验其 native 版本约束,上传,并仅在测试任务成功退出后 promote。

promotion 命令示例如下:

npx @capgo/cli bundle promote <bundle-id> \
  --to-channel production \
  --percent 5

按计划分步提高曝光度,例如 25%、50% 和 100%,并在每一步之间设置 CI 审批或监控任务。百分比和命令只是控制手段,并不能证明安全。构建通过只能说明 bundle 语法有效,不能说明它在特定 native binary 上、在存在持久化本地状态、网络缓慢,或设备从旧会话恢复时表现如何。

发布边界: Web bundle 属于特定的二进制兼容窗口。Channel 绝不能成为漏洞,去下发引用了已安装 App 中不包含的 native API 的 code。

通过将每个 bundle 与其构建时对应的确切 native 版本绑定,保持 TestFlight 和 Android internal-track 构建的一致性。如果 native build 发生变化,请发布针对新兼容目标的新 bundle,或创建新的 channel 映射。这份 Capgo GitHub Actions 集成指南 可以帮助你将该策略转化为可重复的工作流步骤。

展示软件 Channel 与自动化三个步骤的示意图:Staging Channel、Pin Binary 和 CI/CD Auto-Deploy。

测试策略与自动回滚

OTA 发布即使通过了 CI,在激活后仍可能失败。失败原因可能取决于 bundle、native binary、设备上保存的状态或网络状况。模拟器可以确认屏幕能渲染,但无法覆盖每个已安装的 binary,也无法说明启动失败后能否正常恢复。

使用三层测试:

  1. CI bundle 测试: 运行单元测试、类型检查、linting 和生产环境 web build。重点验证变更涉及的结账、认证、导航和持久化路径,而不仅仅是编译通过。
  2. 私有设备通道: 在私有通道中部署运行旧版原生二进制的物理设备。覆盖双平台、全新安装、升级安装以及具有代表性的存储状态。
  3. 生产环境金丝雀: 从一个小规模的合格用户群开始,接入崩溃报告并设置 JavaScript 错误告警。将启动失败视为紧急事件,因为受影响的用户可能永远无法到达报告应用内错误的 code。

本指南中介绍的 Capacitor OTA 测试指南 解释了测试机制。操作规则很简单:针对每个更新,测试其旨在保护的原生二进制文件。

把服务端控制与客户端恢复分开

服务端回滚会停止新的下载:

npx @capgo/cli channel set production --bundle <previous-id>

这会改变符合条件的设备接下来会发现的内容。它不会清除每台设备上已下载或已激活的 bundle,因此客户端还需要恢复控制机制。请配置 updater,保留一个已知可用的回退版本,并在满足预定义的启动失败条件后回退。

可靠的恢复路径包括:

  • 下载失败追踪: 区分网络连接问题与无效的 artifact。
  • 安装失败追踪: 检测解包、校验及文件系统问题。
  • 启动健康度追踪: 确认应用在激活后能进入可用状态。
  • 自动回退: 当启动反复失败时,恢复上一个已知可用的 bundle。
  • 手动升级处理: 保留设备和 bundle 标识符,以便进行技术支持和工程排查。

在设备群规模下,即使很小的失败率也会带来显著的支持负担。A OTA 更新成功率 99.95%,仍意味着每 100 万台设备中约有 1,000 次失败,根据 IoT 设备群的 OTA 测试基准。同一来源描述了如下防护机制:30 分钟内回滚、崩溃率低于 0.1%,以及 98% 的设备处于支持窗口内。请将这些视为基准示例,而非通用的验收标准。应根据应用的风险和用户影响来设定错误预算。

签名、安全,以及 OTA 无法改变的内容

未签名的更新会让交付端点成为供应链攻击面的一部分。TLS 保护连接,但客户端还必须确认下载的产物来自授权的发布流程,并且在传输过程中未被替换或篡改。

使用更新器工具生成 Ed25519 密钥对。将私钥存入 CI secret,并在应用 build 时将验证公钥嵌入原生二进制文件。客户端应在激活前验证每个 bundle。按环境和权限范围限制 CLI token,保留 upload 和 promotion 日志,并对生产操作要求强身份验证。

将密钥轮换视为兼容性迁移,而不是一次配置变更。生成替换密钥,发布一个同时信任当前公钥和替换公钥的 Native Build,然后用新私钥签名发布版本。只有在兼容的原生二进制已采用替换密钥后,才移除旧密钥。过早移除信任会导致旧二进制无法接受有效更新。无限期信任已泄露的密钥则会让轮换失去意义。

在发布计划中划清边界

OTA 负责 Web 层变更。当变更依赖已安装二进制中不具备的能力时,就需要 Native Build:

类别 通过 OTA 发布 需要 Native Build
应用行为 JavaScript 逻辑、路由、验证和状态处理 Native 插件实现
展示 CSS、HTML、文案、主题 token 及兼容的图片资源 应用图标和启动屏资源
平台访问权限 现有 Capacitor Web 层对二进制包中已有能力的调用 新的权限、entitlements 或原生 API
配置 兼容的 Web 配置和远程内容规则 Info.plist, AndroidManifest.xml,签名身份
应用商店元数据 没有会改变经商店审核的行为或版本元数据的变更 对商店政策敏感的元数据和版本号变更

本指南中介绍的 Capacitor 更新器 code 签名文档 说明了验证模型。请在实际运营中结合防篡改审计日志、证书验证、TLS、严格的访问控制,以及针对版本欺骗或密钥泄露的调查流程来落实。

兼容性目标仍然重要,因为已安装的原生设备群体是逐渐变化的。Apple 报告称 66% 的活跃 iOS 设备运行 iOS 26 以及 74% 近四年内推出的设备运行 iOS 26,而另一份报告将 iOS 26 的比例定为 在周期后期,79% 的设备运行 iOS 26,正如 移动 OTA 密钥管理研究。这些数据涵盖不同的时间点和设备群体。实际结论是一致的:即使是成熟的平台,也不会一次性更新所有设备。应按原生版本和功能分配 bundle,而不是假设一个 OTA 产物适用于整个设备群。

每次发布前的运维检查清单

将发布检查清单控制在一页内,与代码仓库或 runbook 一起保存。每次事故后都更新它。真实故障后新增的控制措施,通常能防止下一次复发。

上传前

  • 确认兼容性: 验证 bundle 是否针对正确的原生二进制,且未使用任何不可用的插件 API。
  • 检查构建产物: 在干净的工作区进行 build,检查生成的文件,并确认 web 版本符合预期。
  • 保护签名安全: 确认 CI 已配置预期的 secret 和 key。检查 logs 不会暴露私密材料。
  • 验证 channels: 在分配 bundle 之前,检查 staging、beta 和 production 的映射关系。
  • 保留恢复能力: 记录上一个已知可用的 bundle,并验证 fallback 路径是否仍然有效。
  • 进行真机冒烟测试: 在代表性设备上测试启动、登录、结账、导航、持久化和更新就绪情况。

推广期间

先向小范围用户群发布,然后监控与用户影响相关的信号:

  • 无崩溃会话率: 将结果与上一个 bundle 进行对比,并排查回归问题。
  • JavaScript 错误率: 按 bundle、原生版本、平台和功能路径对错误进行分组。
  • 冷启动行为: 检查激活是否改变了启动过程,或导致用户看到空白屏幕。
  • 功能覆盖: 确认远程启用的功能与接收它的二进制文件相匹配。
  • 采用情况与失败: 将未检查更新的合格设备与已检查但失败的设备区分开来。

在扩大发布范围前,请先保持首个发布后观察窗口为 30 分钟 如果关键路径中断,请将生产 channel 回滚到上一个 bundle,保留 logs,并决定修复是放进另一个 OTA bundle,还是发布新的原生版本。

发布日规范: 只有当团队明确 fallback bundle 的标识符、兼容范围及恢复命令时,它才能发挥作用。

请将此清单视为共享的 code。Capgo 可提供已签名的 bundle 交付、channel 定向、发布控制、更新历史以及设备级可观测性。你的团队仍需负责兼容性策略和发布决策。


Capgo 为 CapacitorJS 团队提供了受控的已签名 JavaScript、CSS、配置和资源更新路径,支持 channel、分阶段交付、回滚保护和发布可观测性。请在下次部署时应用此清单,用 staging binary 测试,并评估 Capgo 其在生产工作流中的表现。

为 Capacitor 应用提供 Live Updates

当 Web 层出现线上 Bug 时,通过 Capgo 发布修复,而不用等 App Store 审核好几天。用户会在后台收到更新,原生变更则仍走正常审核流程。

博客最新动态

Capgo 为你提供打造真正专业的移动应用所需的最有价值的洞察。