Angebot

Xcode Product → Archive: Der komplette Ablauf (und warum CI hier hängenbleibt)

CI Xcode Archive · iOS Release
2026-06-15 ~14 Min.

Lokal reicht ein Klick auf Product → Archive, und der Organizer exportiert die IPA. In der CI hängt xcodebuild archive oft 20 Minuten lang, läuft in ein Timeout oder bricht still an derselben Stelle ab. Der Unterschied liegt selten am Xcode-Wissen — entscheidend ist, ob ihr Archive als beobachtbare Pipeline behandelt statt als Black-Box-Befehl.

Dieser Artikel geht jede Phase durch: was Build → Archive → Sign → Export wirklich tut, wie xcodebuild den Menüpunkt abbildet, und liefert eine Sieben-Kategorien-Symptom-zu-Maßnahme-Tabelle für die Fehler, die iOS- und Flutter-Teams auf GitHub Actions und Self-hosted Mac-Runnern am häufigsten sehen.

Xcode-Archive-Pipeline und CI-Workflow-Diagramm
Lokal liefert Archive GUI-Feedback; in der CI braucht dieselbe Kette logische Segmentierung — sonst sieht man nur „Job lief 40 Minuten“.

Was zuerst wichtig ist

  1. Archive ≠ Build: Archive läuft immer mit Release + Any iOS Device und erzeugt ein .xcarchive. Ein lokaler Debug-Build, der durchläuft, garantiert kein erfolgreiches Archive.
  2. CI-Hänger clustern in vier Segmenten: Scheme/Konfiguration, Compile-Cache, Signing & Schlüsselbund und exportArchive — erst das Segment in den Logs finden, dann fixen.
  3. xcodebuild archive entspricht dem Menüpunkt, aber die CI muss explizit -scheme, -configuration Release und -destination 'generic/platform=iOS' übergeben.
  4. „Hängt ohne Output“ bedeutet meist Warten auf Schlüsselbund-Freigabe, Provisioning-Profile-Download oder Swap unter Speicherdruck — kein toter xcodebuild-Prozess.
  5. Self-hosted Cloud-Mac-Runner mit persistentem DerivedData und dediziertem Signing-Schlüsselbund sind oft eine Größenordnung stabiler als kaltes Archive auf ephemeren Hosted-Runnern.

1. Archive vs. Build: was sich wirklich ändert

Viele Teams fassen CI-Fehler als „xcodebuild kaputt“ zusammen. Tatsächlich sind Cmd+B Build und Product → Archive zwei verschiedene Pfade:

Dimension Build (⌘B) Archive
Typische Configuration Debug (lokale Entwicklung) Release (App Store / TestFlight)
Destination Simulator oder angeschlossenes Gerät Any iOS Device (arm64)
Output .app in DerivedData .xcarchive + exportierbare IPA
Signing Development-Zertifikat reicht für manche Targets Distribution-Zertifikat + Provisioning Profile erforderlich
Optimierung Niedrig — schnelle Kompilierung Volle Optimierung — deutlich längere Compile-Zeit

„3 Minuten lokal“ und „20 Minuten in der CI, dann Fehler“ widersprechen sich also nicht: lokal läuft oft Debug + Simulator, in der CI Release + Gerätearchitektur + vollständige Signing-Kette. Vergleichbare Bedingungen herstellen, bevor ihr optimiert. Speziell zu Compile-Zeit-Unterschieden: warum xcodebuild in der CI 2–3× langsamer ist als lokal.

2. Vom Menü zur CLI: Product → Archive in der Shell

Ein Klick auf Product → Archive in der Xcode-GUI entspricht in etwa:

  1. Ein Shared Scheme mit Release-Konfiguration auswählen;
  2. Destination auf Any iOS Device setzen (nicht Simulator);
  3. xcodebuild archive ausführen und Output nach ARCHIVE_PATH schreiben;
  4. (Optional) Distribute App im Organizer → entspricht xcodebuild -exportArchive.

