Salta al contenuto principale
CI/CD

Build automatica iOS di Capacitor con GitHub Actions usando match

Come configurare una pipeline CI/CD per la tua app Ionic iOS con fastlane e GitHub Actions in 5 min (2022)

Build automatica iOS di Capacitor con GitHub Actions usando match

Build iOS automatici con GitHub Actions e Match

Configurare la CI/CD per le app Capacitor può essere complesso e richiedere tempo. Ecco cosa devi sapere:

Ora consigliamo di usare Capgo Build con Capgo CLI per le build native Capacitor. Questa guida a Fastlane Match è pensata per i team che mantengono pipeline GitHub Actions esistenti, ma per le nuove build iOS conviene usare Capgo CLI, così non devi mantenere tu Fastlane, i repository Match, i runner Xcode, i certificati e gli script di upload.

Capgo Build per CI/CD con Capgo

Evita la manutenzione di Fastlane Match, runner Xcode, certificati, provisioning profile e script di upload. Build Capgo esegue build iOS native firmate dalla tua pipeline CI/CD esistente:

  • Si integra con la tua pipeline: Avvia Capgo Build tramite GitHub Actions, GitLab CI, Jenkins o script locali dopo la web build e npx cap sync.
  • Firma tramite segreti CI: Mantieni le chiavi di App Store Connect, certificati, provisioning profile, password e team ID nei tuoi segreti CI.
  • Nessuna gestione runner native: Capgo Build offre ambienti di build Apple gestiti, così non dovrai occuparti di macOS runner, immagini Xcode, Fastlane o repository Match.
  • Artifacts and submission: Scarica gli artifact firmati per la QA o invia le release build tramite Capgo CLI.

Prezzi

  • I piani Capgo partono da $12/mese
  • Include aggiornamenti OTA e circa 15 Native Build al mese
  • I minuti di build extra sono fatturati al minuto tramite crediti

Configura Capgo Build in CI/CD

Guida alla configurazione manuale

Ecco cosa devi fare:

Continuous Delivery per iOS con Fastlane e GitHub Actions tramite match

Prerequisiti

Prima di continuare con il tutorial…

  • Assicurati di avere Fastlane installato sulla tua macchina di sviluppo.
  • Iscrizione al programma per sviluppatori iOS.
  • Voglia di leggere 😆…
  • Un team con molti dev, altrimenti consigliamo di usare fastlane cert per workflow più semplici.

Da sapere sul prezzo

Prezzo GitHub Action

https://github.com/features/actions

Il servizio è 'gratuito' entro certi limiti, a seconda della macchina scelta.
We are going to use a macOS machine; nello screenshot vedi prezzi e limiti (prezzi validi al momento del tutorial, soggetti a variazioni)

🔴 Presi nota requisiti e costi, se ti va, procediamo...

📣 In the post we assume that we have the app created in iTunes connect, we do have the certificates of the Apple ecosystem, everything will be copied by Fastlane!

Tuffiamoci subito 🤿

Passaggi descritti nel post

  1. Usare App Store Connect API con Fastlane Match
  2. Requisiti
  3. Creazione di una API Key per App Store Connect
  4. Usare una API Key di App Store Connect
  5. Copia i file di Fastlane
  6. Configura Fastlane match

1. Usare API di App Store Connect con Fastlane Match

Da febbraio 2021, l'autenticazione a due fattori o la verifica in due passaggi è obbligatoria per accedere ad App Store Connect. Questo livello di sicurezza extra per il tuo Apple ID garantisce che solo tu possa accedere al tuo account.
Da Supporto Apple

Per iniziare con match dovrai revocare i certificati esistenti. Non preoccuparti, riceverai subito quelli nuovi.

Requisiti

Per poter usare API di App Store Connect, Fastlane ha bisogno di tre elementi.

  1. ID dell'issuer.
  2. ID chiave.
  3. File della chiave o contenuto della chiave.

Creazione di una API Key per App Store Connect

Per generare le chiavi devi avere i permessi di Admin in App Store Connect. Se non li hai, puoi indicare questo articolo alla persona responsabile e seguire le istruzioni qui sotto.

1 — Accedi a App Store Connect.

2 — Seleziona Utenti e accesso.

Accesso utenti in App Store Connect

3 — Seleziona la scheda Integrazioni.

Integrazione API di App Store Connect

4 — Clicca su Generate API Key o sul pulsante Add (+).

