Automatisierte iOS Builds mit GitHub Actions via Match
CI/CD für Capacitor Apps einzurichten, kann komplex und zeitaufwendig sein. Das hier musst du wissen:
Empfehlung für neue Builds: Nutze Capgo Build
Wir empfehlen jetzt die Nutzung von Capgo Build mit Capgo CLI für native Capacitor Builds. Dieser Fastlane Match Guide ist für Teams gedacht, die bestehende GitHub Actions Pipelines pflegen. Neue iOS Builds sollten jedoch Capgo CLI nutzen, damit ihr Fastlane, Match-Repos, Xcode Runner, Zertifikate und Upload-Skripte nicht selbst verwalten müsst.
Capgo Build für CI/CD von Capgo
Spart die Wartung von Fastlane Match, Xcode Runner, Zertifikaten, Provisioning Profiles und Upload-Skripten. Capgo Build führt signierte native iOS Builds aus deiner bestehenden CI/CD Pipeline aus:
- Funktioniert mit deiner Pipeline: Trigger Capgo Build via GitHub Actions, GitLab CI, Jenkins oder lokale Skripte nach deinem Web-Build und
npx cap sync. - Signing via CI Secrets: Behalte App Store Connect Keys, Zertifikate, Provisioning Profiles, Passwörter und Team IDs sicher in deinen eigenen CI Secrets.
- Kein Wartungsaufwand für native Runner: Capgo Build bietet gewartete Apple-Buildumgebungen, sodass du dich nicht um macOS-Runner, Xcode-Images, Fastlane oder Match-Repositories kümmern musst.
- Artifacts und Submission: Lade signierte Artifacts für die QA herunter oder reiche Release-Builds über Capgo CLI ein.
Preise
- Capgo Pläne ab 12 $/Monat
- Inklusive OTA-Updates und ca. 15 Native Builds pro Monat
- Zusätzliche Build-Minuten werden per Credit abgerechnet
Capgo Build in CI/CD einrichten
Anleitung zur manuellen Einrichtung
Das müssen Sie tun:
Continuous Delivery für iOS mit Fastlane und GitHub Actions via match
Voraussetzungen
Bevor du mit dem Tutorial weitermachst…
- Stelle sicher, dass Fastlane installiert ist auf deiner Entwicklungsmaschine.
- iOS Developer Program Mitgliedschaft.
- Lust zu lesen 😆…
- Ein Team mit vielen Devs, ansonsten empfehlen wir fastlane cert für einfachere Workflows.
Wichtige Infos zum Preis

https://github.com/features/actions
Der Service ist bis zum Limit „kostenlos“, je nach gewählter Maschine.
Wir werden eine macOS Maschine nutzen; ihren Preis und ihre Limits siehst du im Screenshot (Preise Stand Tutorial-Erstellung, Änderungen vorbehalten).
🔴 Nachdem Anforderungen und Preise geklärt sind, geht es – wenn du magst – weiter…
📣 In diesem Post gehen wir davon aus, dass die App in iTunes Connect erstellt ist und die Zertifikate aus dem Apple-Ökosystem vorliegen – Fastlane kopiert alles!
Tauchen wir ein! 🤿
Schritte in diesem Post
- App Store Connect API mit Fastlane Match nutzen
- Anforderungen
- Einen App Store Connect API Key erstellen
- Einen App Store Connect API Key verwenden
- Fastlane-Dateien kopieren
- Fastlane match konfigurieren
1. App Store Connect API mit Fastlane Match nutzen
Seit Februar 2021 ist für alle Nutzer die Zwei-Faktor-Authentifizierung oder eine Zwei-Schritt-Verifizierung erforderlich, um sich bei App Store Connect anzumelden. Diese zusätzliche Sicherheitsebene für Ihre Apple ID stellt sicher, dass nur Sie Zugriff auf Ihr Konto haben.
Von Apple Support
Um mit match zu starten, müssen Sie Ihre bestehenden Zertifikate widerrufen. Aber keine Sorge, Sie erhalten direkt neue.
Anforderungen
Um App Store Connect API nutzen zu können, benötigt Fastlane drei Dinge.
- Issuer-ID.
- Key-ID.
- Key-Datei oder Key-Inhalt.
Einen App Store Connect API Key erstellen
Um Keys zu generieren, benötigst du Admin-Rechte in App Store Connect. Falls du diese Berechtigung nicht hast, leite diesen Artikel an die zuständige Person weiter und folge diesen Schritten.
1 — Logge dich bei App Store Connect ein.
2 — Wähle Benutzer und Zugriff.