Minimaler Archive-Befehl für die CI (Pfade an euer Projekt anpassen):

xcodebuild archive \
  -workspace MyApp.xcworkspace \
  -scheme MyApp \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
  CODE_SIGN_STYLE=Manual \
  DEVELOPMENT_TEAM=XXXXXXXXXX \
  | tee archive.log

Drei Parameter, die Teams oft vergessen:

  • -destination 'generic/platform=iOS': ohne diesen Parameter fällt xcodebuild oft auf einen Simulator zurück — Archive schlägt fehl oder verhält sich unvorhersehbar.
  • -archivePath: muss beschreibbar sein; Berechtigungsprobleme in Hosted-Runner-Temp-Verzeichnissen führen zu „kompiliert, aber Archive konnte nicht geschrieben werden“.
  • Scheme muss Shared sein und in Git liegen: ein nicht geteiltes lokales Scheme existiert nach CI-Checkout nicht → scheme not found.

Offizielle Referenzen: Apple — Building your app, Distributing your app.

3. Vier-Phasen-Pipeline im Überblick

┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│ 1. Resolve   │ →  │ 2. Archive   │ →  │ 3. Sign      │ →  │ 4. Export    │
│ SPM/Pods     │    │ Release voll │    │ Certs/Profile│    │ IPA / Upload │
│ DerivedData  │    │ → .xcarchive │    │ Schlüsselbund│    │ TestFlight   │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘
     pod install          xcodebuild           codesign           exportArchive
     oft 5–15 Min.        archive 8–25 Min.    stille Hängerzone  plist-Fehler häufig

Wenn die CI „hier hängenbleibt“, fragt zuerst: zu welcher Phase gehört die letzte Log-Zeile? Stoppt es bei CompileSwift, ist es Compile; bei CodeSign Signing; bei exportArchive Export-Konfiguration. Phasen zu vermischen führt zu endlosem Cache-Löschen.

4. Phase 1: Resolve & Compile (warum CI langsamer ist)

Dependency-Resolution und Kompilierung vor dem Archive sind lokal meist „warm“, in der CI „kalt“:

  • CocoaPods / SPM: die CI führt pod install pro Job von Grund auf aus — Logs voller Installing … und 10 Minuten weg, bevor Archive startet. Siehe Flutter-iOS-CI: Cache-Propagation für Pods.
  • Kaltes DerivedData: voller Release-Build ohne Cache bedeutet, dass jedes native Pod-Modul neu kompiliert wird. DERIVED_DATA_PATH auf einem SSD-gestützten Self-hosted-Runner zu pinnen halbiert oft das zweite Archive.
  • Speicherdruck: Archive hat höhere Spitzen als Debug; eine 16-GB-Maschine mit parallelen Jobs swapt — CPU bleibt niedrig, aber nichts bewegt sich. Siehe Runner-Speicher und Swap-Steuerung.

Schnellcheck: zeigen die Logs viel CompileC / SwiftCompile für Drittanbieter-Pods, die ihr nicht angefasst habt, zuerst Cache fixen — nicht Signing.

5. Phase 2: Archive (.xcarchive erzeugen)

Nach erfolgreichem xcodebuild archive sollte unter -archivePath diese Struktur stehen:

MyApp.xcarchive/
  Info.plist
  Products/Applications/MyApp.app
  dSYMs/...

Typische Fehlermuster:

  • Scheme-Archive-Action ohne angehaktes Target: lokal baut ein anderes Scheme, in der CI archiviert das CI-Scheme nichts.
  • Multi-Target / Extension falsch konfiguriert: Haupt-App läuft durch, Notification Service Extension scheitert am Signing — ganzes Archive fällt durch.
  • Build-Nummer / Versionskonflikt: die CI erhöht CFBundleVersion nicht; Upload scheitert später, wird aber „langsamem Archive“ angelastet.

