跳过主要内容

CI/CD for Capacitor: 常见问题和解决方案

The CI/CD pitfalls that break Capacitor builds: iOS signing, macOS runners, Xcode 26, stale cap sync, version codes, store rejections, and fixes for each.

马丁·多纳迪

作者

Writer

Valeria

审阅者

乔丹

编辑

CI/CD for Capacitor: 常见陷阱和解决方案

大多数 Capacitor CI/CD 失败来自同一短列表:iOS code 签名,缺失或过时的 macOS 工具,未能进入本机项目的 Web 构建,重复使用的构建号码,以及仅在上传时才会失败的商店要求。每个都有已知原因和可以应用一次的修复。这份指南将陷阱按管道阶段分组,包括错误、发生原因和需要更改的内容。

如果您正在从头设置管道,请先阅读 为 Capacitor 应用设置 CI/CD 然后使用此列表来加固它。

阶段 1:构建 Web 层

陷阱:本机应用程序传输的 Web 构建过时

症状: CI成功,但应用安装后显示昨天的UI。

原因: cap sync 拷贝的是当时的内容。如果流水线在web构建之前运行,或者web构建写入的文件夹与 webDir 中的文件夹不同,native项目会获取过时的文件。 cap sync 解决方法: webDir in capacitor.config.ts与打包工具的输出(对于Vite,

)匹配。 始终按照此顺序运行步骤,并在输出文件夹为空时失败。

bun install --frozen-lockfile
bun run build
test -f dist/index.html || { echo "web build missing"; exit 1; }
bunx cap sync

确保 webDir 匹配您的打包器输出(dist 为 Vite www 对于 Angular 与 Ionic 的情况, build 对于某些 React 配置的情况,

陷阱:开发服务器 URL 未从配置中删除

症状: 发布构建显示空白屏幕或尝试加载 http://192.168.x.x:5173.

原因: server.url 在 capacitor.config.ts 设置为实时重载并提交了

修复: 永远不要提交 server.url. 从 CI never 设置的环境变量中读取:

import type { CapacitorConfig } from '@capacitor/cli'

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'Example',
  webDir: 'dist',
  ...(process.env.LIVE_RELOAD_URL && {
    server: { url: process.env.LIVE_RELOAD_URL, cleartext: true },
  }),
}

export default config

陷阱:环境变量在错误的时间内被烘焙

症状: 生产应用程序与API阶段通信。

原因: Vite、webpack和Angular在构建时间内行内环境变量。存在于__CAPGO_KEEP_0__中的值是二进制中的一个,来自同一工作的任何__CAPGO_KEEP_0__。 bun run build 在二进制中,ran是唯一的,并且在任何来自相同作业的live update中都是如此。

Fix: 陷阱:锁文件漂移 cap sync.

CI中的插件版本与机器上的版本不同,原生编译失败,缺少符号。

症状: 将锁文件提交并使用

Fix: Pitfall: lockfile drift bun install --frozen-lockfile 或 npm ci. Pin @capacitor/core, @capacitor/ios, @capacitor/android, 和 @capacitor/cli 到相同的版本。版本不匹配是原生错误的常见来源;参见 修复Capacitor版本不匹配错误.

阶段 2:原生工具链

陷阱:错误的 Node、JDK 或 Xcode 版本

症状: Unsupported class file major version, The engine "node" is incompatible, 或 Xcode 错误关于SDK功能。

原因: 托管运行器图像更改,并且Capacitor 8 有固定的最低要求。

工具 Capacitor 8 要求
Node.js 22 或更新
JDK 21
Xcode 26 或更新
iOS 部署目标 15.0
Android minSdkVersion / targetSdkVersion 24 / 36

修复: 将每个版本都明确地在管道中固定,而不是信任 latest:

- uses: actions/setup-node@v6
  with:
    node-version-file: .nvmrc
- uses: actions/setup-java@v4
  with:
    distribution: temurin
    java-version: '21'
- uses: maxim-lobanov/setup-xcode@v1
  with:
    xcode-version: '26'

陷阱:在 Xcode Apple 不再接受的构建

症状: 由于应用程序使用不支持的SDK进行构建,上传失败,显示错误信息。

原因: 自 2026 年 4 月 28 日起,App Store Connect 需要 Xcode 26 和 iOS 26SDK。自建 Mac 和旧的运行器图像仍然默认使用 Xcode 16。

解决方法: 选择一个 macos-26 图像或在运行器上安装 Xcode 26。详细信息在 Apple 的 Xcode 26 要求Capacitor应用程序.

