Notarisierung und TestFlight-Verteilung auf einem Cloud Mac mini automatisieren

DevOps & CI/CD ·ca. 6 Min. Lesezeit

Notarisierung und TestFlight-Verteilung auf einem Cloud Mac mini automatisieren

Notarisierung und TestFlight-Verteilung auf einem Cloud Mac mini automatisieren

Zwei Uhr morgens, im Team-Chat fragt jemand aus dem Ops-Team: "Wann landet die neue Version endlich in TestFlight?" – und du starrst zum dritten Mal auf einen fehlgeschlagenen codesign-Aufruf im Terminal. Wenn in der Notarisierungskette auch nur ein einziges Detail falsch konfiguriert ist, bleibt der gesamte Prozess beim Einreichen lautlos stehen: kein klarer Fehler, keine Freigabe, nur ein kryptischer Statuscode. Dieser Beitrag beschreibt, wie wir Signierung, Notarisierung und den Upload zu TestFlight auf einem Cloud Mac mini vollständig automatisiert und zu einer unbeaufsichtigten Pipeline zusammengeführt haben.

Warum Notarisierung für Teams mit globalem Rollout zur stillen Blockade wird

Seit Xcode 13 ist die Notarisierungs-Schnittstelle von altool veraltet – Apple hat alles auf notarytool konsolidiert. Viele CI-Skripte basieren aber noch auf älteren Tutorials, und schon ein falscher Zertifikatstyp, ein fehlender Timestamp-Parameter oder eine falsche Entitlements-Konfiguration reicht aus, damit die Anfrage stillschweigend abgelehnt wird. Die eigentliche Fehlermeldung steckt dann in einem separat abzurufenden JSON-Log. Erschwerend kommt hinzu, dass die Notarisierung serverseitig bei Apple in einer Warteschlange hängt – warum ein Durchlauf plötzlich 40 Minuten länger dauert, lässt sich lokal nicht nachvollziehen. Die einzige Gegenmaßnahme ist ein möglichst standardisierter, variablenarmer Prozess.

Wenn man diese Pipeline auf einem Cloud Mac mini betreibt, bleiben Zertifikate, API-Key und Keychain-Zustand dauerhaft auf einem durchgehend erreichbaren Node gespeichert – man muss nicht bei jedem Laptop-Wechsel oder Teamausstieg Zertifikate neu importieren. Genau deshalb setzen wir auf dedizierte physische Hardware im Mietmodell statt auf jedes Mal frisch aufgesetzte, temporäre Umgebungen.

Vorbereitung: Zertifikate und API Key

Zunächst sollten drei Dinge vorliegen:

  1. Apple Distribution-Zertifikat (für die App-Store-Verteilung) samt privatem Schlüssel, exportiert als .p12 und in die Keychain des Nodes importiert.
  2. App Store Connect API Key: auf der Seite "Nutzer und Zugriff" in App Store Connect erzeugt, die .p8-Datei herunterladen und sich Key ID sowie Issuer ID notieren. Das ist der entscheidende Baustein, um im gesamten Prozess ohne interaktiven Apple-Login auszukommen – sowohl Notarisierung als auch Upload laufen über die API-Key-Authentifizierung.
  3. Provisioning Profile: muss zu Bundle ID und Zertifikat passen. Am besten mit fastlane match zentral verwalten, statt es auf jeder Maschine einzeln zu erzeugen.

Zertifikat in die Keychain des Nodes importieren:

security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security import DistributionCert.p12 -k build.keychain -P "$CERT_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

Die .p8-Datei kommt an einen festen Pfad auf dem Node, z. B. ~/.appstoreconnect/private_keys/AuthKey_ABCDE12345.p8, mit Rechten 600 – und darf niemals in ein Code-Repository committet werden.

Von der Signierung zur Notarisierung mit notarytool

Beim Signieren müssen Timestamp und Hardened Runtime gesetzt sein, sonst schlägt die Notarisierung mangels sicherem Timestamp sofort fehl:

codesign --sign "Apple Distribution: Your Company" \
  --timestamp --options runtime \
  --entitlements App.entitlements \
  build/App.app

Nach dem Packen als .pkg oder dem Export einer .ipa via xcodebuild -exportArchive wird die Notarisierung eingereicht:

xcrun notarytool submit build/App.ipa \
  --key ~/.appstoreconnect/private_keys/AuthKey_ABCDE12345.p8 \
  --key-id ABCDE12345 \
  --issuer 69a6de7x-xxxx-47e3-e053-5b8c7c11a4d1 \
  --wait

--wait blockiert, bis Apples Server ein Ergebnis liefert – üblicherweise 3 bis 8 Minuten, bei Stoßzeiten auch mal über 20 Minuten. Ist der Status der zurückgegebenen submissionId Invalid, unbedingt das ausführliche Log abrufen, bevor man auf Verdacht neu signiert:

xcrun notarytool log <submissionId> \
  --key ~/.appstoreconnect/private_keys/AuthKey_ABCDE12345.p8 \
  --key-id ABCDE12345 --issuer 69a6de7x-xxxx-47e3-e053-5b8c7c11a4d1