Sofort validieren: ls -la "$ARCHIVE_PATH" und plutil -p "$ARCHIVE_PATH/Info.plist" ausführen und prüfen, ob ApplicationProperties existiert — nicht erst beim Export-Fehler feststellen, dass das Archive unvollständig ist.

6. Phase 3: Code Sign (die häufigste CI-Falle)

Lokal fragt Archive vielleicht nach Schlüsselbund-Zugriff; ihr klickt „Immer erlauben“ und die Pipeline läuft weiter. Unbeaufsichtigte CI hat diesen Schritt nicht, deshalb:

  • errSecInternalComponent, User interaction is not allowed: privater Schlüssel liegt im Login-Schlüsselbund; CI-Benutzer hat keine GUI-Freigabe.
  • Abgelaufenes Profil / Bundle-ID-Mismatch: Log stoppt bei CodeSign mit Provisioning profile … doesn't match.
  • -allowProvisioningUpdates versehentlich aktiviert: erfordert interaktiven Apple-ID-Login; Headless-Umgebungen warten ewig.
  • Schlüsselbund nicht entsperrt: Job fehlt security unlock-keychain / set-key-partition-list am Anfang.

Empfohlen auf Cloud-Mac / Self-hosted-Runnern: eine dedizierte Schlüsselbund-Datei (nicht der Login-Schlüsselbund), Distribution-Zertifikat + privaten Schlüssel importieren, am Job-Start entsperren und als Default setzen. Vollständige Kette: Cloud-Mac iOS-CI: codesign und Schlüsselbund-Grenzen.

# Job-Anfang (Passwort über CI-Secret)
KEYCHAIN=$RUNNER_TEMP/ci-signing.keychain-db
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security import dist.p12 -k "$KEYCHAIN" -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN"
security list-keychains -s "$KEYCHAIN" login.keychain-db

Bei fastlane match oder Xcode Cloud für Credentials bleibt das Prinzip gleich: der CI-Prozess muss ohne Interaktion auf den privaten Schlüssel zugreifen können — nicht nur „das Zertifikat liegt irgendwo auf der Maschine“. Siehe fastlane match.

7. Phase 4: IPA exportieren (exportArchive)

Archive erzeugt nur .xcarchive; für TestFlight-Upload braucht es noch den Export. Distribute App im Menü entspricht:

xcodebuild -exportArchive \
  -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
  -exportPath "$RUNNER_TEMP/export" \
  -exportOptionsPlist ExportOptions.plist

Der method-Schlüssel in ExportOptions.plist legt den Export-Typ fest (app-store, ad-hoc, development usw.). Typische CI-Probleme:

  • method passt nicht zum Profil-Typ: Development-Profil mit app-store-Methode.
  • Fehlende teamID / signingCertificate: Manual-Signing-Fehler tauchen beim Export auf, werden aber „langsamem Archive“ zugeschrieben.
  • Upload mit Export gebündelt: altool / Transporter-Netzwerklatenz wird als „Archive-Timeout“ gezählt.

archive, export und upload in drei getimte Schritte aufteilen. Upload über xcrun altool --upload-app oder xcrun notarytool (macOS-Distribution) — vom Archive entkoppelt.

8. Sieben Ursachen: Symptom / Phase / Maßnahme

Symptom / Log-Schlüsselwort Phase Erste Maßnahme
Lange Stille, niedrige CPU Sign Schlüsselbund-Unlock prüfen; GUI-abhängige Provisioning-Auto-Updates deaktivieren
scheme 'Foo' not found Pre-flight Scheme als Shared markieren und committen; in CI mit -list verifizieren
No profiles for … were found Sign Bundle ID prüfen; Profile im Repo oder match-Fetch bestätigen
Viel CompileC bei unberührten Pods Compile DerivedData persistieren; kein clean innerhalb des Jobs
pod install > 8 Minuten Resolve ios/Pods cachen; pod install --deployment nutzen
Job von Plattform gekillt (kein Stacktrace) Global Timeout erhöhen; Swap / volle Platte prüfen (Disk-Governance)
exportArchive failed + plist-Fehler Export Export isoliert ausführen; ExportOptions.plist-Methode verifizieren

