2026 年,Cordova 应用的 CI/CD 就是在 Linux runner 上构建并签名 Android AAB,在 macOS runner 上构建并签名 iOS IPA,再从同一条 pipeline 把两者上传到应用商店。Cordova 团队过去用的托管服务已经停运或即将关停,所以大多数团队现在都在通用 CI 上跑 Cordova CLI。本指南给出一套可用的 GitHub Actions 配置、其他 CI 上的相同步骤,并客观分析 Live Updates 和长期方案。
Cordova 团队面临的变化
三件事塑造了 Cordova CI/CD:
- PhoneGap Build 已于 2020 年 10 月 1 日停止服务。
- Microsoft App Center 已于 2025 年 3 月 31 日退役。Build、分发以及托管的 CodePush 服务随之终止。
- Ionic Appflow 替代方案 已于 2025 年 2 月停止新销售。现有客户可继续使用至 2027 年 12 月 31 日。
Cordova 本身仍然可用。Apache 项目发布 cordova-android 以及 cordova-ios 发布,以及大量内部和企业应用都运行在其上。缺失的是托管层:签名存储、商店上传和空中更新。现在你需要从 CI 服务和几个工具中自行组装这些功能。
Cordova 流水线解析
| 阶段 | Android | iOS |
|---|---|---|
| Runner | Linux | macOS 与 Xcode 26 |
| 恢复 | cordova prepare (来自 package.json) |
相同的 |
| Web 构建 | 你的打包工具(Vite、webpack、Angular CLI)到 www/ |
相同的 |
| Native Build | 通过 Gradle cordova build android --release |
xcodebuild 通过 cordova build ios --release --device |
| 签名 | 上传 keystore (.jks) |
分发证书 (.p12) 和 provisioning profile |
| 构建产物 | .aab 用于 Google Play |
.ipa 用于 TestFlight 和 App Store |
| 上传 | Google Play Developer API (服务账号) | App Store Connect API 密钥 |
每条流水线都适用两条平台规则:
- 自 2026 年 4 月 28 日起,App Store Connect 仅接受使用 Xcode 26 和 iOS 26 SDK 或更高版本构建的版本。请确认你的
cordova-ios版本支持该要求。 - Google Play 要求新应用必须上传 AAB,并且每年都会提高最低 target API 级别。请保持
cordova-android最新,以便其默认targetSdkVersion行为随之生效。
第 1 步:确保项目可复现
CI 只能重新构建仓库里已有的内容。在编写任何 workflow 之前:
- 提交
package.json以及 lockfile。Cordova 将 platforms 和 plugins 存储在cordova的……部分package.json,因此cordova prepare恢复它们。 - 不要提交
platforms/或plugins/。它们是自动生成的。 - 将 Cordova CLI 作为 dev dependency 安装,以确保每台机器都使用相同的版本:
bun add -d cordova
- 将签名值移出
config.xml并移入一个build.json中,该文件会在构建时读取 secrets(见下一步)。 - 在以下文件中固定 Node.js 版本:
.nvmrc或package.jsonengines.
第 2 步:使用 build.json 签名
Cordova 会从以下文件读取签名设置: build.json 文件。提交一个 build.template.json 带占位符,生成真实的 build.json 在 CI 中通过 secrets 生成,且永远不要提交生成的文件:
{
"android": {
"release": {
"keystore": "release.jks",
"storePassword": "${KEYSTORE_STORE_PASSWORD}",
"alias": "upload",
"password": "${KEYSTORE_KEY_PASSWORD}",
"packageType": "bundle"
}
},
"ios": {
"release": {
"codeSignIdentity": "Apple Distribution",
"developmentTeam": "ABCDE12345",
"packageType": "app-store",
"provisioningProfile": "com.example.app AppStore",
"automaticProvisioning": false
}
}
}
本指南中介绍的 ${...} Cordova 不会展开占位符。下面的 workflow 会用以下内容写入真实值 envsubst.
如果你还没有 Android keystore,可以用以下命令创建 keytool 或基于浏览器的 Android keystore 生成器。对于 iOS, iOS 证书生成器 会创建证书签名请求并 .p12 无需 Mac。
第 3 步:GitHub Actions 工作流
该工作流会根据版本标签并行构建两个平台,并将它们上传到内部测试轨道。
# .github/workflows/cordova-release.yml
name: Cordova release
on:
push:
tags: ['v*']
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- run: bun install --frozen-lockfile
- run: bun run build # outputs to www/
- run: bunx cordova prepare android
- name: Signing files
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
run: |
echo "$KEYSTORE_BASE64" | base64 --decode > release.jks
envsubst < build.template.json > build.json
- run: bunx cordova build android --release --buildConfig=build.json
- uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.example.app
releaseFiles: platforms/android/app/build/outputs/bundle/release/app-release.aab
track: internal
status: completed
ios:
runs-on: macos-26
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bunx cordova prepare ios
- name: Install certificate and profile
env:
P12_BASE64: ${{ secrets.IOS_P12_BASE64 }}
P12_PASSWORD: ${{ secrets.IOS_P12_PASSWORD }}
PROFILE_BASE64: ${{ secrets.IOS_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo "$P12_BASE64" | base64 --decode > dist.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -lut 21600 build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security import dist.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security list-keychains -d user -s build.keychain login.keychain
for dir in "$HOME/Library/MobileDevice/Provisioning Profiles" \
"$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"; do
mkdir -p "$dir"
echo "$PROFILE_BASE64" | base64 --decode > "$dir/app.mobileprovision"
done
cp build.template.json build.json # iOS values hold no secrets
- run: bunx cordova build ios --release --device --buildConfig=build.json
- name: Upload to TestFlight
env:
API_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
API_ISSUER: ${{ secrets.APPLE_ISSUER_ID }}
API_KEY_BASE64: ${{ secrets.APPLE_KEY_CONTENT }}
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$API_KEY_BASE64" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8
IPA=$(find platforms/ios/build -name '*.ipa' | head -n 1)
xcrun altool --upload-app -f "$IPA" -t ios --apiKey "$API_KEY_ID" --apiIssuer "$API_ISSUER"
- name: Clean up keychain
if: always()
run: security delete-keychain build.keychain || true
部分代码行的作用:
- 两个 provisioning profile 文件夹。 Xcode 16 及更高版本从以下位置读取 profile:
~/Library/Developer/Xcode/UserData/Provisioning Profiles。旧版 Xcode 使用:~/Library/MobileDevice/Provisioning Profiles。同时写入两者可避免出现 “No profiles for ‘com.example.app’ were found” 错误。 set-key-partition-list防止 macOS 弹出 keychain 访问提示,否则会导致 CI 任务挂起。- JDK 版本。 请使用你的
cordova-android发布文档中指定的 JDK。最近的版本使用 JDK 17 构建。 - Build 号。 提升
android-versionCode以及ios-CFBundleVersion中的config.xml在构建之前,例如使用 CI 运行编号,否则商店会因重复上传而拒绝。
你可以把 altool upload 替换为 fastlane 的 upload_to_testflight 并把 Play action 替换为 fastlane supply 如果你的团队已经在用 fastlane。
步骤 4:在其他 CI 服务上使用同一 pipeline
命令在不同 CI 之间不变。只有 runner 选择和 secret 语法会变。
| CI | Android 运行器 | iOS runner | 备注 |
|---|---|---|---|
| GitHub 操作 | ubuntu-latest |
macos-26 / macos-latest |
私有仓库中 macOS 分钟数比 Linux 更贵 |
| GitLab CI | 任意 Linux runner | 托管 macOS runner(beta、Premium 和 Ultimate)或自托管 | 例如镜像 macos-26-xcode-26 |
| Bitbucket Pipelines | Linux Docker 镜像 | 仅限自托管 Mac | 查看 Bitbucket iOS 指南 |
| Gitea / Forgejo | Linux 运行器 | host 模式下的自托管 Mac | 查看 Gitea iOS 指南 |
| Bitrise | Linux 构建栈 | macOS 构建栈 | 内置 Cordova 步骤 (prepare, archive) |
| Codemagic | Linux | Mac mini 实例 | 记录了 Ionic Cordova 构建,详见 codemagic.yaml |
| Azure DevOps | ubuntu-latest |
macOS-latest |
用于安装证书和描述文件的任务 |
如需逐项查看功能,请阅读 Cordova 应用 CI/CD 平台对比.
第 5 步:Cordova 的 Live Updates
Live Updates 让你无需经过应用商店审核即可发布 HTML、CSS 和 JavaScript 修复。App Center CodePush 已于 2025 年 3 月停止服务,Appflow Live Updates 将于 2027 年 12 月 31 日结束,而旧的社区热 code 推送插件也已无人维护。仍在维护的选择是 Capgo 实时更新 配合 @capgo/cordova-updater 插件使用,它与 Capacitor 更新器使用相同的后端、通道和 CLI。
要求
- Cordova CLI 12+
cordova-android13+ 和/或cordova-ios7+(该插件依赖其默认https://localhost/以及app://localhost/schemes)- 请勿使用
cordova-plugin-ionic-webview配合使用。Ionic WebView 会绕过 Cordova 的 scheme 处理器,导致下载的 bundle 无法加载。
安装插件
使用与 Capacitor 相同的方式在 Capgo 中创建应用,然后使用您的 Capgo app ID 安装插件:
cordova plugin add @capgo/cordova-updater \
--variable APP_ID=com.example.app \
--variable DEFAULT_CHANNEL=production
cordova prepare android ios
DEFAULT_CHANNEL, UPDATE_URL,以及 AUTO_UPDATE 是可选的安装变量。运行 cordova prepare ,每当更改这些变量后,请重新运行。
每次启动时确认
在 deviceready之后,该插件可通过 cordova.plugins.Updater. 调用 notifyAppReady() 在每次启动时调用,否则插件会将新 bundle 视为损坏并回滚:
document.addEventListener('deviceready', async () => {
const { Updater } = cordova.plugins
await Updater.notifyAppReady()
const latest = await Updater.getLatest()
if (latest.url && !latest.error) {
const bundle = await Updater.download({
url: latest.url,
version: latest.version,
checksum: latest.checksum,
})
await Updater.next({ id: bundle.id })
}
})
从 CI 上传 bundle
针对仅 Web 层的变更,添加一个构建 Web 层并上传的任务:
live-update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- run: bun run build
- run: bunx @capgo/cli@latest bundle upload --channel=production
env:
CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
Live Updates 仅替换 Web bundle。添加、删除或升级 Cordova 插件仍然需要新的 store build。Capgo 会将为 live bundle 记录的原生插件与你的项目对比,并标记不兼容的上传;详见 live update 兼容性。完整设置请参阅 Cordova updater 文档.
在构建侧, Capgo 构建 文档仅涵盖 Capacitor 项目(它会上传由 cap sync生成的原生项目)。如上所示,Cordova 二进制文件仍会在你的 CI 上继续构建。
可选:之后再迁移到 Capacitor
你不需要离开 Cordova 也能获得可用的 pipeline 和 Live Updates。迁移是另一个决定,在以下情况下值得做:
- 你希望做 iOS 构建,又不想维护 macOS runner。Capgo Build 可在 Linux CI 任务中编译 Capacitor 应用。
- 你依赖的插件不再接收 Cordova 更新,但有 Capacitor 对应版本。
- 你希望支持 Swift Package Manager 和当前的 Android Gradle Plugin 版本,而无需修补平台。
迁移会保留你的 www code。你添加 Capacitor,把插件迁移过来(大多数 Cordova 插件可直接使用),再替换少数有 Capacitor 原生替代品的插件。你的 Capgo 应用 ID 和 channels 保持不变;只有 updater 插件会从 @capgo/cordova-updater 改为 @capgo/capacitor-updater。具体步骤见 从 Cordova 迁移到 Capacitor,以及 Cordova 迁移到 Capacitor 方案页 涵盖如何获取相关帮助。迁移后,上述 CI 会缩减为 Web build, cap sync,以及 build request 每个平台调用一次,详见 Capacitor 应用的 CI/CD 平台对比.
排查 CI 中的 Cordova build 问题
| 错误 | 原因与修复 |
|---|---|
Current working directory is not a Cordova-based project |
config.xml 或 www/ 缺失。请在执行 Cordova 命令前运行 Web build,或创建一个空的 www/. |
No platforms added to this project |
平台未列在 package.json中。运行 cordova platform add 一次并 commit package.json. |
Could not find an installed version of Gradle |
最近 cordova-android 使用 Gradle wrapper。请更新平台,而不是全局安装 Gradle。 |
Unsupported class file major version |
Runner 上的 JDK 与 cordova-android 期望的不匹配。请使用 setup-java. |
No signing certificate "iOS Distribution" found |
使用 Apple Distribution 作为身份,并检查 .p12 包含私钥。 |
errSecInternalComponent |
钥匙串已锁定或缺少分区列表。请重新运行钥匙串配置命令。 |
| 上传被拒绝:SDK 版本过旧 | Runner 镜像使用的 Xcode 低于 26。请选择 macos-26 或用以下方式设置 Xcode xcode-select. |
Play 上传: Version code has already been used |
提升 android-versionCode 用于每次上传。 |
总结
2026 年的 Cordova 流水线是每个平台一个 CI 任务:Linux runner 用于 Android AAB,配备 Xcode 26 的 macOS runner 用于 IPA,密钥解码到生成的 build.json,以及两个商店的 API 密钥。任何主流 CI 都可以运行它。添加 @capgo/cordova-updater 以及 bundle upload 处理纯 Web 修复的任务,同时拥有 Appflow 和 App Center 曾提供的构建、商店和空中下载(OTA)功能。迁移到 Capacitor 是后续可选项,而非强制要求。