Creazione chiavi API in App Store Connect

5 — Inserisci un nome per la chiave. Il nome serve solo come riferimento e non fa parte della chiave stessa.

Nome della chiave API in App Store Connect

Alla voce Access, seleziona il ruolo per la chiave. I ruoli validi per le chiavi sono gli stessi degli utenti del tuo team. Consulta permessi dei ruoli. Ti consigliamo di selezionare App Manager.

7 — Clicca su Generate.

L'accesso di una chiave API non può essere limitato a singole app.

Nella pagina compaiono il nome della nuova chiave, il key ID, il link per il download e altre informazioni.

Download delle chiavi da App Store Connect

Qui puoi recuperare tutte e tre le informazioni necessarie.
<1> ID dell'issue.
<2> Key ID.
<3> Clicca su “Download API Key” per scaricare la tua API private key. Il link per il download compare solo se la private key non è ancora stata scaricata. Apple non conserva una copia della private key. Quindi puoi scaricarla una sola volta.

🔴 Conserva la tua private key in un luogo sicuro. Non condividere mai le chiavi, non salvarle in un repository code e non includerle nel code lato client.

Usare una API Key di App Store Connect

Per creare il token JWT per l'autorizzazione, sono necessari il file API Key (il file .p8 scaricato), il key ID e l'issuer ID. Esistono diversi modi per inserire queste informazioni in Fastlane utilizzando la nuova action di Fastlane, app_store_connect_api_key. Puoi scoprire altri modi nella documentazione di Fastlane. Ti mostro questo metodo perché credo sia il modo più semplice per lavorare con la maggior parte delle CI, dove puoi impostare variabili d'ambiente.

Ora possiamo gestire Fastlane con la API key di App Store Connect, fantastico!

2. Copia i file di Fastlane

Fastlane è una libreria Ruby creata per automatizzare i comuni task di sviluppo mobile. Con Fastlane puoi configurare "lane" personalizzate che raggruppano una serie di "action" per eseguire operazioni che normalmente faresti su Android Studio. Puoi fare moltissimo con Fastlane, ma per questo tutorial useremo solo poche action fondamentali.

Crea una cartella Fastlane nella root del tuo progetto e copia i seguenti file: Fastfile

default_platform(:ios)

DEVELOPER_APP_IDENTIFIER = ENV["DEVELOPER_APP_IDENTIFIER"]
DEVELOPER_APP_ID = ENV["DEVELOPER_APP_ID"]
PROVISIONING_PROFILE_SPECIFIER = ENV["PROVISIONING_PROFILE_SPECIFIER"]
TEMP_KEYCHAIN_USER = ENV["TEMP_KEYCHAIN_USER"]
TEMP_KEYCHAIN_PASSWORD = ENV["TEMP_KEYCHAIN_PASSWORD"]
APPLE_ISSUER_ID = ENV["APPLE_ISSUER_ID"]
APPLE_KEY_ID = ENV["APPLE_KEY_ID"]
APPLE_KEY_CONTENT = ENV["APPLE_KEY_CONTENT"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]

def delete_temp_keychain(name)
  delete_keychain(
    name: name
  ) if File.exist? File.expand_path("~/Library/Keychains/#{name}-db")
end

def create_temp_keychain(name, password)
  create_keychain(
    name: name,
    password: password,
    unlock: false,
    timeout: 0
  )
end

def ensure_temp_keychain(name, password)
  delete_temp_keychain(name)
  create_temp_keychain(name, password)
end

