跳转至正文

Capacitor 的 CI/CD:常见坑点与解决方案

让 Capacitor 构建失败的 CI/CD 常见坑:iOS 签名、macOS runner、Xcode 26、cap sync 过期、versionCode、商店拒审,以及各自的修复方法。

Code 签名身份以及 Capacitor 流水线中失败的 CI 步骤

大多数 Capacitor CI/CD 失败都源于同一份简短清单:iOS code 签名、缺失或过时的 macOS 工具链、未进入原生项目的 Web 构建、重复使用的构建号,以及仅在上传时才暴露的商店要求。每个问题都有已知原因和一次性的修复方案。本指南按流水线阶段对这些陷阱进行分类,并说明你会看到的错误、发生原因以及需要修改的内容。

如果你正在从零开始搭建流水线,请先阅读 为 Capacitor 应用设置 CI/CD ,然后使用此清单来加固它。

阶段 1:构建 Web 层

坑点:原生应用打包了旧的 Web 构建

现象: CI 成功,应用已安装,但显示的是昨天的 UI。

原因: cap sync 会复制 webDir 在那一刻的内容。如果 pipeline 运行 cap sync 在 Web 构建之前,或者 Web 构建写入的文件夹与 webDir 中的 capacitor.config.ts不一致,会导致原生项目残留旧文件。

修复: 务必按此顺序执行步骤,输出文件夹为空就直接失败。

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

请确保 webDir 与 bundler 输出路径一致(dist 针对 Vite, www 针对使用 Ionic 的 Angular, build 以及部分 React 配置)。

坑点:配置中残留 dev server URL

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

原因: server.url 中的 capacitor.config.ts 是为热重载设置的,却被提交了。

修复: 切勿提交 server.url。请从 CI 从不设置的环境变量中读取:

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

坑点:环境变量在错误时机被固化

现象: 生产环境应用连接的是 staging 环境的 API。

原因: Vite、webpack 和 Angular 会在构建时内联环境变量。构建时存在的值 bun run build 会被固化到二进制文件中,以及由同一 job 构建的任何 live update 中。

修复: 请在每个 job 的 web 构建前设置特定环境的环境变量,并为 staging 和 production 分别构建 bundle。不要试图在 cap sync.

坑点:lockfile 版本漂移

现象: CI 中的插件版本与本地机器不一致时,原生编译会因缺少符号而失败。

修复: 提交 lockfile 并使用以下命令安装: bun install --frozen-lockfile (或 npm ci)。固定 @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 功能相关的错误。

原因: 托管 runner 镜像会变化,且 Capacitor 8 有硬性最低要求。

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

修复: 在 pipeline 中显式固定每个版本,而不是依赖 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'

坑点:使用 Apple 已不再接受的 Xcode 版本构建

现象: 上传失败,提示该应用使用了不受支持的 SDK 构建。

原因: 自 2026 年 4 月 28 日起,App Store Connect 要求使用 Xcode 26 和 iOS 26 SDK。自托管 Mac 和旧版 runner 镜像仍默认使用 Xcode 16。

修复: 选择一个 macos-26 镜像,或在 runner 上安装 Xcode 26。详情请参阅 Apple 对 Capacitor 应用的 Xcode 26 要求.

坑点:CocoaPods 与 SPM 混淆

现象: xcodebuild: error: 'App.xcworkspace' does not exist,或者找不到 pods。

原因: 新的 Capacitor 8 项目使用 Swift Package Manager 并构建 ios/App/App.xcodeproj。旧项目使用 CocoaPods 并构建 ios/App/App.xcworkspace 之后 pod install。从旧教程复制的 Pipeline 会假定使用 workspace。

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

坑点:Gradle 升级后 Android 插件构建失败

现象: Namespace not specified, package attribute is deprecated,或来自插件的 build.gradle 在更新 Android Studio 之后。

修复: 在 android/build.gradle中锁定 Android Gradle Plugin 版本,在分支上升级,并优先更新插件。具体错误在 修复 AGP 9 中的 Capacitor 插件构建错误.

阶段 3:Code 签名

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

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

原因: 你的 Mac keychain 里存着证书,Xcode 会替你下载 profile。CI runner 两者都没有,而且它的 keychain 在非交互式会话中是锁定的。

修复: 将证书导入一个临时且已解锁的 keychain,再把 profile 安装到 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/"

使用 identity 名称 Apple Distribution,而不是旧版的 iOS Distribution。fastlane 的 setup_ci 加 match 能自动完成这件事。Capgo Build 把证书和 profile 作为环境变量接收,并在自己的机器上处理 keychain 操作,因此 Linux job 完全不用接触 security.

坑点:证书过期或被吊销

现象: 跑了一年的 pipeline 一夜之间就失败了。

原因: Apple 分发证书和描述文件会在一年后过期。队友在 Xcode 中创建新证书,也可能让 CI 使用的描述文件失效。

修复: 把过期日期加到团队日历里,为 CI 保留一个共享分发证书,并在构建前检查。 bunx @capgo/cli@latest build prescan --platform ios 上传任何内容前,检查证书有效期、密码和描述文件配对。

坑点:Apple ID 登录和双重认证提示

现象: fastlane 会卡住,等待 6 位 code。