陷阱:CocoaPods 和 SPM 混淆

症状: xcodebuild: error: 'App.xcworkspace' does not exist,或找不到 pods。

原因: 新Capacitor 8 项目使用 Swift Package Manager 构建 ios/App/App.xcodeproj . 使用较旧的项目使用 CocoaPods 和构建 ios/App/App.xcworkspace after pod install . 从较旧的教程中复制的管道假设工作区

Fix: 检查您的项目使用哪一个,并构建正确的文件。如果您正在迁移,请参阅 如何将您的 Capacitor 应用程序迁移到 SPM.

陷阱:Android 插件构建在 Gradle 升级后会中断

症状: Namespace not specified, package attribute is deprecated,或插件的错误 build.gradle 在更新 Android Studio 后

Fix: 固定 Android Gradle 插件 android/build.gradle升级到特定分支,首先更新插件。具体错误在 修复 Capacitor 插件构建错误与 AGP 9.

阶段 3: Code 签名

陷阱:iOS 签名仅在 Mac 上有效

症状: No signing certificate "iOS Distribution" found, No profiles for 'com.example.app' were found或 errSecInternalComponent.

原因: 你的 Mac 的密钥链持有证书,Xcode 下载配置文件。CI 运行器没有,且其密钥链在非交互式会话中锁定。

修复: 导入证书到临时解锁的密钥链,并在 Xcode 16 及其后版本中安装配置文件:

security create-keychain -p "$KEYCHAIN_PASSWORD" ci.keychain
security set-keychain-settings -lut 21600 ci.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" ci.keychain
security import dist.p12 -k ci.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" ci.keychain
security list-keychains -d user -s ci.keychain login.keychain

PROFILES="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"
mkdir -p "$PROFILES"
cp app.mobileprovision "$PROFILES/"

使用身份名称 Apple Distribution而不是遗留 iOS Distribution. fastlane的 setup_ci plus match 自动化此过程。 Capgo security.

构建

使用证书和配置文件作为环境变量, 在其机器上完成密钥链工作,因此Linux任务永远不会触及

错误:过期或被吊销的证书 症状:

Fix: 原因: bunx @capgo/cli@latest build prescan --platform ios 苹果分发证书和配置文件在一年后过期。团队成员在Xcode中创建新证书也可以使CI使用的配置文件失效。

解决方案:将过期日期添加到团队日历中,保持一个共享的分发证书用于CI,并在构建之前检查。

症状: fastlane hangs 等待一个 6 位数字 code.