platform :ios do
  lane :build do
    build_app(
      configuration: "Release",
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
        }
      }
    )
  end
  lane :refresh_profiles do
    match(
      type: "development",
      force: true)
    match(
      type: "adhoc",
      force: true)
  end
  desc "Register new device"
  lane :register_new_device do  |options|
      device_name = prompt(text: "Enter the device name: ")
      device_udid = prompt(text: "Enter the device UDID: ")
      device_hash = {}
      device_hash[device_name] = device_udid
      register_devices(
                       devices: device_hash
                       )
    refresh_profiles
  end
  lane :closed_beta do
    keychain_name = TEMP_KEYCHAIN_USER
    keychain_password = TEMP_KEYCHAIN_PASSWORD
    ensure_temp_keychain(keychain_name, keychain_password)

    api_key = app_store_connect_api_key(
      key_id: APPLE_KEY_ID,
      issuer_id: APPLE_ISSUER_ID,
      key_content: APPLE_KEY_CONTENT,            
      duration: 1200,            
      in_house: false
    )

    match(
      type: 'appstore',
      git_basic_authorization: Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"),
      readonly: true,
      keychain_name: keychain_name,
      keychain_password: keychain_password,
      api_key: api_key
    )

    gym(
      configuration: "Release",
      workspace: "./ios/App/App.xcworkspace",
      scheme: "App",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: { 
            DEVELOPER_APP_ID => "#{PROVISIONING_PROFILE_SPECIFIER}"
        }
      }
    )

    pilot(
      apple_id: "#{DEVELOPER_APP_ID}",
      app_identifier: "#{DEVELOPER_APP_IDENTIFIER}",
      skip_waiting_for_build_processing: true,
      skip_submission: true,
      distribute_external: false,
      notify_external_testers: false,
      ipa: "./App.ipa"
    )

    delete_temp_keychain(keychain_name)
  end
  lane :submit_review do
    version = ''
    Dir.chdir("..") do
      file = File.read("package.json")
      data = JSON.parse(file)
      version = data["version"]
    end
    deliver(
      app_version: version,
      submit_for_review: true,
      automatic_release: true,
      force: true, # Skip HTMl report verification
      skip_metadata: false,
      skip_screenshots: false,
      skip_binary_upload: true
    )
  end
end

Appfile

app_identifier(ENV["DEVELOPER_APP_IDENTIFIER"])
apple_id(ENV["FASTLANE_APPLE_ID"])
itc_team_id(ENV["APP_STORE_CONNECT_TEAM_ID"])
team_id(ENV["DEVELOPER_PORTAL_TEAM_ID"])

Configura Fastlane match

Fastlane match è un nuovo approccio alla firma code su iOS. Fastlane match permette ai team di gestire facilmente i certificati e i provisioning profile necessari per le tue app iOS.

Crea un nuovo repository privato chiamato certificates, ad esempio sul tuo account personale GitHub o sulla tua organizzazione.

Inizializza Fastlane match per la tua app iOS.

fastlane match init

Poi seleziona l'opzione #1 (Git Storage).

[01:00:00]: fastlane match supports multiple storage modes, please select the one you want to use:1. git2. google_cloud3. s3?

Inserisci l'URL del repository appena creato.

[01:00:00]: Please create a new, private git repository to store the certificates and profiles there[01:00:00]: URL of the Git Repo: <YOUR_CERTIFICATES_REPO_URL>

Ora, all'interno della cartella Fastlane, troverai un file chiamato Matchfile e _git_url_deve essere impostato sull'URL HTTPS del repository dei certificati. Puoi anche usare SSH, ma richiede uno step diverso.

# ios/Matchfilegit_url("https://github.com/gitusername/certificates")storage_mode("git")type("appstore")

Ora passiamo a generare i certificati e inserisci le tue credenziali quando Fastlane Match te le chiede.

Ti verrà chiesto di inserire una passphrase. Ricordala bene, perché verrà usata in seguito dalle GitHub Actions per decriptare il repository dei certificati.

fastlane match appstore

Se è andato tutto bene, dovresti vedere qualcosa del genere:

[01:40:52]: All required keys, certificates and provisioning profiles are installed 🙌

Se hai riscontrato problemi con GitHub e i permessi necessari, forse questo post ti aiuterà a generare i token di autenticazione per git.

I certificati e i provisioning profile generati vengono caricati nelle risorse del repository dei certificati

Certificati App Store Connect

Infine, apri il tuo project in Xcode e aggiorna il provisioning profile per la release configuration della tua app.

Certificati Xcode

Alcune note importanti 💡

MATCH

Affinché la CI/CD possa importare certificati e provisioning profile, deve avere accesso al repository dei certificati. Puoi farlo generando un personal access token (creato in precedenza) con i permessi di lettura per i repository privati.

In GitHub, vai su Impostazioni → Impostazioni sviluppatore → Token di accesso personale → clicca su Generate New Token → seleziona lo repo scope → quindi clicca Generate token.

Crea un Personal access token

Conserva una copia del personal access token generato. Ti servirà in seguito per la variabile d'ambiente GIT_TOKEN.

Poi sostituisci il file match generato nella cartella Fastlane con Matchfile

