Panduan lengkap membangun pipeline CI/CD .NET MAUI dengan GitHub Actions: build Android AAB dan iOS IPA, code signing, publish ke Play Store dan TestFlight, plus strategi versioning dan optimasi biaya runner macOS.
CI/CD .NET MAUI dengan GitHub Actions adalah cara paling praktis untuk membangun, menandatangani, dan mengunggah aplikasi Android dan iOS secara otomatis dari satu repositori. Cukup dorong tag Git, dan pipeline akan menghasilkan APK/AAB yang siap masuk Google Play serta build iOS yang otomatis terunggah ke TestFlight. Di artikel ini saya bagikan template workflow lengkap yang sudah kami pakai di tim untuk beberapa aplikasi produksi, termasuk konfigurasi runner, manajemen sertifikat, dan strategi versioning yang tidak membuat rilis Senin pagi jadi bencana.
Build Android .NET MAUI berjalan cepat di runner ubuntu-latest, tetapi build iOS wajib menggunakan runner macos-14 atau lebih baru dengan Xcode 15.4+.
Kredensial signing (keystore Android, .p12 iOS, provisioning profile) harus di-encode base64 dan disimpan sebagai GitHub Encrypted Secrets. Jangan pernah commit ke repo.
Publish otomatis ke Google Play memakai service account JSON via r0adkll/upload-google-play, sementara iOS ke TestFlight lewat App Store Connect API key (issuer ID + key ID + .p8).
Versioning otomatis dari github.run_number mencegah konflik version code di Play Console dan CFBundleVersion di TestFlight.
Biaya bulanan untuk tim kecil biasanya di bawah $50 karena runner Linux gratis untuk repo publik. Runner macOS ditagih 10× credit, jadi optimalkan dengan matrix build.
Cache ~/.nuget/packages dan workload MAUI memangkas waktu build 3–5 menit per job.
Mengapa CI/CD Wajib untuk Proyek .NET MAUI
Jujur, setiap tim yang saya pimpin selalu sampai pada kesimpulan yang sama: rilis manual .NET MAUI adalah utang teknis yang berbunga harian. Kamu perlu build di Windows untuk Android, macOS untuk iOS, lalu upload manual ke dua console berbeda. Itu belum termasuk memutakhirkan version code, menandatangani ulang binary saat sertifikat kedaluwarsa, dan mengejar QA yang butuh build spesifik dari branch feature. Di satu proyek pembayaran retail, kami menghabiskan tiga jam per rilis sebelum mengadopsi pipeline otomatis. Setelah migrasi ke GitHub Actions, waktu itu turun jadi sepuluh menit menunggu pipeline hijau.
Selain hemat waktu, pipeline yang benar juga menghilangkan kelas bug "works on my machine". Dengan runner yang bersih setiap kali, kita tidak lagi khawatir konfigurasi lokal developer bocor ke binary produksi. Semua build reproducible, dan setiap AAB yang masuk Play Store berasal dari commit hash yang tercatat. Untuk tim yang serius mengelola kualitas rilis, ini bukan lagi opsi. Kalau proyekmu masih menggunakan Xamarin.Forms, saya sarankan baca dulu panduan migrasi Xamarin.Forms ke .NET MAUI sebelum menyiapkan pipeline, karena CI/CD di stack lama membutuhkan konfigurasi berbeda yang tidak akan dibahas di sini.
Prasyarat dan Struktur Workflow
Sebelum menulis workflow pertama, pastikan proyekmu memiliki tiga hal: .csproj dengan TargetFrameworks net9.0-android dan net9.0-ios yang valid, sertifikat signing Android (.keystore) yang sudah diuji, dan akses ke App Store Connect sebagai Admin atau App Manager. Untuk sisi Google Play, kamu memerlukan service account dengan role Release Manager di Google Cloud Console. Persiapan ini sering diremehkan tapi memakan waktu paling banyak karena melibatkan tim ops atau security.
Struktur repositori yang saya rekomendasikan meletakkan workflow di .github/workflows/ dengan pemisahan file per platform: build-android.yml, build-ios.yml, dan release.yml untuk trigger produksi. Pemisahan ini memungkinkan trigger yang berbeda. Build Android bisa jalan pada setiap PR, sementara build iOS yang mahal hanya jalan pada branch main. Berikut skeleton awal yang bisa langsung dipakai:
Perhatikan fetch-depth: 0. Kita memerlukan riwayat lengkap agar bisa menghitung versi otomatis dari jumlah commit atau tag terakhir. Detail kecil ini sering dilewatkan dan menyebabkan versi build meloncat-loncat tanpa penjelasan (saya pernah menghabiskan setengah hari mencari penyebabnya sebelum menyadari default fetch-depth adalah 1).
Membangun Android AAB di GitHub Actions
Build Android bisa berjalan di runner ubuntu-latest yang gratis untuk sebagian besar workflow, sehingga jadi target ideal untuk optimasi biaya. Job build ini perlu menginstal .NET 9 SDK, workload MAUI, dan JDK 17. Urutan langkah yang stabil selama enam bulan terakhir di tim kami terlihat seperti ini:
Beberapa hal krusial di sini. Pertama, AndroidPackageFormat=aab menghasilkan Android App Bundle yang wajib untuk Play Store sejak 2021, bukan APK. Kedua, parameter ApplicationVersion memetakan ke versionCode Android, dan nilai ini harus selalu naik. Menggunakan github.run_number memastikan setiap build punya nomor unik dan monoton. Untuk cache, kombinasi hash .csproj sebagai key membuat cache di-invalidate hanya saat dependency berubah, memangkas waktu restore hingga 60% pada build kedua dan seterusnya.
Menandatangani AAB Android dengan Keystore
AAB yang belum ditandatangani tidak akan diterima Play Store. Pendekatan paling aman adalah menyimpan file .keystore sebagai secret dalam bentuk base64, lalu men-decode di runner. Encode dulu di mesin lokal:
Setelah step ini selesai, kamu punya AAB signed yang bisa langsung diunggah ke Google Play. Simpan artifact dengan retention 30 hari agar QA bisa mengambil build spesifik untuk regression testing tanpa harus menjalankan ulang pipeline. Untuk konteks tambahan tentang bagaimana signing berinteraksi dengan sistem keamanan lain, lihat panduan autentikasi dan keamanan .NET MAUI yang membahas rantai kepercayaan aplikasi secara menyeluruh.
Publish Otomatis ke Google Play Console
Untuk mengunggah AAB ke Play Store secara otomatis, kita menggunakan action r0adkll/upload-google-play yang membungkus Google Play Publishing API. Prasyaratnya: service account Google Cloud yang sudah diberi akses ke Play Console melalui menu Users and permissions, dan file JSON kredensialnya disimpan sebagai secret PLAY_SERVICE_ACCOUNT_JSON. Ada empat track yang bisa kamu targetkan (internal, alpha, beta, dan production). Rekomendasi saya: PR selalu ke internal, tag versi ke beta, dan promosi manual dari beta ke production melalui Play Console.
Folder distribution/whatsnew berisi file whatsnew-en-US.txt, whatsnew-id-ID.txt, dan seterusnya per bahasa store listing. Ini adalah changelog yang muncul di halaman Play Store, jadi jangan lupa memperbaruinya di setiap rilis. Detail teknis parameter API bisa dibaca di dokumentasi Google Play Developer API.
Membangun iOS App di macOS Runner
Nah, bagian iOS adalah tempat pipeline mulai mahal dan rumit. Kamu wajib menggunakan runner macos-14 atau macos-15. Tidak bisa Linux, tidak bisa Windows. GitHub menagih runner macOS dengan tarif 10× lipat dibanding Linux, jadi optimasi setiap step itu penting. Berikut konfigurasi yang efektif untuk .NET 9 dan Xcode 15.4:
Pemilihan Xcode eksplisit itu penting karena macOS runner biasanya punya beberapa versi Xcode terinstal, dan default-nya bisa berubah tanpa peringatan (satu bulan lalu build kami tiba-tiba gagal karena default berpindah dari 15.4 ke 16.0). Untuk versi Xcode yang didukung .NET 9, cek matriks kompatibilitas Xcode di dokumentasi Microsoft Learn.
Code Signing iOS: Sertifikat dan Provisioning Profile
Code signing iOS jauh lebih rumit dari Android karena melibatkan tiga artifact: distribution certificate (.p12), provisioning profile (.mobileprovision), dan App Store Connect API key (.p8). Ketiganya harus di-encode base64 dan disimpan sebagai secret. Setup wajib di keychain sementara agar tidak mengotori runner setelah selesai:
Nilai CodesignKey harus persis sesuai common name sertifikat. Cek dengan security find-identity -v -p codesigning. Nilai CodesignProvision adalah nama profile, bukan file name. Kesalahan ini adalah penyebab #1 pipeline iOS gagal dan pesan errornya sering menyesatkan (saya pernah mengejar "profile not found" selama dua hari sebelum sadar ada spasi tersembunyi di secret). Tambahkan step diagnostik yang menjalankan security find-identity saat debugging.
Distribusi ke TestFlight dengan App Store Connect API
Setelah IPA terbentuk, upload ke TestFlight paling andal menggunakan xcrun altool dengan App Store Connect API key. Metode username/password kini deprecated dan sering menyebabkan rate limiting. Kamu memerlukan tiga secret: APPSTORE_ISSUER_ID, APPSTORE_KEY_ID, dan APPSTORE_PRIVATE_KEY (isi file .p8 dalam base64).
Proses upload biasanya memakan 3–8 menit tergantung ukuran IPA. TestFlight akan otomatis memproses build dan mengirim notifikasi ke internal testers dalam 10–30 menit. Untuk external testers, kamu perlu memicu review Apple secara manual dari App Store Connect atau via API. Alternatif populer di komunitas adalah fastlane pilot, cocok bagi tim yang sudah memakai fastlane untuk workflow rilis lain.
Manajemen Secrets dan Strategi Versioning
Semua secret di atas (12 hingga 15 total untuk proyek dual-platform) harus dikelola dengan disiplin. Rekomendasi praktis dari pengalaman kami:
Gunakan Environment secrets, bukan Repository secrets untuk production credentials. Ini memungkinkan required reviewers dan wait timer sebelum deploy.
Rotate credentials setiap 6 bulan. Sertifikat iOS valid setahun, tetapi rotasi sebelum kedaluwarsa mencegah panik dini.
Dokumentasikan setiap secret di README internal: nama secret, owner, tanggal kedaluwarsa, cara regenerasi. Bus factor pipeline sering 1.
Jangan echo secret ke log. GitHub secara otomatis menyensor, tapi transformasi (base64 decode) bisa lolos filter.
Untuk versioning, dua pola yang stabil di tim saya adalah: run number sebagai build number (paling sederhana, versi major.minor manual, patch otomatis dari github.run_number), atau semantic release dengan tag Git sebagai sumber kebenaran. Pola pertama cocok untuk aplikasi internal atau MVP; pola kedua wajib untuk produk yang punya jadwal rilis publik dan changelog.
Optimasi Biaya dan Waktu Build
Runner macOS adalah komponen paling mahal, sekitar $0.08 per menit setelah kuota gratis habis. Sebuah build iOS end-to-end memakan 12–18 menit, sehingga 50 build per bulan bisa dengan mudah menembus $70. Beberapa optimasi yang saya terapkan berhasil menurunkan biaya sampai 40%:
Cache workload MAUI di ~/.dotnet/workload. Instalasi workload memakan 4–6 menit setiap run kalau tidak di-cache.
Skip iOS build di PR. Jalankan hanya Android di PR checks; iOS hanya di main dan tag.
Concurrency group untuk membatalkan build usang: concurrency: { group: ios-${{ github.ref }}, cancel-in-progress: true }.
Matrix build untuk paralelisasi, bukan sequential, sehingga Android dan iOS jalan bersamaan.
Self-hosted Mac mini untuk tim dengan volume tinggi (>200 build iOS/bulan) memberikan ROI dalam 4–5 bulan.
Setelah pipeline stabil, integrasikan monitoring performa aplikasi hasilnya dengan tool seperti Firebase Performance atau App Center. Konfigurasi awal berjalan mulus, tetapi optimasi runtime adalah topik terpisah yang saya bahas di panduan optimasi performa .NET MAUI. Data telemetri dari CI/CD dan runtime bersama memberikan gambaran utuh kesehatan rilis.
Pertanyaan yang Sering Diajukan
Bisakah build iOS .NET MAUI di runner Windows atau Linux?
Tidak. Build dan signing iOS memerlukan Xcode dan toolchain Apple yang hanya tersedia di macOS. Kamu wajib menggunakan runner macos-14 ke atas di GitHub Actions atau self-hosted Mac. Ini adalah pembatasan Apple, bukan .NET MAUI.
Berapa biaya GitHub Actions untuk pipeline .NET MAUI?
Untuk repo privat dengan 20–30 rilis per bulan (Android + iOS), biayanya sekitar $30–$60 di paket Team. Runner macOS ditagih 10× dibanding Linux. Repo publik mendapat runner Linux tak terbatas gratis, sehingga proyek open-source praktis hanya membayar menit macOS untuk iOS.
Bagaimana cara menandatangani IPA iOS tanpa Mac lokal?
Ekspor .p12 dari Keychain rekan setim yang memiliki Mac (atau minta security team menerbitkan), lalu simpan sebagai GitHub Secret dalam base64. Runner macos-14 akan menangani signing tanpa Mac lokal. fastlane match adalah alternatif yang lebih terkelola tapi butuh setup awal lebih panjang.
Apakah pipeline ini mendukung deployment ke Huawei AppGallery?
Ya, tetapi memerlukan langkah tambahan menggunakan Huawei AGC Publish API dan bundle APK berbeda tanpa Google Mobile Services. Untuk pasar Indonesia yang masih memiliki perangkat Huawei, tambahkan job terpisah setelah signing Android dengan flag build -p:AndroidUseGmsCompat=false.
Kenapa build MAUI di GitHub Actions lebih lambat dari lokal?
Runner memulai dari image bersih setiap kali, sehingga instalasi .NET SDK, workload MAUI, dan NuGet restore memakan 5–8 menit sendiri. Aktifkan cache untuk ~/.nuget/packages dan ~/.dotnet/workload. Setelah cache warm, waktu build sisa turun mendekati kecepatan lokal.
Cross-platform engineering lead who's shipped apps to millions on both Play Store and App Store. Believes shared codebases shouldn't mean shared mediocrity.
Panduan lengkap deep linking .NET MAUI: konfigurasi Android App Links (assetlinks.json), iOS Universal Links (AASA), routing Shell, dan migrasi dari Firebase Dynamic Links yang sudah dihentikan. Termasuk trik debugging dari produksi.
Panduan lengkap migrasi aplikasi Xamarin.Forms ke .NET MAUI 9: dari .NET Upgrade Assistant, port custom renderer ke handler, hingga validasi build per platform dengan estimasi waktu realistis.