Eine Falle, in die wir selbst getappt sind: App Sandbox war in den Entitlements aktiviert, aber eine dynamische Bibliothek eines Drittanbieters war nicht entsprechend signiert. Das notarytool-Log zeigte nur "The binary is not signed with a valid Developer ID certificate" – das sieht nach einem Zertifikatsproblem aus, war aber tatsächlich ein fehlendes Submodul-Signat. Erst codesign --verify --deep --verbose=4 mit schrittweiser Prüfung jeder Ebene hat die betroffene Datei zutage gebracht.

Automatische Verteilung an TestFlight mit fastlane pilot

Nach erfolgreicher Notarisierung erfolgt der Upload zu TestFlight per fastlane pilot, ebenfalls über API-Key-Authentifizierung, ohne interaktiven Login:

lane :beta do
  api_key = app_store_connect_api_key(
    key_id: ENV["ASC_KEY_ID"],
    issuer_id: ENV["ASC_ISSUER_ID"],
    key_filepath: ENV["ASC_KEY_PATH"]
  )

  build_app(scheme: "App", export_method: "app-store")

  pilot(
    api_key: api_key,
    skip_waiting_for_build_processing: true,
    changelog: "Automatisierte Verteilung: Absturz beim Start und Netzwerk-Retry-Logik behoben"
  )
end

skip_waiting_for_build_processing: true sorgt dafür, dass das Skript nach dem Upload sofort beendet wird, statt andere Aufgaben auf dem Node zu blockieren. Den weiteren Verarbeitungsstatus kann man per Polling abfragen oder einfach auf Apples Benachrichtigungs-E-Mail warten – die CI-Zeit sollte nicht mit Warten verschwendet werden.

Häufige Fehler und Checkliste zur Diagnose

Fehlermeldung (Stichwort) Wahrscheinliche Ursache Lösung
signature does not include a secure timestamp codesign wurde ohne --timestamp ausgeführt Neu signieren mit --timestamp --options runtime
The binary is not signed with a valid Developer ID Ein eingebettetes Framework/eine dynamische Bibliothek ist nicht signiert Mit codesign --verify --deep Ebene für Ebene prüfen und einzeln nachsignieren
Missing Info.plist key: ITSAppUsesNonExemptEncryption Verschlüsselungs-APIs werden genutzt, aber nicht deklariert Entsprechenden Key in der Info.plist ergänzen oder Ausnahme je nach Sachlage deklarieren
Invalid Provisioning Profile Profile passt nicht zu Zertifikat/Bundle ID Mit fastlane match Zertifikate und Profile neu synchronisieren
Lange keine Reaktion nach dem Einreichen Warteschlange auf Apples Servern Nicht erneut einreichen, sondern warten oder den Status der submissionId abfragen

Diese Tabelle gehört in die Troubleshooting-Doku des Teams – sie erspart viele Diskussionen darüber, wer eigentlich "schuld" ist.

Die Pipeline als Cron-Job auf dem Cloud Mac mini verankern

Der letzte Schritt besteht darin, die gesamte Pipeline automatisch laufen zu lassen, statt sie manuell anzustoßen. Auf dem Cloud Mac mini lässt sich über launchd ein Job registrieren, der auf einen Git-Webhook reagiert oder in festen Intervallen pollt und Signierung, Notarisierung und Upload nacheinander ausführt. Bei einem Fehlschlag sollte die verantwortliche Person per E-Mail oder Ticketsystem benachrichtigt werden, statt dass das Skript einfach stillschweigend beendet wird. Da der Node eine dedizierte physische Maschine ist und dauerhaft online bleibt, werden Keychain-Zustand und Zertifikate nicht wie in temporären Containern bei jedem Lauf neu initialisiert – dadurch sind Erfolgsquote und Wartezeiten der Notarisierung deutlich stabiler.

Häufig gestellte Fragen

notarytool meldet fehlenden secure timestamp, was tun?

Beim Signieren fehlte ein Zeitstempel-Server. Erneut mit codesign --timestamp --options runtime signieren und neu einreichen.

Ist ein langer Processing-Status in TestFlight normal?

Apple braucht meist 15-60 Minuten. Über 2 Stunden lohnt ein Blick in die Aktivität von App Store Connect — oft fehlt ITSAppUsesNonExemptEncryption und löst manuelle Prüfung aus.

Muss ich mich auf dem Cloud Mac mini mit einer Apple-ID anmelden?

Nein. Zertifikat in die Keychain des Nodes importieren und die .p8-Datei des App Store Connect API Keys samt Umgebungsvariablen ablegen reicht für den headless Betrieb.

Brauchen Sie einen dedizierten Mac mini für Ihre Builds?

Physische Standorte in Singapur, Tokio, Seoul, Hongkong und US-West – Mietdauer ab einem Tag, VNC-/SSH-Zugangsdaten innerhalb von 10 Minuten.

Jetzt Mac mini mieten