解决方法: 使用 App Store Connect API 密钥((.p8key ID

base64密钥无法解码

症状: MAC verification failed, invalid keystore format, or base64: invalid input.

, 或 原因: .p12 复制 base64 值时添加的换行符,或者

解决方法: 编码在一行中,使用 -legacy 创建时 .p12 使用 OpenSSL 3:

base64 -i dist.p12 | tr -d '\n' > dist.p12.b64
openssl pkcs12 -export -legacy -inkey key.pem -in cert.pem -out dist.p12

陷阱:丢失的 Android 密钥库

症状: 无法签名更新,因为没有人有密钥库。

解决方案: 使用 Play App Signing 时,密钥库是上传密钥,Play 控制台支持可以注册一个新的密钥库。将密钥库存储在 CI 秘密中,并在离线备份中。绝不让它只存储在一个笔记本电脑上。 Android 密钥库生成器 如果您刚开始使用,会创建一个新的密钥库。

阶段 4:管道设计

陷阱:每次拉取请求都进行原生构建

症状: 慢速PR检查和大型macOS分钟账单。

原因: iOS在托管macOS运行器上构建的分钟是大多数CI计划中最昂贵的分钟,且大多数提交只触摸JavaScript。

解决方案: 在每个PR上运行lint、测试和web构建。 在发布标签或合并时运行本机构建。 对于PR预览,向__CAPGO_KEEP_0__频道发送web包,而不是构建二进制文件,正如在 main比较CI/CD平台的Capgo应用程序 Comparing CI/CD platforms for Capacitor apps.

使用矩阵,使两者在并行中构建,并设置

解决方案: 以便iOS签名问题不会取消一个好的Android构建。 fail-fast: false Comparing CI/CD platforms for __CAPGO_KEEP_0__ apps

strategy:
  fail-fast: false
  matrix:
    platform: [ios, android]

陷阱: 缓存不开或缓存设置错误

症状: 每次构建都会从头下载 Gradle 依赖项和 CocoaPods, 或者是使用陈旧缓存的环境构建会将生产配置发送给客户端。

解决方案: 缓存 ~/.gradle/caches, ~/.gradle/wrapper,并 ios/App/Pods 根据 lockfiles 键值对缓存,根据环境分区缓存。使用 Capgo Build,可以将每个应用程序的构建缓存分区为 --cache-key prod 或 --cache-key staging跳过以进行干净构建。 --no-cache 陷阱: 多模块项目路径

单库项目路径陷阱

症状: could not find capacitor.config 或原生项目中缺少的插件。

解决方案: 在应用程序包中运行 Capacitor 命令,并将工具指向提升的 node_modules。 Capgo CLI 接受 --path 并且 --node-modules 用于此。

第五阶段:商店提交

陷阱:重复使用的构建号

症状: “iOS上必须更新的bundle版本号要高于之前上传的版本号”,或“Google Play上版本code已经被使用过了”。

解决方案: 生成CI中的数字。使用fastlane,读取最新的TestFlight构建并加一。使用 Capgo Build,这是默认行为:它从App Store Connect或最高的 versionCode 从 Google Play 和增加它。

陷阱:在 TestFlight 中卡住的构建

症状: 上传成功,但测试者永远无法看到构建。

原因: 缺少出口控制答案。

解决方案: 在中声明它一次 ios/App/App/Info.plist 如果您只使用标准加密:

<key>ITSAppUsesNonExemptEncryption</key>
<false/>

陷阱:隐私清单拒绝

症状: 来自苹果的电子邮件关于缺少必需的原因API声明(ITMS-91053)。

修复: 添加一个 PrivacyInfo.xcprivacy 到应用目标并更新将自己的插件更新。请参阅 隐私清单指南为Capacitor应用.

__CAPGO_KEEP_0__ 应用

修复: 陷阱:错误的工件类型bundleReleaseGoogle Play 需要一个 AAB( bundleRelease ),而不是 APK。 release 仍然成功而没有签名配置并产生一个 Play 拒绝的未签名 AAB,因此配置 signingConfigs.release 在 android/app/build.gradle 首先。 iOS 需要 App Store 导出,而不是开发或广告 hoc IPA。检查您的构建步骤中的导出方法。

第 6 阶段: 实时更新

陷阱: 发布一个需要新本机构建的live update

症状: 在实时更新后,应用程序崩溃,调用一个在安装的二进制文件中不存在的插件方法。

原因: web 包依赖于一个版本的插件新于用户设备上的应用程序编译的版本。

解决方案: 让管道决定。 build needed 退出码 0 表示本机依赖项与实时通道上的匹配,1 表示需要新二进制文件:

if bunx @capgo/cli@latest build needed com.example.app --channel production; then
  bunx @capgo/cli@latest bundle upload com.example.app --channel production
else
  bunx cap sync
  bunx @capgo/cli@latest build request com.example.app --platform ios
  bunx @capgo/cli@latest build request com.example.app --platform android
fi

也强制本机路径,当文件位于 ios/, android/或 capacitor.config.* 改变。完整模式在 自动选择 live update 或原生构建, 并且兼容性规则在 原生兼容.

移除最多陷阱的修复

如果您只改变一个东西,请将 iOS 编译和签名从 CI 运行器中移出。 Keychain 设置、 Xcode 升级、 macOS 成本和配置文件安装都从管道中消失,当 Linux 任务将准备好的项目交给 Capgo 构建:

bun install --frozen-lockfile && bun run build
bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release

context 修复CapacitorCI/CD管道中的构建失败.

阶段 1、5 和 6 的陷阱仍然适用,因为它们与您的项目有关,而不是运行器。关于调试失败的工作更多信息,请参见

Error 快速参考 错误信息
新旧 UI 在新构建中 cap sync 在 web 构建之前 阶段 1
No profiles for ... were found 未安装或不匹配的配置文件 阶段 3
errSecInternalComponent 密钥链已锁定 阶段 3
MAC verification failed 密码错误或 OpenSSL 3 .p12 阶段 3
Unsupported class file major version JDK 错误 阶段 2
SDK 上传时过旧 Xcode版本低于26 阶段2
捆绑包版本必须高于 重复使用的构建号 阶段5
版本code已使用 重复使用 versionCode 阶段5
在live update后发生崩溃 原生代码通过无线更新 阶段6
为Capacitor应用提供实时更新

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

来自马丁的人性化支持

立即开始

最新博客文章

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