修复: 使用 App Store Connect API 密钥(.p8key ID、issuer ID)代替 Apple ID 和密码。它不需要双重认证,并且权限可限定为 App Manager。如果密钥正确但认证仍然失败,请检查 runner 的时钟:token 使用本地时间签名,Apple 会拒绝时间戳有偏差的 token。

坑点:base64 密钥无法解码

现象: MAC verification failed, invalid keystore format,或 base64: invalid input.

原因: 复制 base64 值时带入了换行符,或者 .p12 使用 OpenSSL 3 默认设置创建,macOS 无法读取。

修复: 编码成一行,并使用 -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 keystore 丢失

现象: 由于没人持有 keystore,你无法为更新签名。

修复: 使用 Play App Signing 时,keystore 就是上传密钥,Play Console 支持团队可以注册新的上传密钥。请把 keystore 存到 CI secrets 和离线备份中。别让它只留在一台笔记本上。 Android keystore 生成器 如果你从零开始,它会新建一个。

阶段 4:Pipeline 设计

坑点:每个 Pull Request 都跑 Native Build

现象: PR 检查缓慢,macOS 分钟数账单高昂。

原因: 在托管 macOS runner 上跑 iOS 构建,是大多数 CI 套餐里最贵的分钟数,而大多数提交只改 JavaScript。

修复: 每个 PR 都运行 lint、测试和 web build。在 release tag 或合并到 main时运行 native build。PR 预览时,将 web bundle 发布到 Capgo channel,而不是构建二进制文件,如 Capacitor 应用的 CI/CD 平台对比.

坑点:串行构建 iOS 和 Android

修复: 所述。用 matrix 让两个平台并行构建,并设置 fail-fast: false ,这样 iOS 签名问题就不会取消正常的 Android 构建。

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

坑点:未配置缓存或缓存配置错误

现象: 每次构建都要从头下载 Gradle 依赖和 CocoaPods,或者 staging 构建会因缓存过期而发布生产配置。

修复: 缓存 ~/.gradle/caches, ~/.gradle/wrapper,以及 ios/App/Pods 基于 lockfiles 进行键控。按环境划分缓存。使用 Capgo Build,可以通过以下方式拆分每个应用的构建缓存: --cache-key prod 以及 --cache-key staging,或通过以下方式跳过 --no-cache 以进行 clean build。

坑点:Monorepo 路径问题

现象: could not find capacitor.config 或原生项目中缺失的插件。

修复: 从 app 包中运行 Capacitor 命令,并让工具指向提升后的 node_modules。Capgo CLI 支持 --path 以及 --node-modules 来实现这一点。

阶段 5:提交至应用商店

坑点:重复使用 Build Number

现象: 在 iOS 上会提示“The bundle version must be higher than the previously uploaded version”,或在 Google Play 上提示“Version code has already been used”。

修复: 在 CI 中生成版本号。使用 fastlane 时,读取最新的 TestFlight build 并加 1。使用 Capgo Build 时这是默认行为:它会从 App Store Connect 获取最新 build number,或取 Google Play 上最高 versionCode 版本号并递增。

坑点:构建卡在 TestFlight 中

现象: upload 成功,但测试人员始终看不到 build。

原因: 缺少 export compliance 答案。

修复: 只需在...中声明一次 ios/App/App/Info.plist 如果你只使用标准加密:

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

坑点:隐私清单被拒

现象: 来自 Apple 的邮件,指出缺少必需的 reason API 声明 (ITMS-91053)。

修复: 添加一个 PrivacyInfo.xcprivacy 到 app target,并更新自带隐私清单的插件。请参阅 iOS 应用隐私清单指南.

坑点:构建产物类型错误

修复: Google Play 需要 AAB (bundleRelease),而不是 APK。 bundleRelease 仍然会成功,但如果没有 release 签名配置,会生成一个被 Play 拒绝的未签名 AAB,因此请配置 signingConfigs.release 中的 android/app/build.gradle 。iOS 需要 App Store 导出,而不是开发版或 ad hoc IPA。请检查构建步骤中的导出方式。

第 6 阶段:Live Updates

坑点:发布一个需要新原生构建的 live update

现象: OTA 更新后,应用调用已安装二进制中不存在的插件方法时会崩溃。

原因: web bundle 依赖的插件版本比用户设备上应用内编译的版本更新。

修复: 让 pipeline 来决定。 build needed 当原生依赖与 channel 上 Live Update 的内容匹配时退出码为 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 或 Native Build,兼容性规则见 原生兼容性.

消除最多陷阱的修复方案

如果只改一件事,请将 iOS 编译和签名从 CI 运行器中移出。当 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

阶段 1、5 和 6 中的陷阱仍然适用,因为它们与项目本身有关,而非运行器。有关调试失败任务的更多信息,请参阅 修复 Capacitor CI/CD 流水线中的构建失败.

快速参考

错误 陷阱 章节
新构建中的旧 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
Bundle 版本必须更高 Build number 重复 阶段 5
版本 code 已被使用 重复使用 versionCode 阶段 5
live update 后崩溃 原生变更通过空中推送发布 阶段 6
为 Capacitor 应用提供 Live Updates

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

博客最新动态

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