Langsung ke konten utama
CI/CD

Build iOS Capacitor otomatis dengan GitHub actions dan sertifikat

Cara menyiapkan pipeline CI/CD untuk aplikasi Ionic iOS Anda menggunakan fastlane dan GitHub Actions dalam 5 menit (2024)

Build iOS Capacitor otomatis dengan GitHub actions dan sertifikat

Menyiapkan CI/CD untuk aplikasi Capacitor bisa jadi rumit dan memakan waktu. Berikut yang perlu Anda ketahui:

Sekarang kami merekomendasikan penggunaan Capgo Build dengan Capgo CLI untuk build native Capacitor. Panduan Fastlane ini dipertahankan untuk tim yang mengelola pipeline GitHub Actions yang sudah ada, tetapi build iOS baru sebaiknya menggunakan Capgo CLI supaya Anda tidak perlu mengurus Fastlane, Xcode runner, sertifikat, dan skrip upload sendiri.

Capgo Build untuk CI/CD oleh Capgo

Tak perlu lagi maintenance Fastlane, Xcode runner, sertifikat, provisioning profile, dan skrip upload. Capgo Build menjalankan signed native iOS build dari pipeline CI/CD Anda yang sudah ada:

  • Kompatibel dengan pipeline Anda: Trigger Capgo Build dari GitHub Actions, GitLab CI, Jenkins, atau script lokal setelah web build Anda dan npx cap sync.
  • Signing via CI secrets: Simpan App Store Connect key, sertifikat, provisioning profile, password, dan team ID di CI secrets Anda sendiri.
  • Tanpa maintenance native runner: Capgo Build menyediakan environment build Apple yang terkelola, jadi Anda tidak perlu mengelola macOS runner, Xcode image, atau Fastlane lane.
  • Artifact dan submission: Download signed artifacts untuk QA atau submit release build melalui Capgo CLI.

Harga

  • Paket Capgo mulai dari $12/bulan
  • Termasuk OTA updates dan sekitar 15 build native per bulan
  • Menit build ekstra ditagih per menit lewat credit

Setup Capgo Build di CI/CD

Panduan Setup Manual

Berikut langkah-langkah yang perlu Anda lakukan:

Continuous Delivery untuk iOS menggunakan Fastlane, GitHub Actions, dan sertifikat

Prerequisites

Sebelum melanjutkan tutorial ini:

  • Pastikan Fastlane sudah terpasang terinstal di mesin development Anda.
  • Pastikan Anda sudah terdaftar sebagai anggota iOS Developer Program.

Informasi penting tentang harga

Harga GitHub Action

https://github.com/features/actions

Layanan ini ‘gratis’ sampai batasnya, tergantung mesin yang dipilih.
Kita akan menggunakan macOS mesin, Anda bisa melihat harga dan batasnya di screenshot (harga saat tutorial dibuat, bisa berubah di masa mendatang)

Setelah memahami persyaratan dan harga, mari kita lanjut.

Catatan: Dalam postingan ini, saya berasumsi Anda sudah membuat aplikasi di App Store Connect. Informasi penting akan disalin secara otomatis oleh Fastlane!

Apa yang akan Anda pelajari di tutorial ini

Langkah-langkah dalam postingan ini

  1. Menggunakan API App Store Connect dengan Fastlane
    • Persyaratan:
      • Membuat App Store Connect API Key
      • Menggunakan App Store Connect API Key
  2. Salin file Fastlane
  3. Konfigurasi GitHub Actions

1. Menggunakan API App Store Connect dengan Fastlane

Mulai Februari 2021, autentikasi dua faktor atau verifikasi dua langkah wajib bagi semua pengguna untuk login ke App Store Connect. Lapisan keamanan tambahan untuk Apple ID ini memastikan hanya Anda yang dapat mengakses akun tersebut.
Dari Dukungan Apple

Persyaratan

Agar Fastlane dapat menggunakan App Store Connect API untuk upload aplikasi Anda, Anda perlu menyiapkan tiga hal berikut:

  1. Issuer ID
  2. Key ID
  3. File Key atau isi Key

Cara mendapatkan App Store Connect API Key

Untuk membuat key, Anda harus memiliki izin Admin di App Store Connect. Jika tidak punya izin tersebut, Anda bisa mengarahkan orang yang berwenang ke artikel ini.

  1. Login ke App Store Connect.

  2. Pilih Pengguna dan Akses.

