Zum Hauptinhalt springen

CI/CD für Cordova-Apps 2026: Der komplette Setup-Guide

CI/CD für Cordova-Apps 2026 einrichten: signierte Android-AAB- und iOS-IPA-Builds, Store-Uploads, Optionen für live update und wann du zu Capacitor wechseln solltest.

Apache Cordova App, die durch eine CI/CD-Pipeline in den App Store und Google Play gelangt

CI/CD für eine Cordova-App bedeutet 2026, das Android-AAB auf einem Linux-Runner zu bauen und zu signieren, das iOS-IPA auf einem macOS-Runner zu bauen und zu signieren und beides aus derselben Pipeline in die Stores hochzuladen. Die Managed Services, die Cordova-Teams dafür genutzt haben, gibt es nicht mehr oder sie werden eingestellt, deshalb lassen die meisten Teams den Cordova CLI inzwischen auf einer allgemeinen CI laufen. Dieser Guide zeigt dir ein funktionierendes GitHub Actions Setup, dieselben Schritte für andere CIs und einen ehrlichen Blick auf Live Updates und langfristige Optionen.

Was sich für Cordova-Teams geändert hat

Drei Ereignisse prägten Cordova CI/CD:

  • PhoneGap Build wurde am 1. Oktober 2020 eingestellt.
  • Microsoft App Center wurde am 31. März 2025 eingestellt. Damit endeten auch Build, Distribution und der gehostete CodePush-Service.
  • Ionic Appflow hat im Februar 2025 den Verkauf eingestellt. Bestandskunden behalten ihren Zugriff bis zum 31. Dezember 2027.

Cordova selbst funktioniert weiterhin. Das Apache-Projekt liefert cordova-android und cordova-ios Releases, und viele interne sowie Enterprise-Apps laufen darauf. Die Lücke liegt bei der Managed Layer: Signing-Speicher, Store-Uploads und Over-the-Air-Updates. Diese stellst du dir heute aus einem CI-Service und ein paar Tools selbst zusammen.

Anatomie einer Cordova-Pipeline

Phase Android iOS
Runner Linux macOS mit Xcode 26
Restore cordova prepare (Plattformen und Plugins von package.json) gleich
Web-Build deinem Bundler (Vite, webpack, Angular CLI) in www/ gleich
Native Build Gradle über cordova build android --release xcodebuild über cordova build ios --release --device
Signing Keystore hochladen (.jks) Distributionszertifikat (.p12) und Provisioning Profile
Artefakt .aab für Google Play .ipa für TestFlight und den App Store
Upload Google Play Developer API (Service Account) App Store Connect API Key

Für jede Pipeline gelten zwei Plattformregeln:

  • Seit dem 28. April 2026 akzeptiert App Store Connect nur noch Builds, die mit Xcode 26 und iOS 26 SDK oder neuer erstellt wurden. Prüfe, ob deine cordova-ios Version das unterstützt.
  • Google Play verlangt AAB-Uploads für neue Apps und hebt jedes Jahr das Mindest-API-Level an. Halte cordova-android aktuell, damit der Standard targetSdkVersion mitzieht.

Schritt 1: Projekt reproduzierbar machen

CI kann nur neu bauen, was im Repository liegt. Bevor du Workflows schreibst:

  1. Commit package.json und die Lockfile. Cordova speichert Plattformen und Plugins im cordova Abschnitt von package.json, also cordova prepare stellt sie wieder her.
  2. Nicht committen platforms/ oder plugins/. Sie werden generiert.
  3. Installiere das Cordova CLI als dev dependency, damit jeder Rechner dieselbe Version verwendet:
bun add -d cordova
  1. Verschiebe die Signing-Werte aus config.xml und in eine build.json die Secrets zur Build-Zeit liest (nächster Schritt).
  2. Pinne die Node.js-Version in .nvmrc oder package.json engines.

Schritt 2: Signierung mit build.json

Cordova liest die Signierungseinstellungen aus einer build.json Datei. Committe eine build.template.json mit Platzhaltern, generiere die echte build.json in der CI aus Secrets und committe die generierte Datei niemals:

{
  "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
    }
  }
}

Der ${...} Platzhalter werden von Cordova nicht aufgelöst. Der folgende Workflow schreibt die echten Werte mit envsubst.

Falls du noch keinen Android Keystore hast, erstelle einen mit keytool oder dem browserbasierten Android Keystore Generator. Für iOS erstellt der iOS-Zertifikatsgenerator einen Certificate Signing Request und .p12 ohne einen Mac.