CERTIFICATE_STORE_URL = ENV["CERTIFICATE_STORE_URL"]
GIT_USERNAME = ENV["GIT_USERNAME"]
GIT_TOKEN = ENV["GIT_TOKEN"]
FASTLANE_APPLE_ID = ENV["FASTLANE_APPLE_ID"]

git_url(CERTIFICATE_STORE_URL)
storage_mode("git")
type("appstore")
git_basic_authorization(Base64.strict_encode64("#{GIT_USERNAME}:#{GIT_TOKEN}"))
username(FASTLANE_APPLE_ID)

Questo verrà usato dalle GitHub Actions per importare certificati e provisioning profiles. La variabile sarà impostata nei GitHub Secrets, evitando di scriverli in chiaro nel file.

Elaborazione Build

In GitHub Actions, la fatturazione avviene in base ai minuti utilizzati per eseguire il tuo workflow CI/CD. Per esperienza, occorrono circa 10–15 minuti prima che una build venga elaborata in App Store Connect.

Per i progetti privati, il costo stimato per build può arrivare a $0.08/min x 15 min = $1.2, o più, a seconda della configurazione o delle dipendenze del tuo progetto.

Se hai le stesse preoccupazioni sui costi che ho io per i progetti privati, puoi mantenere il skip_waiting_for_build_processing a true.

Qual è l'inghippo? Devi aggiornare manualmente la conformità della tua app in App Store Connect dopo che il build è stato processato, per poter distribuire il build ai tuoi utenti.

Questo è solo un parametro opzionale da aggiornare se vuoi risparmiare sui minuti di build per i progetti privati. Per i progetti free, non dovrebbe essere affatto un problema. Vedi prezzi.

3. Configura le GitHub Actions

Configura i secret di GitHub

Ti sei mai chiesto da dove vengono i valori di ENV vengono? Beh, non è più un segreto: vengono dai secret del tuo progetto. 🤦

Imposta i secret di GitHub

1. APP_STORE_CONNECT_TEAM_ID - l'ID del tuo team App Store Connect se fai parte di più team.

2. DEVELOPER_APP_ID - in App Store Connect, vai sull'app → Informazioni sull'app → Scorri verso la General Information sezione della tua app e cerca Apple ID.

3. DEVELOPER_APP_IDENTIFIER - il bundle identifier della tua app.

4. DEVELOPER_PORTAL_TEAM_ID - l'ID del team sul Developer Portal, se fai parte di più team.

5. FASTLANE_APPLE_ID - l'Apple ID o l'email developer che usi per gestire l'app.

6. GIT_USERNAME &amp; GIT_TOKEN - Il tuo username git e il tuo personal access token.

7. MATCH_PASSWORD - la passphrase impostata durante l'inizializzazione di match verrà utilizzata per decriptare i certificati e i provisioning profile.

8. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>, es. match AppStore com.domain.blabla.demo.

9. TEMP_KEYCHAIN_USER &amp; TEMP_KEYCHAIN_PASSWORD - assegna un utente e una password temporanei per il keychain del workflow.

10. APPLE_KEY_ID — Chiave App Store Connect API 🔺Key ID.

11. APPLE_ISSUER_ID — Chiave API di App Store Connect 🔺Issuer ID.

12. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 File della chiave o contenuto della chiave di .p8, controllalo

13. CERTIFICATE_STORE_URL — L'URL del repo delle tue chiavi Match (es: https://github.com/***/fastlane_match.git)

4. Configura il file di workflow di GitHub

Crea una directory per il workflow di GitHub.

cd .github/workflows