3 — Wähle den Tab Integration.

4 — Klicke auf Generate API Key oder das Add (+)-Icon.

5 — Gib einen Namen für den Key ein. Der Name dient nur deiner Referenz und ist nicht Teil des Keys selbst.

6 — Wähle unter Zugriff die Rolle für den Key. Für Keys gelten dieselben Rollen wie für Benutzer in deinem Team. Siehe Rollenberechtigungen. Wir empfehlen die Auswahl von App Manager.
7 — Klicke auf Generate.
Der Zugriff eines API Keys kann nicht auf bestimmte Apps beschränkt werden.
Der Name des neuen Keys, die Key-ID, ein Download-Link und weitere Informationen werden auf der Seite angezeigt.

Hier kannst du alle drei benötigten Informationen abrufen.
<1> Issue-ID.
<2> Key ID.
<3> Klicke auf „API Key downloaden“, um deinen API Private Key herunterzuladen. Der Download-Link erscheint nur, wenn der Private Key noch nicht heruntergeladen wurde. Apple speichert keine Kopie des Private Keys. Du kannst ihn also nur einmal herunterladen.
🔴 Speichere deinen Private Key an einem sicheren Ort. Du solltest deine Keys niemals teilen, sie nicht in einem code Repository speichern oder Keys in clientseitigem code einbinden.
Einen App Store Connect API Key verwenden
Die API Key-Datei (die heruntergeladene p8-Datei), die Key ID und die Issuer ID werden benötigt, um den JWT-Token für die Autorisierung zu erstellen. Es gibt verschiedene Wege, diese Informationen über die neue Fastlane-Action einzugeben, app_store_connect_api_key. Weitere Möglichkeiten findest du in der Fastlane-Dokumentation. Ich zeige diese Methode, weil sie meiner Meinung nach der einfachste Weg für die meisten gängigen CI-Systeme ist, bei denen man Umgebungsvariablen setzen kann.
Jetzt können wir Fastlane mit dem App Store Connect API Key verwalten, super!
2. Fastlane-Dateien kopieren
Fastlane ist eine Ruby-Library, die entwickelt wurde, um gängige Mobile-Development-Tasks zu automatisieren. Mit Fastlane kannst du eigene „Lanes“ konfigurieren, die eine Reihe von „Actions“ bündeln, um Aufgaben auszuführen, die du normalerweise in Android Studio erledigen würdest. Mit Fastlane lässt sich viel machen, aber für dieses Tutorial nutzen wir nur eine Handvoll Core-Actions.
Erstelle einen Fastlane-Ordner in deinem Projekt-Root und kopiere die folgenden Dateien hinein: 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"])
Fastlane match konfigurieren
Fastlane match ist ein neuer Ansatz für das code Signing unter iOS. Fastlane match erleichtert Teams die Verwaltung der benötigten Zertifikate und Provisioning Profiles für deine iOS-Apps.
Erstelle ein neues privates Repository namens certificates, zum Beispiel auf deinem GitHub Personal Account oder deiner Organisation.
Initialisiere Fastlane match für deine iOS-App.
fastlane match init
Wähle dann Option #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?
Gib die URL des neu erstellten Repositories an.
[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>
Du findest nun im Fastlane-Ordner eine Datei namens Matchfile und
_git_url_muss die HTTPS-URL des Zertifikats-Repos enthalten. Optional ist auch SSH möglich, erfordert jedoch einen zusätzlichen Schritt.
# ios/Matchfilegit_url("https://github.com/gitusername/certificates")storage_mode("git")type("appstore")
Als Nächstes generieren wir die Zertifikate und geben bei der Abfrage von Fastlane Match deine Zugangsdaten ein.
Du wirst aufgefordert, eine Passphrase festzulegen. Merke sie dir gut, da GitHub Actions sie später zum Entschlüsseln deines Zertifikats-Repos benötigen.
fastlane match appstore
Wenn alles geklappt hat, sollte es etwa so aussehen:
[01:40:52]: All required keys, certificates and provisioning profiles are installed 🙌
Falls du Probleme mit GitHub und den erforderlichen Berechtigungen hattest, hilft dir vielleicht dieser Post bei der Generierung von Git-Authentifizierungs-Tokens weiter.
Die generierten Zertifikate und Provisioning Profiles werden in das Zertifikats-Repo hochgeladen.

Zuletzt öffne dein project in Xcode und aktualisiere das Provisioning Profile für die Release-Konfiguration deiner App.

Ein paar Hinweise 💡
MATCH
Damit die CI/CD die Zertifikate und Provisioning Profiles importieren kann, benötigt sie Zugriff auf das Zertifikate-Repository. Erstelle dazu einen Personal Access Token mit Leserechten für private Repositories.
Gehe in GitHub zu Einstellungen → Entwickler-Einstellungen → Personal Access Tokens → klicke auf Generate New Token → wähle den repo Scope aus → und klicke auf Generate token.

Erstelle eine Kopie des generierten Personal Access Tokens. Du benötigst ihn später für die Umgebungsvariable. GIT_TOKEN.
Ersetze dann deine generierte Match-Datei im Fastlane-Ordner durch 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)
Dies wird von GitHub Actions genutzt, um Zertifikate und Provisioning Profiles zu importieren. Die Variable wird in GitHub Secrets gesetzt, statt sie hart im Code zu hinterlegen.
Build-Verarbeitung
Bei GitHub Actions erfolgt die Abrechnung basierend auf den Minuten, die du für deinen CI/CD-Workflow verbrauchst. Erfahrungsgemäß dauert es etwa 10–15 Minuten, bis ein Build in App Store Connect verarbeitet werden kann.
Bei privaten Projekten können die geschätzten Kosten pro Build bis zu $0,08/Min. x 15 Min. = $1,2betragen, oder mehr, je nach Konfiguration oder Abhängigkeiten deines Projekts.
Wenn du bei privaten Projekten die gleichen Bedenken bezüglich der Kosten hast wie ich, kannst du den skip_waiting_for_build_processing zu true.
Wo ist der Haken? Du musst die Compliance deiner App in App Store Connect manuell aktualisieren, nachdem der Build verarbeitet wurde, um ihn an deine Nutzer zu verteilen.
Dies ist nur ein optionaler Parameter, um Build-Minuten bei privaten Projekten zu sparen. Bei kostenlosen Projekten sollte das gar kein Problem sein. Siehe Preise.
3. GitHub Actions einrichten
GitHub Secrets konfigurieren
Hast du dich jemals gefragt, woher die Werte für ENV kommen? Nun, es ist kein Geheimnis mehr – sie stammen aus den Secrets deines Projekts. 🤦

