Zum Hauptinhalt springen
CI/CD iOS Tutorial

iOS-Apps mit Gitea-Aktionen erstellen und bereitstellen

iOS-Apps auf TestFlight hochladen: Ein Mac-Runner mit act_runner oder eine Linux-Runner mit Cloud-Builds.

Artikelcredits

Martin Donadieu

Schreiber

Valeria

Rezensent

Jordan

Redakteur

iOS-Apps mit Gitea Actions erstellen und bereitstellen

Gitea Actions can build iOS apps, but only on a Mac you register yourself, because Gitea has no hosted runners and Xcode only runs on macOS. The two practical setups are a Mac running act_runner Teams wählen Gitea, weil sie ihre Capacitor auf Infrastruktur kontrollieren möchten. Das sollte sich nicht ändern, nur weil die App Xcode benötigt. Beide Konfigurationen unten halten Ihre Quelle auf Ihrem Server. .gitea/workflows/.

Teams pick Gitea because they want their code on infrastructure they control. That should not change just because the app needs Xcode. Both setups below keep your source on your server.

Wie Gitea Actions Jobs ausführt

Gitea Actions verwendet eine GitHub-kompatible Syntax für Actions und Jobs werden auf act_runner Instanzen ausgeführt, die Sie gegenüber Ihrem Gitea-Server registrieren. Jeder Runner bewirbt Labels wie ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latestDie Teil nach dem Doppelpunkt bestimmt, wie Jobs ausgeführt werden:

  • docker://image führt den Job in einem Container aus. Dies ist die Standardkonfiguration für Linux-Runner.
  • host führt den Job direkt auf dem Rechner aus, mit dem, was dort installiert ist.

macOS kann nicht in einem Docker-Container ausgeführt werden, daher muss ein Mac-Runner ein host Dass hat Konsequenzen, die wir später erörtern werden.

Zwei Kompatibilitätsanmerkungen, bevor Sie eine Workflow aus GitHub kopieren:

  • Gitea liest .gitea/workflows/ und fällt auf .github/workflows/ zurück, wenn das erste Verzeichnis nicht existiert.
  • uses: actions/checkout@v4 funktioniert, weil Gitea die Aktionen von GitHub standardmäßig auflöst (die DEFAULT_ACTIONS_URL Einstellung). Luftdichte Instanzen müssen die verwendeten Aktionen duplizieren und sie über die volle URL referenzieren.

Option 1: Ein Mac-Runner mit act_runner

Registriere den Mac

Herunterladen der act_runner Binär für macOS (arm64 für Apple Silicon) von der Gitea-Releases-Seite herunter, dann hol dir einen Registrierungstoken von Site Administration > Aktionen > Runner (instanzweit), oder von den Organisation- oder Repository-Einstellungen.

./act_runner register --no-interactive \
  --instance https://gitea.example.com \
  --token <registration_token> \
  --name mac-mini-01 \
  --labels macos-arm64:host

./act_runner daemon

Führe den Daemon als Launch-Agent unter einem dedizierten Benutzer aus, damit er nach Neustarts wieder startet und seinen eigenen Login-Schlüsselkasten hat.

Installiere die Werkzeuge auf dem Host

Host mode means the job uses the Mac’s own tools. Install:

  • Xcode 26 (erforderlich für Uploads in App Store Connect seit dem 28. April 2026) und führen Sie sudo xcodebuild -license accept
  • Node.js 22 oder neuer und Bun (Capacitor 8 erfordert Node 22)
  • Ruby und fastlane oder verwenden Sie ein Gemfile mit bundle exec
  • CocoaPods, nur wenn Ihr iOS-Projekt es noch verwendet. Neue Capacitor 8-Projekte verwenden den Swift Package Manager.

Der Workflow

# .gitea/workflows/ios-mac.yaml
name: iOS release (Mac runner)

on:
  push:
    tags:
      - 'v*'

jobs:
  ios:
    runs-on: macos-arm64
    env:
      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 }}
      APP_STORE_CONNECT_TEAM_ID: ${{ vars.APP_STORE_CONNECT_TEAM_ID }}
      PROFILE_NAME: ${{ vars.IOS_PROFILE_NAME }}
    steps:
      - uses: actions/checkout@v4
      - run: bun install --frozen-lockfile
      - run: bun run build
      - run: bunx cap sync ios
      - name: Decode signing files
        run: |
          echo "${{ secrets.IOS_CERTIFICATE_BASE64 }}" | base64 --decode > dist.p12
          echo "${{ secrets.IOS_PROFILE_BASE64 }}" | base64 --decode > profile.mobileprovision
      - run: bundle exec fastlane ios beta
      - name: Clean up
        if: always()
        run: rm -f dist.p12 profile.mobileprovision