Im Einsatz: Logs von unten nach dem ersten error: scannen und in der Tabelle zuordnen — schneller als pauschales flutter clean.

9. Troubleshooting-Runbook: Segment zuerst, Fix danach

  1. Release-Archive lokal reproduzieren: Product → Scheme → Edit Scheme → Archive nutzt Release; Destination ist Any iOS Device. Scheitert es lokal, keine CI-Minuten verbrennen.
  2. Vier-Segment-Timing in der CI: pod installarchiveexportArchiveupload; jede Dauer ins Step-Summary schreiben.
  3. Vollständige Logs behalten: zu aggressive Log-Prettifier vermeiden; Signing-Fehler brauchen rohe CodeSign-Zeilen.
  4. Xcode-Version pinnen: Pfad mit xcode-select oder sudo xcode-select -s fixieren; mit .xcode-version oder Workflow-macos-15 abgleichen.
  5. 48-Stunden-Validierung: denselben Commit zweimal archivieren; der zweite Lauf sollte spürbar schneller sein (Cache-Hit). Wenn nicht: Speicher und Disk prüfen.

Für Release-Sprints oder temporäre Build-Kapazität: kurzfristiger Remote-Mac für Release-Sprints statt Archive-Timeouts auf ephemeren Hosted-Runnern zu bekämpfen.

10. GitHub-Actions-Vorlage

jobs:
  archive-ios:
    runs-on: [self-hosted, macOS, ios]  # oder macos-14 Hosted-Runner
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4

      - name: Install pods
        run: pod install --deployment
        working-directory: ios

      - name: Archive
        run: |
          set -o pipefail
          xcodebuild archive \
            -workspace ios/MyApp.xcworkspace \
            -scheme MyApp \
            -configuration Release \
            -destination 'generic/platform=iOS' \
            -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
            | tee archive.log
        env:
          DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer

      - name: Export IPA
        run: |
          xcodebuild -exportArchive \
            -archivePath "$RUNNER_TEMP/MyApp.xcarchive" \
            -exportPath "$RUNNER_TEMP/export" \
            -exportOptionsPlist ios/ExportOptions.plist

Für Self-hosted-Runner-Label-Routing, launchd-Keepalive und CI-Benutzer-Isolation: Mac-mini-Self-hosted-Runner-Setup und Flutter + GitHub Actions Architektur-Überblick.

11. Fazit

Product → Archive ist nicht „etwas länger bauen“ — es ist die vollständige Kette aus Release-Kompilierung + Archive + Distribution-Signing + optionalem Export. Wenn die CI hier hängenbleibt, ist Xcode selten kaputt. Häufiger wenden Teams Debug-Gewohnheiten auf Release an, behandeln Signing als reines GUI-Thema oder verschmelzen Export und Archive zu einem undurchsichtigen Schritt.

Konkrete nächste Schritte: lokales Archive zuerst durchbekommen → Vier-Segment-CI-Timing einbauen → dedizierter Schlüsselbund + persistenter Cache → dann Maschinen ergänzen oder Specs hochziehen. Für Apple-Plattform-Teams spart ein Self-hosted Cloud-Mac-Runner oft mehr Gesamtzeit als endloses Hosted-Runner-Cache-Tuning — weil Archive einen stabilen Warm Path braucht, keinen Einmal-Chip.

Die komplette Archive-Pipeline auf Cloud Mac

Dediziertes M4-Bare-Metal für xcodebuild archive, dedizierter Signing-Schlüsselbund und persistentes DerivedData. Tagesmiete, um Archive-Zeit und Stabilität zu benchmarken; Monats-Tarif, wenn ihr einen permanenten Runner wollt.

Cloud-Mac-Tarife ansehen · M4-Specs · Onboarding-Checkliste