Schritt 3: Ein GitHub Actions Workflow

Dieser Workflow baut beide Plattformen parallel auf einem Versions-Tag und lädt sie in die internen Testing-Tracks hoch.

# .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

Warum manche Zeilen enthalten sind:

  • Zwei Provisioning-Profile-Ordner. Xcode 16 und neuer lesen Profile aus ~/Library/Developer/Xcode/UserData/Provisioning Profiles. Ältere Xcode-Versionen nutzten ~/Library/MobileDevice/Provisioning Profiles. Beide zu schreiben verhindert die Meldung „No profiles for ‘com.example.app’ were found“.
  • set-key-partition-list verhindert, dass macOS nach dem Keychain-Zugriff fragt, was einen CI-Job hängen lassen würde.
  • JDK-Version. Nutze das JDK, das deine cordova-android Release-Dokumentation vorgibt. Aktuelle Releases bauen mit JDK 17.
  • Build-Nummern. Erhöhen android-versionCode und ios-CFBundleVersion in einem config.xml vor dem Build, zum Beispiel anhand der CI-Run-Nummer, sonst lehnen die Stores den Upload als Duplikat ab.

Du kannst den altool Upload gegen fastlanes upload_to_testflight ersetzen und die Play-Action durch fastlane, supply falls dein Team bereits fastlane nutzt.

Schritt 4: Dieselbe Pipeline auf anderen CI-Services

Die Commands ändern sich zwischen den CIs nicht. Nur die Runner-Auswahl und die Secret-Syntax variieren.

CI Android-Runner iOS-Runner Notizen
GitHub Aktionen ubuntu-latest macos-26 / macos-latest macOS-Minuten kosten in privaten Repos mehr als Linux-Minuten
GitLab CI jeder Linux-Runner gehostete macOS-Runner (Beta, Premium und Ultimate) oder self-hosted Images wie macos-26-xcode-26
Bitbucket Pipelines Linux-Docker-Image nur self-hosted Mac siehe Bitbucket-iOS-Anleitung
Gitea / Forgejo Linux-Runner self-hosted Mac im Host-Modus siehe Gitea-iOS-Anleitung
Bitrise Linux-Stacks macOS-Stacks liefert Cordova-Schritte (prepare, archive)
Codemagic Linux Mac-mini-Instanzen dokumentiert Ionic Cordova Builds in codemagic.yaml
Azure DevOps ubuntu-latest macOS-latest Tasks zur Installation von Zertifikaten und Profilen

Für eine Feature-für-Feature-Übersicht lies Vergleich von CI/CD-Plattformen für Cordova-Apps.

Schritt 5: Live Updates für Cordova

Mit Live Updates kannst du HTML-, CSS- und JavaScript-Fixes ohne Store-Review ausrollen. App Center CodePush wurde im März 2025 eingestellt, Appflow Live Updates enden am 31. Dezember 2027, und die alten Hot-code-Push-Plugins aus der Community werden nicht mehr gepflegt. Eine gepflegte Option ist Capgo Live Updates mit dem @capgo/cordova-updater Plugin, das dasselbe Backend, dieselben Channels und dieselben CLI wie der Capacitor Updater nutzt.

Anforderungen

  • Cordova CLI 12+
  • cordova-android 13+ und/oder cordova-ios 7+ (das Plugin setzt auf deren Standard- https://localhost/ und app://localhost/ Schemes)
  • Nicht verwenden cordova-plugin-ionic-webview damit. Die Ionic WebView umgeht Cordovas Scheme-Handler, sodass heruntergeladene Bundles nicht geladen würden.

Plugin installieren

Erstelle die App in Capgo genauso wie für Capacitor, und installiere dann das Plugin mit deiner 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, und AUTO_UPDATE sind optionale Installationsvariablen. Führe cordova prepare erneut aus, sobald du sie änderst.

Jeden Start bestätigen

Danach deviceready, das Plugin ist verfügbar als cordova.plugins.Updater. Aufrufen notifyAppReady() bei jedem Start, oder das Plugin behandelt das neue Bundle als defekt und rollt zurück:

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 })
  }
})

Bundles aus CI hochladen

