CI/CD untuk aplikasi Cordova di 2026 berarti membangun dan menandatangani Android AAB di runner Linux, membangun dan menandatangani iOS IPA di runner macOS, serta mengunggah keduanya ke store dari pipeline yang sama. Layanan terkelola yang dulu digunakan tim Cordova untuk ini sudah tutup atau akan ditutup, sehingga sebagian besar tim kini menjalankan CLI Cordova di CI umum. Panduan ini menyediakan setup GitHub Actions yang berfungsi, langkah serupa untuk CI lain, serta pandangan jujur tentang live update dan opsi jangka panjang.
Apa yang berubah bagi tim Cordova
Tiga peristiwa membentuk CI/CD Cordova:
- PhoneGap Build dihentikan pada 1 Oktober 2020.
- Microsoft App Center dipensiunkan pada 31 Maret 2025. Build, distribusi, dan layanan CodePush yang dihosting berakhir bersamanya.
- Ionic Appflow menghentikan penjualan baru pada Februari 2025. Pelanggan yang ada tetap memiliki akses hingga 31 Desember 2027.
Cordova itu sendiri masih berfungsi. Proyek Apache merilis cordova-android dan cordova-ios releases, dan banyak aplikasi internal serta enterprise berjalan di atasnya. Kesenjangan ada di lapisan terkelola: penyimpanan signing, upload ke store, dan pembaruan over-the-air. Anda kini merakit semuanya dari layanan CI ditambah beberapa tool.
Anatomi pipeline Cordova
| Tahap | Android | iOS |
|---|---|---|
| Runner | Linux | macOS dengan Xcode 26 |
| Pulihkan | cordova prepare (platform dan plugin dari package.json) |
sama |
| Build web | bundler Anda (Vite, webpack, Angular CLI) ke dalam www/ |
sama |
| Native Build | Gradle via cordova build android --release |
xcodebuild melalui cordova build ios --release --device |
| Signing | upload keystore (.jks) |
sertifikat distribusi (.p12) dan provisioning profile |
| Artifact | .aab untuk Google Play |
.ipa untuk TestFlight dan App Store |
| Unggah | Google Play Developer API (service account) | API key App Store Connect |
Dua aturan platform berlaku untuk setiap pipeline:
- Sejak 28 April 2026, App Store Connect hanya menerima build yang dibuat dengan Xcode 26 dan SDK iOS 26 atau lebih baru. Pastikan
cordova-iosversi Anda mendukungnya. - Google Play mewajibkan upload AAB untuk aplikasi baru, dan menaikkan level target API minimum setiap tahun. Jaga
cordova-androidsaat ini, jadi ini defaulttargetSdkVersionfollows.
Langkah 1: Buat proyek yang reproducible
CI hanya dapat membangun ulang apa yang ada di repository. Sebelum menulis workflow apa pun:
- Commit
package.jsondan lockfile. Cordova menyimpan platform dan plugin dicordovabagianpackage.json, jadicordova preparememulihkan mereka. - Jangan commit
platforms/atauplugins/. Mereka dihasilkan. - Pasang Cordova CLI sebagai dev dependency agar setiap mesin menggunakan versi yang sama:
bun add -d cordova
- Pindahkan nilai signing dari
config.xmlkebuild.jsonyang membaca secrets saat build (langkah berikutnya). - Tetapkan versi Node.js di
.nvmrcataupackage.jsonengines.
Langkah 2: Signing dengan build.json
Cordova membaca pengaturan signing dari build.json file. Commit build.template.json dengan placeholder, generate file asli build.json di CI dari secrets, dan jangan pernah commit file yang di-generate:
{
"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
}
}
}
Panduan ${...} Placeholder tidak di-expand oleh Cordova. Workflow di bawah menulis nilai asli dengan envsubst.
Jika Anda belum memiliki Android keystore, buat satu dengan keytool atau yang berbasis browser Generator keystore Android. Untuk iOS, iOS certificate generator kami membuat certificate signing request dan .p12 tanpa Mac.
Langkah 3: Workflow GitHub Actions
Workflow ini membangun kedua platform secara paralel pada version tag dan meng-upload ke internal testing tracks.
# .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
Mengapa beberapa baris ada:
- Dua folder provisioning profile. Xcode 16 dan versi lebih baru membaca profile dari
~/Library/Developer/Xcode/UserData/Provisioning Profiles. Xcode lama menggunakan~/Library/MobileDevice/Provisioning Profiles. Menulis keduanya menghindari “No profiles for ‘com.example.app’ were found”. set-key-partition-listmencegah macOS meminta akses keychain, yang akan membuat CI job hang.- Versi JDK. Gunakan JDK yang
cordova-androiddidokumentasikan dalam rilis Anda. Rilis terbaru dibangun dengan JDK 17. - Nomor build. Naikkan
android-versionCodedanios-CFBundleVersiondiconfig.xmlsebelum build, misalnya dari nomor run CI, atau store menolak upload karena duplikat.
Anda dapat mengganti altool upload untuk fastlane upload_to_testflight dan aksi Play dengan fastlane supply jika tim Anda sudah menggunakan fastlane.
Langkah 4: Pipeline yang sama di layanan CI lain
Perintahnya tidak berubah antar CI. Hanya pemilihan runner dan sintaks secret yang berbeda.
| CI | Runner Android | Runner iOS | Catatan |
|---|---|---|---|
| Aksi GitHub | ubuntu-latest |
macos-26 / macos-latest |
Menit macOS lebih mahal daripada Linux di repo privat |
| GitLab CI | runner Linux apa pun | runner macOS hosted (beta, Premium, dan Ultimate) atau self-hosted | image seperti macos-26-xcode-26 |
| Bitbucket Pipelines | Image Docker Linux | Hanya Mac self-hosted | lihat Panduan Bitbucket iOS |
| Gitea / Forgejo | Runner Linux | Mac self-hosted dalam mode host | lihat Panduan Gitea iOS |
| Bitrise | Stack Linux | Stack macOS | menyertakan langkah Cordova (prepare, archive) |
| Codemagic | Linux | Instansi Mac mini | mendokumentasikan build Ionic Cordova di codemagic.yaml |
| Azure DevOps | ubuntu-latest |
macOS-latest |
tugas untuk menginstal sertifikat dan profil |
Untuk tampilan per fitur, baca Membandingkan platform CI/CD untuk aplikasi Cordova.
Langkah 5: Live updates untuk Cordova
Live updates memungkinkan Anda mengirim perbaikan HTML, CSS, dan JavaScript tanpa review store. App Center CodePush berakhir pada Maret 2025, Appflow Live Updates berakhir pada 31 Desember 2027, dan plugin hot code push komunitas lama tidak lagi dipelihara. Opsi yang dipelihara adalah Live updates Capgo dengan @capgo/cordova-updater plugin, yang menggunakan backend, channel, dan CLI yang sama dengan updater Capacitor.
Persyaratan
- Cordova CLI 12+
cordova-android13+ dan/ataucordova-ios7+ (plugin ini bergantung pada defaulthttps://localhost/danapp://localhost/schemes mereka)- Jangan gunakan
cordova-plugin-ionic-webviewbersamanya. Ionic WebView melewati scheme handler Cordova, sehingga bundle yang diunduh tidak akan dimuat.
Install plugin
Buat aplikasi di Capgo dengan cara yang sama seperti untuk Capacitor, lalu instal plugin dengan ID aplikasi Capgo Anda:
cordova plugin add @capgo/cordova-updater \
--variable APP_ID=com.example.app \
--variable DEFAULT_CHANNEL=production
cordova prepare android ios
DEFAULT_CHANNEL, UPDATE_URL, dan AUTO_UPDATE adalah variabel instal opsional. Jalankan cordova prepare kembali setiap kali Anda mengubahnya.
Konfirmasi setiap peluncuran
Setelah deviceready, plugin ini tersedia sebagai cordova.plugins.Updater. Panggil notifyAppReady() pada setiap peluncuran, atau plugin menganggap bundle baru rusak dan melakukan rollback:
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 })
}
})
Upload bundle dari CI
Tambahkan job yang mem-build web layer dan meng-upload-nya, untuk perubahan web-only:
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 update hanya mengganti web bundle. Menambah, menghapus, atau meng-upgrade plugin Cordova tetap butuh build store baru. Capgo membandingkan plugin native yang tercatat untuk live bundle dengan proyek Anda dan menandai upload yang tidak kompatibel; lihat kompatibilitas live update. Setup lengkapnya ada di dokumentasi updater Cordova.
On the build side, the Build Capgo dokumentasi hanya mencakup proyek Capacitor (mengunggah proyek native yang dihasilkan oleh cap sync). Binary Cordova tetap di-build di CI Anda seperti yang ditunjukkan di atas.
Opsional: migrasi ke Capacitor nanti
Anda tidak perlu meninggalkan Cordova untuk mendapatkan pipeline yang berfungsi dan live updates. Migrasi adalah keputusan terpisah, yang layak dilakukan ketika:
- Anda ingin build iOS tanpa memelihara runner macOS. Capgo Build mengompilasi aplikasi Capacitor dari job CI Linux.
- Plugin yang Anda andalkan berhenti menerima pembaruan Cordova, tetapi memiliki ekuivalen Capacitor.
- Anda ingin dukungan Swift Package Manager dan versi Android Gradle Plugin terbaru tanpa perlu patching platform.
Migrasi ini mempertahankan www code. Anda menambahkan Capacitor, memindahkan plugin (sebagian besar plugin Cordova langsung jalan), dan mengganti beberapa yang punya padanan Capacitor-native. Capgo app ID dan channel Anda tetap sama; hanya plugin updater yang berubah dari @capgo/cordova-updater ke @capgo/capacitor-updater. Langkah-langkahnya ada di Migrasi dari Cordova ke Capacitor, dan Halaman solusi Cordova ke Capacitor membahas cara mendapatkan bantuan untuknya. Setelah migrasi, CI di atas berkurang jadi web build, cap sync, dan satu build request panggilan per platform, seperti yang dijelaskan di Membandingkan platform CI/CD untuk aplikasi Capacitor.
Troubleshooting build Cordova di CI
| Error | Penyebab dan solusi |
|---|---|
Current working directory is not a Cordova-based project |
config.xml atau www/ hilang. Jalankan web build Anda sebelum perintah Cordova, atau buat www/. |
No platforms added to this project |
Platform tidak terdaftar di package.json. Jalankan cordova platform add lokal sekali lalu commit package.json. |
Could not find an installed version of Gradle |
Terbaru cordova-android menggunakan Gradle wrapper. Perbarui platform, bukan menginstal Gradle secara global. |
Unsupported class file major version |
JDK di runner tidak cocok dengan yang cordova-android diharapkan. Kunci dengan setup-java. |
No signing certificate "iOS Distribution" found |
Gunakan Apple Distribution sebagai identitas, dan pastikan bahwa .p12 berisi private key. |
errSecInternalComponent |
Keychain terkunci atau daftar partisi hilang. Jalankan ulang perintah setup keychain. |
| Upload ditolak: SDK terlalu lama | Runner image menggunakan Xcode versi di bawah 26. Pilih macos-26 atau atur Xcode dengan xcode-select. |
Upload ke Play: Version code has already been used |
Naikkan android-versionCode untuk setiap upload. |
Ringkasan
Pipeline Cordova di tahun 2026 adalah job CI per platform: Linux runner untuk Android AAB, macOS runner dengan Xcode 26 untuk IPA, secrets yang didekode ke dalam file yang di-generate build.json, dan kunci API untuk kedua store. CI mainstream apa pun bisa menjalankannya. Tambahkan @capgo/cordova-updater dan bundle upload job untuk perbaikan web-only, dan Anda memiliki bagian build, store, dan over-the-air yang dulu disediakan Appflow dan App Center. Beralih ke Capacitor tetap menjadi opsi untuk nanti, bukan keharusan.