Der beta Der lane importiert das Zertifikat in einen temporären Schlüsselkette, wechselt das Projekt auf manuelles Signieren, erhöht die Buildnummer von TestFlight, archiviert und lädt hoch. Ein vollständiges Fastfile für ein Capacitor-Projekt ist in iOS-Apps mit Bitbucket Pipelines erstellen und bereitstellen; es funktioniert unverändert auf Gitea. Für den Hintergrund von fastlane siehe die GitHub-Actions iOS-Anleitung.

Der Kostenfaktor des Host-Modus

Der act_runner Die Dokumentation ist direkt über dies: Host-Aufträge sind nicht isoliert. In der Praxis:

  • Every job shares the same user, disk, and keychain. A job that dies halfway can leave a temporary keychain or a stale provisioning profile that breaks the next one.
  • Jeder Workflow, der die Bezeichnung ansteuert, läuft code auf diesem Mac mit Zugriff des Benutzers. Registrieren Sie nur Host-Aufträge für Repositorys, die Sie vertrauen, und vermeiden Sie es, sie auf Pull-Anforderungen von Forks auszuführen.
  • Die Datenbank von Xcode, Archivdateien und Simulator-Runzeitwerte wachsen, bis die Festplatte voll ist.
  • Xcode-Updates sind Ihre Sache, und Apple erhöht die Mindestanforderung SDK jedes Frühjahr.
  • Ein Mac baut einen Auftrag nach dem anderen. Release-Tageswarte.

Wenn Ihr Team Macs bereits verwendet und jemand sie besitzt, funktioniert dies. Wenn nicht, entfernt die nächste Option den Mac aus Ihrer Infrastruktur.

Option 2: Erstellen Sie iOS von einem Linux-Auftragsrunner mit Capgo Build

Capgo Build kompilet und signiert Capacitor-Apps auf Capgo-verwalteten Macs. Ihr Gitea-Auftrag bleibt auf dem Linux-Auftragsrunner, den Sie bereits haben. Es checkt aus code, baut die Web-Schicht, führt cap sync iosund ruft build request. Der Capgo CLI lädt die vorbereitete native Projektdatei hoch, streamt die Xcode-Protokolle zurück in die Gitea-Auftragsprotokoll und beendet sich nicht-null auf Fehler.

Dieses Modell passt sich gut an Gitea an.

  • Kein Eingangsverkehr. Capgo kloniert nicht Ihren Repository. Der Runner schiebt das vorbereitete Projekt über HTTPS aus, sodass ein Gitea-Server hinter einem Firewall oder VPN ohne Änderungen funktioniert.
  • Kein Repository-Token. Es gibt keine Git-Verbindung, die auf der Capgo-Seite konfiguriert werden muss.
  • Ihr Web-Quellcode bleibt zu Hause. Nur der native ios/ Ordner, Capacitor-Konfiguration und die native Teile von Plugins werden hochgeladen. src/, .git/und .env Dateien werden nicht.

Einmaliger Setup

Auf einem Entwickler-Computer:

bunx @capgo/cli@latest login
bunx @capgo/cli@latest app add
bunx @capgo/cli@latest build init --platform ios

build init erstellt oder verwendet Ihr Zertifikat für die Verteilung und das Provisionierungsprofil für den App Store und speichert die App Store Connect API-Schlüssel. Führen Sie eine lokale Build durch, um sicherzustellen, dass alles signiert ist:

bun run build && bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release

Dann exportieren Sie die Anmeldeinformationen:

bunx @capgo/cli@latest build credentials manage --appId com.example.app --platform ios
# select "Export to .env"

Hinzufügen von Geheimnissen zu Gitea

Öffnen Repository-Einstellungen > Aktionen > Geheimnisse und fügen Sie jede Schlüssel aus dem exportierten Datei hinzu, plus Ihre Capgo API-Schlüssel:

Geheimnis Zweck
CAPGO_TOKEN Capgo API Schlüssel mit Upload-Berechtigung
BUILD_CERTIFICATE_BASE64 Verteilungszertifikat (".p12)
P12_PASSWORD __CAPGO_KEEP_0__ __CAPGO_KEEP_1__-Schlüssel mit Upload-Berechtigung
CAPGO_IOS_PROVISIONING_MAP_BASE64 Verteilungszertifikat ("
APPLE_KEY_ID App Store Connect API Schlüssel-ID
APPLE_ISSUER_ID App Store Connect Aussteller-ID
APPLE_KEY_CONTENT Base64 .p8 Schlüssel
APP_STORE_CONNECT_TEAM_ID Apple-Team-ID

Gitea lehnt geheime und Variablenamen ab, die mit GITEA_ oder GITHUB_oder, also behalten Sie diese Namen bei. Legen Sie nicht-geheime Werte wie die App-ID unter Variablen und lesen Sie sie mit ${{ vars.NAME }}Secrets, die auf Organisationsebene gesetzt sind, werden von jedem Repository in ihr geteilt, was praktisch ist, wenn Sie mehrere Apps von einem Apple-Team ausliefern. Löschen Sie das exportierte Datei, wenn Sie fertig sind.

Veröffentlichen Sie auf Tag-Push

# .gitea/workflows/ios-release.yaml
name: iOS release

on:
  push:
    tags:
      - 'v*'

jobs:
  release-ios:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install --frozen-lockfile
      - run: bun run build
      - run: bunx cap sync ios
      - name: Build, sign, and upload to TestFlight
        env:
          CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          CAPGO_IOS_PROVISIONING_MAP_BASE64: ${{ secrets.CAPGO_IOS_PROVISIONING_MAP_BASE64 }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
        run: |
          bunx @capgo/cli@latest build request ${{ vars.APP_ID }} \
            --platform ios \
            --build-mode release \
            --ios-distribution app_store \
            --store-release-name "${{ gitea.ref_name }}"

Hinweise:

  • ${{ gitea.ref_name }} ist der Tagname. Das github.* wirkt auch als Alias, was bei der Portierung von Workflows hilft.
  • Gitea setzt CI=true in jedem Job, sodass das Capgo CLI nie auf interaktive Eingaben wartet.
  • Capgo erhöht die Buildnummer von der letzten TestFlight-Build, sodass zwei Tags in einer Reihe nie kollidieren.
  • Ohne --submit-to-store-reviewwird die Build in TestFlight landen und stoppen. Fügen Sie die Flagge hinzu, wenn Sie CI auch für die App-Überprüfung einreichen möchten.

Die Veröffentlichung ist jetzt git tag v2.3.0 && git push origin v2.3.0Jeder, der einen v* tag can ship, so add a protected tag rule for v* unter Einstellungen > Tags und beschränken Sie es auf Release-Manager.

Ad-hoc-Builds für Tester

Für QA-Builds, die direkt auf registrierten Geräten installiert werden, erstellen Sie in Ad-hoc-Modus und erhalten Sie einen Download-Link:

# .gitea/workflows/ios-adhoc.yaml
name: iOS ad hoc build

on:
  workflow_dispatch:

jobs:
  adhoc:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - run: bun install --frozen-lockfile && bun run build
      - run: bunx cap sync ios
      - name: Ad hoc build
        env:
          CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
          BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
          P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
          APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
          APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
          APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
          APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
        run: |
          echo "${{ secrets.ADHOC_PROFILE_BASE64 }}" | base64 --decode > adhoc.mobileprovision
          bunx @capgo/cli@latest build request ${{ vars.APP_ID }} \
            --platform ios \
            --ios-distribution ad_hoc \
            --ios-provisioning-profile ./adhoc.mobileprovision \
            --output-upload --output-retention 3d \
            --output-record /tmp/build.json
          bunx @capgo/cli@latest build last-output --path /tmp/build.json --field outputUrl

Ad-hoc-Builds benötigen ein Ad-hoc-Provisioning-Profil, das die Testgeräte auflistet. Speichern Sie dieses Profil bas64-codiert als Geheimnis (ADHOC_PROFILE_BASE64 oben), entschlüsseln Sie es in der Aufgabe und übergeben Sie es mit --ios-provisioning-profile. CLI-Flaggen haben Vorrang vor Umgebungsvariablen, daher überschreibt dies das App-Store-Profil für diese Aufgabe nur. Sammeln Sie UDIDs mit dem iOS UDID Finder. Für die meisten Tester ist die TestFlight-Interne Testung einfacher, da sie keine UDIDs benötigt.

Manuelle Freigabeabstimmung

GitHub ermöglicht es Ihnen, Reviewer auf einer Umgebung zu erfordern. Gitea Actions implementiert keine Umgebungsrichtlinien, daher wird eine Aufgabe, die eine Umgebung nennt, nicht für die Abstimmung anhalten. Zwei Muster funktionieren stattdessen:

  1. Schutz von Tags. Nur Release-Manager können pushen v* Tags und das Tag-Workflow schicken.
  2. Zuerst bauen, dann promoten. Auf jeden push bauen Sie main mit --ios-distribution ad_hoc oder laden Sie es in TestFlight für interne Tests hoch, dann verwenden Sie ein workflow_dispatch Workflow (verfügbar in aktuellen Gitea-Releases) um die genehmigte Commit mit --submit-to-store-review.

Schiffe Web-Änderungen ohne neue Binär

Die meisten Commits in einer Capacitor App ändern nur JavaScript, CSS und HTML. Das Wiederaufbauen von iOS für diese verliert Minuten und App-Review-Zeit. Mit Capgo Live-Updateskann eine Aufgabe die richtige Route wählen:

      - name: Live update or native build
        env:
          CAPGO_TOKEN: ${{ secrets.CAPGO_TOKEN }}
          # plus the iOS signing secrets from above
        run: |
          if bunx @capgo/cli@latest build needed ${{ vars.APP_ID }} --channel production; then
            bunx @capgo/cli@latest bundle upload ${{ vars.APP_ID }} --channel production
          else
            bunx cap sync ios
            bunx @capgo/cli@latest build request ${{ vars.APP_ID }} --platform ios --build-mode release
          fi

build needed wenn native Abhängigkeiten mit dem Live-Channel übereinstimmen, und 1 wenn eine neue Binär erforderlich ist. Zwingen Sie auch die native Route, wenn Dateien unter ios/ oder capacitor.config.* weil dies nicht Teil der Abhängigkeitsvergleich ist. Siehe Auto wählen live update oder native Build.

Mac-Runner gegen Cloud-Build

act_runner auf Ihrem Mac Capgo-Build von Linux
Hardware Sie kaufen und hosten es Keine
Xcode-Updates Manuell, pro Maschine Behandelt durch Capgo
Trennung zwischen Aufgaben Keine in Host-Modus Neue Build-Umgebung pro Aufgabe
Parallel-Builds Eines pro Mac Mehrere, von jedem Runner
Signierung von Dateien In Gitea-Secrets, entschlüsselt auf dem Mac In Gitea-Secrets, gesendet pro Build
Funktioniert hinter einem Firewall Ja Ja, nur HTTPS- Ausgangsverkehr
Native Swift-Anwendungen (kein Capacitor) Ja Nein, nur Capacitor-Anwendungen
Zeitlimit für die Erstellung Einstellbar durch dich 10 Minuten pro Erstellung

Fehlersuche

Symptom Lösung
Aufgabe bleibt ewig in der Warteschleife Kein Online-Runner hat die Bezeichnung runs-onÜberprüfe die Liste der Runner und die Schreibweise der Labels.
uses: actions/checkout@v4 Download fehlt Der Runner kann GitHub nicht erreichen. Spiegeln Sie die Aktion auf Ihrem Gitea wider und referenzieren Sie sie über die vollständige URL.
Das geheime Wert ist leer in der Aufgabe Der Name beginnt mit GITEA_/GITHUB_, oder das Geheimnis ist auf einem anderen Repository definiert.
security: SecKeychainItemImport: MAC verification failed Falsch .p12 Passwort, oder ein .p12 created by OpenSSL 3 ohne -legacy. Neu exportieren Sie es.
No profiles for 'com.example.app' were found Das Profil in den Geheimnissen passt nicht zum Bundle-Id oder Zertifikat. Wiederholen Sie den Vorgang. build init und neu exportieren.
Die Upload-Abnahme für SDK-Version wurde abgelehnt. Der Mac-Runner hat noch ein älteres Xcode als 26.
cap sync ios warnt vor CocoaPods auf Linux Erfreulich. Die Capgo-Baumachine installiert Pods.

Für weitere Fehlerarten siehe CI/CD für Capacitor: häufige Fallen und das Capgo-Build-Problembehandlungshandbuch.

Zusammenfassung

Gitea-Aktionen verarbeiten iOS fehlerfrei, sobald Sie entschieden haben, wo Xcode läuft. Ein Host-Modus-Mac-Runner gibt Ihnen die volle Kontrolle und die volle Verantwortung. Ein Linux-Job, der Capgo-Build aufruft, belässt Ihre Gitea-Konfiguration unverändert, benötigt keine Eingangsverbindung und wandelt eine Testflug-Veröffentlichung in eine Tag-Push um. Worauf Sie sich auch entscheiden, bewahren Sie das Signiermaterial in Gitea-Secrets auf, schützen Sie Ihre Veröffentlichungs-Labels und schicken Sie Web-Änderungen als Live-Updates, damit die native Pipeline nur dann läuft, wenn es muss.

Live-Updates für Capacitor-Apps

Wenn ein Web-Schicht-Bug live ist, schicken Sie die Reparatur über Capgo anstatt Tage für die Genehmigung des App-Stores zu warten. Die Benutzer erhalten die Aktualisierung im Hintergrund, während native Änderungen im normalen Review-Prozess bleiben.

Neuestes aus unserem Blog

Capgo gibt Ihnen die besten Einblicke, die Sie benötigen, um ein wirklich professionelles Mobiltelefon-App zu erstellen.