Füge einen Job hinzu, der die Web-Schicht baut und hochlädt, für web-spezifische Änderungen:

  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 ersetzen nur das Web-Bundle. Das Hinzufügen, Entfernen oder Aktualisieren eines Cordova-Plugins erfordert weiterhin einen neuen Store-Build. Capgo vergleicht die für das Live-Bundle erfassten nativen Plugins mit deinem Projekt und markiert inkompatible Uploads; siehe live update-Kompatibilität. Die vollständige Einrichtung findest du in der Cordova-Updater-Docs.

Auf der Build-Seite ist die Capgo Build Dokumentation deckt nur Capacitor-Projekte ab (sie lädt das native Projekt hoch, das von cap sync) erstellt wird. Cordova-Binaries werden weiterhin auf deiner CI gebaut, wie oben gezeigt.

Optional: Später zu Capacitor migrieren

Du musst Cordova nicht verlassen, um eine funktionierende Pipeline und Live Updates zu erhalten. Eine Migration ist eine separate Entscheidung, die sich lohnt, wenn:

  • Du möchtest iOS-Builds, ohne macOS-Runner pflegen zu müssen. Capgo Build kompiliert Capacitor-Apps aus einem Linux-CI-Job.
  • Plugins, von denen du abhängst, erhalten keine Cordova-Updates mehr, haben aber Capacitor-Äquivalente.
  • Du Swift Package Manager-Unterstützung und aktuelle Android Gradle Plugin-Versionen möchtest, ohne Plattformen patchen zu müssen.

Die Migration behält dein www code. Du fügst Capacitor hinzu, migrierst die Plugins (die meisten Cordova-Plugins funktionieren so wie sie sind) und ersetzt einige durch Capacitor-native Äquivalente. Deine Capgo-App-ID und Kanäle bleiben gleich; nur das Updater-Plugin wechselt von @capgo/cordova-updater zu @capgo/capacitor-updater. Die Schritte findest du in Migration von Cordova zu Capacitor, und die Cordova zu Capacitor Lösung deckt die Unterstützung ab. Nach der Migration reduziert sich die CI oben auf einen Web-Build, cap sync, und ein build request Aufruf pro Plattform, wie in Vergleich von CI/CD-Plattformen für Capacitor-Apps.

Cordova-Builds in CI Troubleshooting

Fehler Ursache und Lösung
Current working directory is not a Cordova-based project config.xml oder www/ fehlt. Führe deinen Web-Build vor den Cordova-Befehlen aus oder erstelle eine leere www/.
No platforms added to this project Plattformen sind nicht aufgeführt in package.json. Ausführen cordova platform add einmal lokal ausführen und committen package.json.
Could not find an installed version of Gradle Zuletzt cordova-android nutzt den Gradle Wrapper. Aktualisiere die Plattform, statt Gradle global zu installieren.
Unsupported class file major version Das JDK auf dem Runner stimmt nicht mit dem überein, das cordova-android erwartet. Fixiere es mit setup-java.
No signing certificate "iOS Distribution" found Nutzt Apple Distribution als Identität und prüfe, ob der .p12 den privaten Schlüssel enthält.
errSecInternalComponent Keychain gesperrt oder Partition List fehlt. Führe die Keychain-Setup-Befehle erneut aus.
Upload abgelehnt: SDK zu alt Runner-Image nutzt Xcode älter als 26. Wählen Sie macos-26 oder setzen Sie Xcode mit xcode-select.
Play-Upload: Version code has already been used Erhöhen android-versionCode für jeden Upload.

Zusammenfassung

Eine Cordova-Pipeline im Jahr 2026 ist ein CI-Job pro Plattform: ein Linux-Runner für das Android-AAB, ein macOS-Runner mit Xcode 26 für die IPA, Secrets, die in eine generierte build.json, und API -Keys für beide Stores. Jeder gängige CI kann es ausführen. Füge hinzu @capgo/cordova-updater und einen bundle upload Job für Web-only-Fixes, und du hast die Build-, Store- und Over-the-Air-Komponenten, die Appflow und App Center früher bereitgestellt haben. Der Wechsel zu Capacitor bleibt eine Option für später, keine Pflicht.

Live-Updates für Capacitor-Apps

Wenn ein Bug im Web-Layer live ist, liefere den Fix über Capgo aus, statt Tage auf die App-Store-Freigabe zu warten. Nutzer bekommen das Update im Hintergrund, während native Änderungen im normalen Review-Prozess bleiben.

Neues aus unserem Blog

Capgo gibt dir die besten Insights, um eine wirklich professionelle Mobile App zu erstellen.