All'interno della workflow cartella, crea un file chiamato build-upload-ios.ymle aggiungi quanto segue.

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: 2.7.2
      - uses: maierj/fastlane-action@v2.3.0
        env:
          DEVELOPER_APP_IDENTIFIER: ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
          DEVELOPER_APP_ID: ${{ secrets.DEVELOPER_APP_ID }}
          PROVISIONING_PROFILE_SPECIFIER: match AppStore ${{ secrets.DEVELOPER_APP_IDENTIFIER }}
          TEMP_KEYCHAIN_USER: ${{ secrets.TEMP_KEYCHAIN_USER }}
          TEMP_KEYCHAIN_PASSWORD: ${{ secrets.TEMP_KEYCHAIN_PASSWORD }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          CERTIFICATE_STORE_URL: https://github.com/${{ secrets.CERTIFICATE_STORE_REPO }}.git
          GIT_USERNAME: ${{ secrets.GIT_USERNAME }}
          GIT_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          FASTLANE_APPLE_ID: ${{ secrets.FASTLANE_APPLE_ID }}
          MATCH_USERNAME: ${{ secrets.FASTLANE_APPLE_ID }}
          MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
          DEVELOPER_PORTAL_TEAM_ID: ${{ secrets.DEVELOPER_PORTAL_TEAM_ID }}
        with:
          lane: closed_beta
      - name: Upload release bundle
        uses: actions/upload-artifact@v2
        with:
          name: ios-release
          path: ./App.ipa
          retention-days: 60

Questo workflow dovrebbe essere attivato dopo ogni GitHub tag, se hai bisogno di automatizzare i tag, consulta Build e release automatiche con le action di GitHub prima.

Questo workflow scaricherà le dipendenze NodeJS, le installerà e builderrà la tua app JavaScript.

Ogni volta che invii un nuovo commit, verrà creata una release in TestFlight.

La tua app non deve usare Ionic, è necessario solo Capacitor. Può avere vecchi moduli Cordova, ma è preferibile usare il plugin JS Capacitor.

5. Trigger del workflow

Crea un commit

Esegui un commit, dovresti vedere il workflow attivo nel repository.

Avvia il workflow

Pusha i nuovi commit sul branch main o development per avviare il workflow.

Iniziato con il commit

Dopo pochi minuti, la build sarà disponibile nella dashboard di App Store Connect.

Dashboard Testflight

Si può deployare da locale?

Sì, è possibile e non richiede alcuno sforzo.

Immagina di avere un repository privato, di aver esaurito i minuti del piano free e di non voler pagare per nuovi release, o forse preferisci inviare l'app manualmente.

Cominciamo!

Ok, per prima cosa dobbiamo creare in my_project_path/fastlane percorso di un file chiamato .env, nello stesso percorso di Fastfile, per poter creare le stesse secret proprietà presenti nel nostro _GitHub, come mostrato sotto:

file .env per il deploy da locale

Ora puoi andare nel terminale ed eseguire il Fastlane dalla tua macchina:

fastlane closed_beta

❌ Nota importante sul .env file, dato che preferiamo non esporre questi dati, dobbiamo aggiungerli nel nostro .gitignore, in questo modo: ❌

fastlane/*.env

Dovrebbe funzionare esattamente come con GitHub Actions sulla macchina remota, ma sulla nostra macchina locale. 🍻

Esecuzione locale di Fastlane

Esecuzione da terminale: $ Fastlane closed_beta

Se sei arrivato fin qui, complimenti: ora hai un processo completamente automatizzato per le tue app iOS con Fastlane e GitHub Actions.

Ogni volta che invii un nuovo commit, verrà generata una release nella beta channel della Google Play console. Aggiornerò questo blog con i vostri feedback; se avete domande o suggerimenti, scrivetemi via email. martin@capgo.app

Build sul tuo dispositivo

Se hai ancora bisogno di fare il build sul tuo dispositivo, devi aggiungerlo manualmente al provisioning. Collega il dispositivo al Mac e apri il menu del dispositivo trova il menu iOS del dispositivo Poi copia il tuo identifier trova l'identifier iOS E poi avvia il comando: fastlane register_new_device ti verrà chiesto di impostare il nome del dispositivo e l'identifier: imposta l'identifier iOS

se riscontri problemi

Se hai problemi con il dispositivo di dev che non riesce a testare, ecc., di solito questo risolve.

C'è un comando magico che può salvarti:

fastlane match nuke development
fastlane match development

Poi: Pulisci il progetto premendo Shift(⇧)+Command(⌘)+K o selezionando Product > Clean (potrebbe essere indicato come “Clean Build Folder”)

Poi prova a eseguire nuovamente l'app sul tuo dispositivo.

Grazie

Questo blog si basa sui seguenti articoli:

Continua con l'Automatic Capacitor IOS build con GitHub actions usando match

Se stai usando Build automatica iOS di Capacitor con GitHub Actions usando match per pianificare l'automazione CI/CD e connetterla a CI/CD con Capgo per il workflow del prodotto in Capgo CI/CD, Capgo Native Build per il workflow di prodotto in Capgo Native Builds, Integrazioni Capgo per il workflow di prodotto in Integrazioni Capgo, Integrazione CI/CD per i dettagli implementativi in CI/CD Integration, e Integrazione GitHub Actions per i dettagli implementativi in GitHub Actions Integration.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.