Akses user App Store Connect

3 — Pilih tab Integration.

Integrasi API App Store Connect

  1. Klik Generate API Key atau tombol Add (+).

Membuat key API App Store Connect

  1. Masukkan nama untuk key ini. Nama ini hanya untuk referensi Anda dan bukan bagian dari key itu sendiri.

Nama pembuatan key API App Store Connect

6 — Di bagian Access, pilih role untuk key tersebut. Role yang berlaku untuk key sama dengan role yang berlaku untuk user di tim Anda. Lihat role permissions. Kami menyarankan untuk memilih Manajer aplikasi.

  1. Klik Generate.

Akses kunci API tidak dapat dibatasi ke aplikasi tertentu.

Nama kunci baru, ID kunci, link download, dan info lainnya akan muncul di halaman.

Kunci download App Store Connect

Anda bisa mendapatkan ketiga informasi penting di sini.
<1> ID Issue. (APPLE_ISSUER_ID secret)
<2> Key ID. (APPLE_KEY_ID secret)
<3> Klik “Download API Key” untuk download private key API Anda. Link download hanya muncul jika private key belum pernah di-download. Apple tidak menyimpan salinan private key tersebut. Jadi, Anda hanya bisa men-download-nya satu kali.

🔴 Simpan private key Anda di tempat yang aman. Jangan pernah membagikan kunci Anda, menyimpan kunci di repositori code, atau menyertakan kunci dalam code client-side.

Menggunakan App Store Connect API Key

File kunci API (file p8 yang Anda unduh), key ID, dan issuer ID diperlukan untuk membuat JWT token otorisasi. Ada beberapa cara untuk memasukkan informasi ini ke Fastlane. Saya memilih menggunakan action baru dari Fastlane app_store_connect_api_key. Anda bisa mempelajari cara lainnya di dokumentasi Fastlane. Saya menunjukkan metode ini karena menurut saya ini cara termudah untuk bekerja dengan kebanyakan CI, di mana Anda bisa mengatur environment variables.

