跳转至正文

2026 年 Cordova 应用 CI/CD 完整配置指南

2026 年为 Cordova 应用搭建 CI/CD:签名的 Android AAB 和 iOS IPA 构建、应用商店上传、live update 选项,以及何时迁移到 Capacitor。

Apache Cordova 应用通过 CI/CD 流水线发布到 App Store 和 Google Play

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 之前:

  1. 提交 package.json 以及 lockfile。Cordova 将 platforms 和 plugins 存储在 cordova 的……部分 package.json,因此 cordova prepare 恢复它们。
  2. 不要提交 platforms/ 或 plugins/。它们是自动生成的。
  3. 将 Cordova CLI 作为 dev dependency 安装,以确保每台机器都使用相同的版本:
bun add -d cordova
  1. 将签名值移出 config.xml 并移入一个 build.json 中,该文件会在构建时读取 secrets(见下一步)。
  2. 在以下文件中固定 Node.js 版本: .nvmrc 或 package.json engines.

第 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-android 13+ 和/或 cordova-ios 7+(该插件依赖其默认 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 是后续可选项,而非强制要求。

为 Capacitor 应用提供 Live Updates

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

博客最新动态

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