1. APP_STORE_CONNECT_TEAM_ID - die ID deines App Store Connect Teams, falls du in mehreren Teams bist.
2. DEVELOPER_APP_ID - gehe in App Store Connect zur App → App-Informationen → Scrolle nach unten zum General Information Bereich deiner App und suche nach Apple ID.
3. DEVELOPER_APP_IDENTIFIER - der Bundle Identifier deiner App.
4. DEVELOPER_PORTAL_TEAM_ID - der ID deines Developer Portal Teams, falls du in mehreren Teams bist.
5. FASTLANE_APPLE_ID - der Apple ID oder der Developer-E-Mail, mit der du die App verwaltest.
6. GIT_USERNAME & GIT_TOKEN - deinem Git-Benutzernamen und deinem Personal Access Token.
7. MATCH_PASSWORD - dem Passphrase, das du bei der Initialisierung von match vergeben hast; es wird zum Entschlüsseln der Zertifikate und Provisioning Profiles benötigt.
8. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>, z. B. match AppStore com.domain.blabla.demo.
9. TEMP_KEYCHAIN_USER & TEMP_KEYCHAIN_PASSWORD - weise deinem Workflow einen temporären Keychain-User und ein Passwort zu.
10. APPLE_KEY_ID — App Store Connect API Key 🔺Key ID.
11. APPLE_ISSUER_ID — App Store Connect API Key 🔺Issuer ID.
12. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Key-Datei oder Inhalt von .p8, überprüfen
13. CERTIFICATE_STORE_URL — Die Repo-URL deiner Match-Keys (z. B.: https://github.com/***/fastlane_match.git)
4. GitHub Workflow-Datei konfigurieren
Erstelle ein GitHub Workflow-Verzeichnis.
cd .github/workflows
Erstelle im workflow -Ordner eine Datei namens build-upload-ios.ymlund füge Folgendes hinzu.
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
Dieser Workflow sollte nach jedem GitHub ausgelöst werden Tag, falls du das Tagging automatisieren möchtest, schau hier: Automatischer Build und Release mit GitHub Actions Zuerst.
Danach zieht dieser Workflow deine NodeJS-Deps, installiert sie und baut deine JavaScript-App.
Jedes Mal, wenn du einen neuen Commit pushst, wird ein Release in TestFlight erstellt.
Deine App muss nicht Ionic nutzen, nur die Capacitor Basis ist zwingend erforderlich. Sie kann alte Cordova-Module enthalten, aber Capacitor JS-Plugins sollten bevorzugt werden.
5. Workflow triggern
Einen Commit erstellen
Erstelle einen Commit, solltest du den aktiven Workflow im Repository sehen.
Workflow auslösen
Die neuen Commits in den Branch pushen main oder development um den Workflow auszulösen.

Nach wenigen Minuten sollte der Build in deinem App Store Connect Dashboard verfügbar sein.

Deployment vom lokalen Rechner möglich?
Ja, das geht und ist absolut mühelos.
Stell dir vor, du hast ein privates Repository, die Minuten des Free-Plans sind aufgebraucht und du willst nicht für neue Releases zahlen, oder du bevorzugst das manuelle Einreichen der App.
Legen wir los
Ok, zuerst müssen wir in erstellen my_project_path/fastlane den Pfad zu einer Datei namens .env, direkt im selben Verzeichnis wie Fastfile, um das Gleiche erstellen zu können Secrets zu erstellen, die in unserem _GitHub enthalten sind, wie unten:
.env-Datei für das Deployment vom lokalen Rechner
Jetzt kannst du ins Terminal gehen und den folgenden Befehl ausführen: Fastlane von deinem Rechner:
fastlane closed_beta
❌ Wichtiges zum .env Datei: Da wir diese Daten nicht preisgeben wollen, müssen wir sie in unsere .gitignoreeintragen, etwa so: ❌
fastlane/*.env
Es sollte genauso funktionieren wie über GitHub Actions auf der Remote-Maschine, nur eben auf unserer lokalen Maschine. 🍻

Terminal-Ausführung: $ Fastlane closed_beta
Wenn du bis hierher gekommen bist: Herzlichen Glückwunsch! Du hast nun einen vollautomatisierten Prozess für deine iOS-Apps mit Fastlane und GitHub Actions.
Bei jedem neuen Commit wird ein Release im Google Play Console Beta-Channel erstellt. Ich werde diesen Blog mit eurem Feedback verbessern. Bei Fragen oder Anregungen schreibt mir einfach eine E-Mail. martin@capgo.app
Build auf deinem Gerät
Falls du weiterhin auf deinem Gerät builden musst, musst du sie manuell zum Provisioning hinzufügen.
Verbinde dein Gerät mit deinem Mac und öffne das Gerätemenü
Kopiere dann deine Identifier
Und starte dann den Befehl:
fastlane register_new_device
Du wirst aufgefordert, einen Gerätenamen und die Identifier festzulegen:

Falls Probleme auftreten
Wenn du Probleme damit hast, dass du auf deinem Dev-Device nicht testen kannst, hilft das normalerweise.
Ein magischer Befehl, der dich retten kann:
fastlane match nuke development
fastlane match development
Danach: Bereinige das Projekt mit Shift(⇧)+Command(⌘)+K oder über Product > Clean (evtl. auch „Clean Build Folder“ genannt)
Versuche dann erneut, die App auf deinem Gerät zu starten.
Vielen Dank
Dieser Blogpost basiert auf folgenden Artikeln:
- Continuous Delivery für iOS mit Fastlane und GitHub Actions
- Fastlane Dokumentation
- Diese GitHub Nachricht von @mrogunlana
Weiterlesen: Automatischer Capacitor iOS Build mit GitHub Actions via match
Wenn Sie ... verwenden Automatischer Capacitor iOS Build mit GitHub Actions via match um CI/CD-Automatisierung zu planen, verbinde sie mit Capgo CI/CD für den Produkt-Workflow in Capgo CI/CD, Capgo Native Builds für den Produkt-Workflow in Capgo Native Builds, Capgo Integrationen für den Produkt-Workflow in Capgo Integrations, CI/CD-Integration für das Implementierungsdetail in der CI/CD-Integration und GitHub Actions-Integration für das Implementierungsdetail in der GitHub Actions-Integration.