Silakan konversi file p8 yang Anda unduh ke Base64 dan simpan sebagai secret ("APPLE_KEY_CONTENT).

base64 -i APPLE_KEY_CONTENT.p8 | pbcopy

Sekarang kita bisa mengelola App Store Connect dengan Fastlane menggunakan kunci API, mantap!

2. Sertifikat

Buka Xcode dan buka Pengaturan > Akun > Apple ID > Tim dan pilih tim Anda.

identitas signing Code

Klik pada Kelola sertifikat.

Jika Anda belum membuat sertifikat, Anda bisa membuat sertifikat baru.

Klik pada + dan pilih Distribusi Apple

Distribusi Apple

Kemudian Anda perlu membuka keychain untuk mengunduh sertifikat sebagai a .p12 Anda.

Untuk melakukannya, Anda perlu membuka keychain dan beralih ke login keychain, lalu pilih tab Sertifikat Saya.

Sertifikat Saya

Setelah itu, pilih sertifikat yang ingin diunduh. (Cek berdasarkan tanggal sertifikat)

Lalu klik kanan pada private key di sertifikat tersebut dan pilih Ekspor.

Pilih format file Pertukaran Informasi Pribadi (.p12).

Ini akan mengunduh sertifikat sebagai .p12 Anda.

Silakan buka file tersebut di terminal dan gunakan perintah berikut untuk mengubahnya ke Base64:

base64 -i BUILD_CERTIFICATE.p12 | pbcopy

Ini akan menjadi BUILD_CERTIFICATE_BASE64 secret. Selain itu, jika diminta, masukkan juga password sertifikatnya. Password ini akan menjadi P12_PASSWORD secret Anda.

3. Provisioning profiles

Buka Apple Developer dan pilih tim yang sesuai.

Lalu buat profil baru dengan klik +

Buat profil baru

Dan pilih App Store Connect.

Pilih App Store Connect

Kemudian Anda perlu memilih aplikasi yang tepat, berhati-hatilah karena Anda tidak bisa menggunakan wildcard atau proses signing akan gagal.

Pilih aplikasi yang sesuai

Pilih sertifikat yang tepat yang sudah Anda buat sebelumnya (cek tanggal kedaluwarsanya, harus sesuai dengan hari dan bulan ini) lalu klik Lanjutkan.

Pilih sertifikat yang sesuai

Terakhir, masukkan nama profil lalu klik pada Generate.

Nama ini akan digunakan untuk mengidentifikasi profil di Fastlane, di bawah nilai APPLE_PROFILE_NAME.

Generate profil

Anda dapat mengunduh profil sebagai .mobileprovision Anda.

Download profil

Harap konversi profil ke Base64 dan simpan sebagai secret (BUILD_PROVISION_PROFILE_BASE64).

base64 -i BUILD_PROVISION_PROFILE.mobileprovision | pbcopy

4. Salin file Fastlane

Fastlane adalah library Ruby yang dibuat untuk mengotomatisasi tugas pengembangan mobile yang umum. Dengan Fastlane, Anda bisa mengonfigurasi “lanes” kustom yang membundel serangkaian “actions” untuk menjalankan tugas yang biasanya dilakukan di Android Studio. Anda bisa melakukan banyak hal dengan Fastlane, namun untuk tutorial ini, kita hanya akan menggunakan beberapa core actions saja.

Buat folder Fastlane di root proyek Capacitor/Ionic Anda dan tambahkan Fastfile di sana:

  • Folder: <project-root>/fastlane/
  • File: <project-root>/fastlane/Fastfile

Ini berada di level yang sama dengan package.json, capacitor.config.*, dan ios/ folder. Jangan buat di dalam ios/App/.

platform :ios do
  desc 'Export ipa and submit to TestFlight'
  lane :beta do
    keychain_info = { keychain_name: "ios-build-#{Time.now.to_i}.keychain", keychain_password: SecureRandom.uuid }
    
    begin
      setup_signing(keychain_info)
      bump_build_number
      build_app_with_signing(keychain_info)
      submit_to_testflight
    ensure
      cleanup_keychain(keychain_info)
    end
  end

  private_lane :setup_signing do |options|
    create_keychain(
      name: options[:keychain_name],
      password: options[:keychain_password],
      unlock: true,
      timeout: 0,
      lock_when_sleeps: false, 
      add_to_search_list: true
    )
    import_cert(options)
    install_profile
    update_project_settings
  end

  lane :bump_build_number do
		file = File.read('../package.json')
		data_hash = JSON.parse(file)
		api_key = app_store_connect_api_key(
      key_id: ENV['APPLE_KEY_ID'],
      issuer_id: ENV['APPLE_ISSUER_ID'],
      key_content: ENV['APPLE_KEY_CONTENT'],
      is_key_content_base64: true,
      duration: 1200,
      in_house: false
    )
		build_num = app_store_build_number(
      api_key: api_key,
			app_identifier: ENV['BUNDLE_IDENTIFIER'],
			live: false
    )
		build_num = build_num + 1
		UI.message("Bumped build number to #{build_num}")
		increment_build_number(
			build_number: build_num,
			xcodeproj: "./ios/App/App.xcodeproj",
			skip_info_plist: true
		)
	end

  private_lane :import_cert do |options|
    cert_path = "#{Dir.tmpdir}/build_certificate.p12"
    File.write(cert_path, Base64.decode64(ENV['BUILD_CERTIFICATE_BASE64']))
    import_certificate(
      certificate_path: cert_path,
      certificate_password: ENV['P12_PASSWORD'] || "",
      keychain_name: options[:keychain_name],
      keychain_password: options[:keychain_password],
      log_output: true
    )
    File.delete(cert_path)
  end  
  
  private_lane :cleanup_keychain do |options|
    delete_keychain(
      name: options[:keychain_name]
    )
  end  

  private_lane :install_profile do
    profile_path = "#{Dir.tmpdir}/build_pp.mobileprovision"
    File.write(profile_path, Base64.decode64(ENV['BUILD_PROVISION_PROFILE_BASE64']))
    UI.user_error!("Failed to create provisioning profile at #{profile_path}") unless File.exist?(profile_path)
    ENV['PROVISIONING_PROFILE_PATH'] = profile_path
    install_provisioning_profile(path: profile_path)
    File.delete(profile_path)
  end

  private_lane :update_project_settings do
    update_code_signing_settings(
      use_automatic_signing: false,
      path: "./ios/App/App.xcodeproj",
      code_sign_identity: "iPhone Distribution",
      profile_name: ENV['APPLE_PROFILE_NAME'],
      bundle_identifier: ENV['BUNDLE_IDENTIFIER'],
      team_id: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
    update_project_team(
      path: "./ios/App/App.xcodeproj",
      teamid: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
  end

  private_lane :build_app_with_signing do |options|
    unlock_keychain(
      path: options[:keychain_name],
      password: options[:keychain_password],
      set_default: false
    )
    build_app(
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      configuration: "Release",
      export_method: "app-store",
      output_name: "App.ipa",
      export_options: {
        provisioningProfiles: {
          ENV['BUNDLE_IDENTIFIER'] => ENV['APPLE_PROFILE_NAME']
        }
      },
      xcargs: "-verbose",
      buildlog_path: "./build_logs",
      export_xcargs: "-allowProvisioningUpdates",
    )
  end   

  private_lane :submit_to_testflight do
    api_key = app_store_connect_api_key(
      key_id: ENV['APPLE_KEY_ID'],
      issuer_id: ENV['APPLE_ISSUER_ID'],
      key_content: ENV['APPLE_KEY_CONTENT'],
      is_key_content_base64: true,
      duration: 1200,
      in_house: false
    )
    pilot(
      api_key: api_key,
      skip_waiting_for_build_processing: true,
      skip_submission: true,
      distribute_external: false,
      notify_external_testers: false,
      ipa: "./App.ipa"
    )
  end
end

5. Mengatur secrets

GitHub Actions menggunakan repository secrets yang Anda konfigurasi di langkah berikutnya. Anda hanya butuh file .env lokal jika ingin menjalankan atau mengetes Fastlane dari mesin sendiri.

Untuk testing lokal, buat <project-root>/fastlane/.env di sebelah Fastfile. Jangan commit file ini. Tambahkan fastlane/.env ke dalam .gitignore Anda terlebih dahulu (atau pastikan sudah di-ignore). Berikut contohnya:

APP_STORE_CONNECT_TEAM_ID=UVTJ336J2D
BUNDLE_IDENTIFIER=ee.forgr.testfastlane
# See previous section for these secrets
BUILD_CERTIFICATE_BASE64=
BUILD_PROVISION_PROFILE_BASE64=
APPLE_KEY_ID=
APPLE_ISSUER_ID=
APPLE_KEY_CONTENT=
P12_PASSWORD=
APPLE_PROFILE_NAME=

Cara mendapatkan APP_STORE_CONNECT_TEAM_ID

Buka Pusat Developer dan scroll ke bawah ke Membership details section. Lalu Team ID adalah nilai yang perlu diatur di APP_STORE_CONNECT_TEAM_ID secret.

app-store-connect-team-id

Mendapatkan BUNDLE_IDENTIFIER

  1. Buka Xcode
  2. Klik dua kali pada App di project navigator
  3. Kemudian klik tab Signing and Capabilities
  4. Salin nilai dari Bundle identifier. Ini adalah nilai yang perlu Anda atur di BUNDLE_IDENTIFIER secret.
bundle-identifier-xcode

6. Pemrosesan Build

Di GitHub Actions, biaya dihitung berdasarkan menit yang Anda gunakan untuk menjalankan workflow CI/CD. Berdasarkan pengalaman saya, butuh sekitar 10–15 menit sebelum build dapat diproses di App Store Connect.

Untuk proyek privat, estimasi biaya per build bisa mencapai $0,08/menit x 15 menit = $1,2, atau lebih, tergantung pada konfigurasi dan dependensi proyek Anda.

Jika Anda khawatir dengan biaya proyek privat, Anda bisa mengatur skip_waiting_for_build_processing ke true. Ini akan menghemat build minutes karena tidak perlu menunggu App Store Connect selesai memproses build.

Namun, ada konsekuensinya — Anda harus memperbarui informasi kepatuhan aplikasi secara manual di App Store Connect sebelum mendistribusikan build ke pengguna.

Optimasi ini terutama berguna untuk proyek privat di mana build minutes berbayar. Untuk proyek publik/gratis, build minutes tersedia gratis sehingga tidak perlu mengaktifkan pengaturan ini. Lihat GitHub di halaman pricing untuk detail lebih lanjut.

7. Setup GitHub Actions

Konfigurasikan secret GitHub

Silakan salin secrets dari .env file dan tempelkan ke repository secrets GitHub.

Buka Pengaturan &gt; Secrets dan variabel &gt; Aksi &gt; Secret repositori baru

github-secrets

2. BUILD_CERTIFICATE_BASE64 - Sertifikat dalam format Base64.

3. BUILD_PROVISION_PROFILE_BASE64 - Provisioning profile dalam format Base64.

4. BUNDLE_IDENTIFIER - Bundle identifier aplikasi Anda.

5. APPLE_KEY_ID — App Store Connect API Key 🔺Key ID.

6. APPLE_ISSUER_ID — App Store Connect API Key 🔺Issuer ID.

7. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Isi key dari .p8, cek di sini

8. Konfigurasi file workflow GitHub

Buat direktori workflow GitHub.

cd .github/workflows

Di dalam folder workflow , buat file bernama build-upload-ios.ymldan tambahkan kode berikut.

name: Build source code on ios

on:
  push:
    tags:
      - '*'

jobs:
  build_ios:
    runs-on: macOS-latest
    steps:
      - uses: actions/checkout@v6
      - name: Set Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: npm
      - name: Install dependencies
        id: install_code
        run: npm ci
      - name: Build
        id: build_code
        run: npm run build
      - uses: actions/cache@v5
        with:
          path: ios/App/Pods
          key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-pods-
      - name: Sync
        id: sync_code
        run: npx cap sync
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.0'
          bundler-cache: true
      - uses: maierj/fastlane-action@v3.1.0
        env:
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
          BUNDLE_IDENTIFIER: ${{ secrets.BUNDLE_IDENTIFIER }}
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          APPLE_PROFILE_NAME: ${{ secrets.APPLE_PROFILE_NAME }}
        with:
          lane: ios beta
      - name: Upload release bundle
        uses: actions/upload-artifact@v6
        with:
          name: ios-release
          path: ./App.ipa
          retention-days: 10

Workflow ini akan terpicu setelah setiap GitHub tag, jika Anda perlu mengotomatiskan tag, silakan lihat Build dan release otomatis dengan GitHub actions pertama.

Kemudian workflow ini akan mengambil deps NodeJS Anda, menginstalnya, dan mem-build aplikasi JavaScript Anda.

Setiap kali Anda mengirim commit baru, sebuah release akan di-build di TestFlight.

App Anda tidak wajib menggunakan Ionic, hanya base Capacitor yang wajib. Bisa menggunakan modul Cordova lama, tapi Capacitor JS plugin lebih disarankan.

8. Trigger workflow

Buat commit

Lakukan commit, Anda akan melihat workflow yang aktif di repositori.

Trigger workflow

Push commit baru ke branch main atau development untuk trigger workflow.

Dimulai dengan commit

Setelah beberapa menit, build seharusnya sudah tersedia di dashboard App Store Connect Anda.

Dashboard Testflight

9. Bisa deploy dari mesin lokal?

Ya, bisa, dan sangat mudah.

Anda tetap bisa menggunakan Xcode untuk build dan sign aplikasi Anda.

Panduan Setup CI/CD

Platform CI/CD Alternatif

Live Updates &amp; Deployment

Terima kasih

Blog ini berdasarkan artikel berikut:

Lanjutkan dari Automatic Capacitor IOS build dengan GitHub actions dengan sertifikat

Jika Anda menggunakan Build iOS Capacitor otomatis dengan GitHub actions dan sertifikat untuk merencanakan otomatisasi CI/CD, hubungkan dengan CI/CD Capgo untuk workflow produk di Capgo CI/CD, Capgo Native Build untuk workflow produk di Capgo Native Builds, Integrasi Capgo untuk alur kerja produk di Integrasi Capgo, Integrasi CI/CD untuk detail implementasi di Integrasi CI/CD, dan Integrasi GitHub Actions untuk detail implementasi di Integrasi GitHub Actions.

Live updates untuk aplikasi Capacitor

Saat bug web-layer sudah live, kirim perbaikannya lewat Capgo alih-alih menunggu berhari-hari untuk persetujuan app store. Pengguna mendapat update di background, sementara perubahan native tetap mengikuti jalur review normal.

Terbaru dari Blog kami

Capgo memberikan insight terbaik yang Anda butuhkan untuk membuat aplikasi mobile yang benar-benar profesional.