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 Il motivo per cui le squadre scelgono Gitea è che vogliono il proprio Capacitor su infrastrutture che controllano. Ciò non dovrebbe cambiare solo perché l'app necessita di Xcode. Entrambe le configurazioni sotto mantengono il tuo codice sul tuo 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.
How Gitea Actions esegue i job
Gitea Actions utilizza un GitHub sintassi compatibile con Actions, e i job vengono eseguiti su act_runner istanze che si registrano contro il tuo server Gitea. Ogni runner pubblicizza etichette come ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest. La parte dopo la colonna decide come vengono eseguiti i job:
docker://imageesegue il job in un contenitore. Questo è il default per i runner Linux.hostesegue il job direttamente sulla macchina, con tutto ciò che è installato lì.
macOS non può eseguire in un contenitore Docker, quindi un runner Mac deve utilizzare un host etichetta. Ciò ha conseguenze che affronteremo più avanti.
Due note di compatibilità prima di copiare un workflow da GitHub:
- Gitea legge
.gitea/workflows/e cade in modo automatico.github/workflows/quando il primo cartello non esiste. uses: actions/checkout@v4funziona perché Gitea risolve le azioni da GitHub di default (ilDEFAULT_ACTIONS_URLimpostazione). Le istanze aeree necessitano di riflettere le azioni che utilizzano e di riferirsi a loro con l'URL completo.
Opzione 1: Un esecutore Mac con act_runner
Registra il Mac
Scarica il act_runner binario per macOS (arm64 per Apple Silicon) dalla pagina rilasci di Gitea, quindi ottieni un token di registrazione dalla Amministrazione del sito > Azioni > Esecutori (di istanza), o dalle impostazioni dell'organizzazione o del repository.
./act_runner register --no-interactive \
--instance https://gitea.example.com \
--token <registration_token> \
--name mac-mini-01 \
--labels macos-arm64:host
./act_runner daemon
Eseguire il demone come agente di avvio sotto un utente dedicato affinché si riavvii dopo i riavvii e abbia la sua chiave di accesso al login.
Installare il kit di strumenti sul host
Modalità host significa che il lavoro utilizza gli strumenti del Mac stessi. Installa:
- Xcode 26 (richiesto per gli upload su App Store Connect a partire dal 28 aprile 2026) e esegui
sudo xcodebuild -license accept - Node.js 22 o più recente e Bun (Capacitor 8 richiede Node 22)
- Ruby e fastlane, o utilizzare un
Gemfileconbundle exec - CocoaPods, solo se il tuo progetto iOS lo utilizza ancora. Nuovi Capacitor 8 progetti utilizzano il gestore di pacchetti Swift.
Il 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
Il beta la lane importa il certificato in un keychain temporaneo, cambia il progetto in modalità firma manuale, aumenta il numero di build da TestFlight, archivia e carica. Un file Fastfile completo per un progetto Capacitor è in Costruire e distribuire app iOS con Bitbucket Pipelines; funziona inalterato su Gitea. Per il background di fastlane, vedere la GitHub Actions iOS guide.
Il costo del modalità host
Il act_runner La documentazione è diretta su questo: i lavori host non sono isolati. In pratica:
- Ogni lavoro condivide lo stesso utente, disco e keychain. Un lavoro che muore a metà può lasciare un keychain temporaneo o un profilo di provisioning obsoleto che rompe il successivo.
- Qualsiasi flusso di lavoro che mira al rilascio esegue code su quel Mac con l'accesso dell'utente. Registra solo i runner host per repository che ti fiduci, e evita di eseguirli su richieste di pull da fork.
- Derivata, archivi e runtime simulator crescono fino a quando il disco è pieno.
- Aggiornamenti di Xcode sono a tuo carico, e Apple aumenta il minimo SDK ogni primavera.
- Un Mac costruisce un lavoro alla volta. Coda di rilascio.
Se il tuo team già utilizza Mac e qualcuno li possiede, questo funziona. Se non è così, l'opzione successiva elimina il Mac dalla tua infrastruttura.
Opzione 2: Costruisci iOS da un runner Linux con Capgo Build
Capgo Build compila e firma Capacitor app su Mac Capgo-gestiti. Il tuo lavoro Gitea rimane sul runner Linux che già hai. Esegue il check-out del code, costruisce la layer web, esegue cap sync iose chiama build requestIl Capgo CLI carica il progetto nativo preparato, invia i log Xcode nella log del lavoro Gitea e esce con codice di uscita non zero in caso di fallimento.
Questo modello si adatta bene a Gitea self-hosted:
- Nessuna accesso in entrata. Capgo non clona il tuo repository. Il tuo runner invia il progetto preparato tramite HTTPS, quindi un server Gitea dietro un firewall o VPN funziona senza modifiche.
- Nessun token di repository. Non ci sono connessioni Git da configurare sul lato Capgo.
- Il tuo fonte web rimane a casa. Solo la cartella nativa,
ios/Capacitor config, e le parti native dei plugin sono caricati.src/,.git/e.envi file non lo sono.
Impostazione una volta sola
Su una macchina di sviluppatore:
bunx @capgo/cli@latest login
bunx @capgo/cli@latest app add
bunx @capgo/cli@latest build init --platform ios
build init crea o riutilizza il certificato di distribuzione e il profilo di provisioning per l'App Store e memorizza la chiave API di App Store Connect. Esegui una compilazione locale per confermare che tutto firmi:
bun run build && bunx cap sync ios
bunx @capgo/cli@latest build request com.example.app --platform ios --build-mode release
Poi esporta le credenziali:
bunx @capgo/cli@latest build credentials manage --appId com.example.app --platform ios
# select "Export to .env"
Aggiungi segreti a Gitea
Apre Impostazioni repository > Azioni > Segreti e aggiungi ogni chiave dall'archivio esportato, più la tua Capgo API chiave:
| Segreto | Scopo |
|---|---|
CAPGO_TOKEN |
Capgo API chiave con permesso di caricamento |
BUILD_CERTIFICATE_BASE64 |
Certificato di distribuzione (".p12) |
P12_PASSWORD |
Certificato di distribuzione ( |
CAPGO_IOS_PROVISIONING_MAP_BASE64 |
Password del certificato |
APPLE_KEY_ID |
App Store Connect API ID chiave dell'applicazione |
APPLE_ISSUER_ID |
App Store Connect ID rilasciatore |
APPLE_KEY_CONTENT |
Base64 .p8 chiave |
APP_STORE_CONNECT_TEAM_ID |
ID Apple team |
Gitea rifiuta i nomi segreti e variabili che iniziano con GITEA_ o GITHUB_Mantieni questi nomi come sono. Inserisci i valori non segreti, come l'ID dell'app, sotto. e leggi con e leggileli con ${{ vars.NAME }}. Secrets set at the organization level are shared by every repository in it, which is handy when you ship several apps from one Apple team. Delete the exported file when you are done.
Rilascio su push di tag
# .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 }}"
Nota:
${{ gitea.ref_name }}è il nome del tag. Ilgithub.*il contesto funziona anche come alias, il che aiuta quando si portano avanti i workflow.- Gitea imposta
CI=truein ogni job, quindi il Capgo CLI non aspetta mai l'input interattivo. - Capgo incrementa il numero di build dal più recente build di TestFlight, quindi due tag in fila non si sovrappongono mai.
- Senza
--submit-to-store-reviewIl build atterra in TestFlight e si ferma. Aggiungi la bandiera quando desideri che CI invii l'app per la revisione di App Store.
L'invio è ora git tag v2.3.0 && git push origin v2.3.0. Chiunque può spingere un v* aggiungi una regola per un tag protetto v* sotto Impostazioni > Etichette e limitarlo ai manager delle release.
Costruzioni ad hoc per i tester
Per le release QA che installano direttamente su dispositivi registrati, costruisci in modalità ad hoc e ottieni un link di download:
# .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 build necessita di un profilo di provisioning ad hoc che elenchi i dispositivi di test. Salva quel profilo in codice base64 come un segreto ("ADHOC_PROFILE_BASE64 di sopra), decodificalo nel job e passalo con --ios-provisioning-profileLe CLI flag ha la precedenza sulle variabili di ambiente, quindi sovrascrive il profilo dell'App Store per questa sola build. Raccogli i UDIDs con il Finder di UDID per iOSPer la maggior parte dei tester, la prova di TestFlight è più semplice, perché non richiede UDIDs.
Approvazione manuale della release
GitHub consente di richiedere i revisori su un ambiente. Gitea Actions non implementa le regole di protezione degli ambienti, quindi un job che nomina un ambiente non si fermerà per l'approvazione. Due modelli funzionano al posto suo:
- Etichette protette. Solo i manager delle rilascie possono pubblicare
v*tag, e il flusso di lavoro dei tag parte. - Costruisci, poi promuovi. Costruisci su ogni push a
maincon--ios-distribution ad_hoco carica su TestFlight per la sola prova interna, poi utilizza unworkflow_dispatchflusso di lavoro (disponibile nelle versioni correnti di Gitea) per ricostruire il commit approvato con--submit-to-store-review.
Spedisce le modifiche web senza un nuovo binario
La maggior parte dei commit in un'app Capacitor cambia solo JavaScript, CSS e HTML. Ricostruire l'iOS per quelle sprecerebbe minuti e tempo di revisione dell'app. Con aggiornamenti live Capgo, un lavoro può scegliere la strada giusta:
- 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 exits 0 when native dependencies match what is live on the channel, and 1 when a new binary is required. Also force the native path when files under ios/ o capacitor.config.* modifica, poiché non fanno parte della comparazione delle dipendenze. Vedi Scegli automaticamente live update o costruzione nativa.
Mac runner vs costruzione cloud
| att_runner sul tuo Mac | Capgo Costruzione da Linux | |
|---|---|---|
| Hardware | Lo acquisti e lo ospiti | nessuno |
| Aggiornamenti di Xcode | Manuale, per macchina | Trattato da Capgo |
| Isolamento tra i processi | Nessuno in modalità host | Ambiente di costruzione fresco per processo |
| Costruzioni parallele | Uno per Mac | Molti, da qualsiasi esecutore |
| Firma di file | Nelle segrete di Gitea, decodificate sul Mac | Nelle segrete di Gitea, inviate per costruzione |
| Funziona dietro un firewall | Sì | Sì, solo HTTPS in uscita |
| Applicazioni native Swift (nessun Capacitor) | Sì | No, solo app Capacitor |
| Limite di tempo di costruzione | Da impostare da te | 10 minuti per costruzione |
Risolvere i problemi
| Sintomo | Soluzione |
|---|---|
| Il lavoro rimane in coda per sempre | Nessun esecutore online ha l'etichetta in runs-onControlla la lista dei runner e la ortografia dei label. |
uses: actions/checkout@v4 fallisce il download |
Il runner non riesce a raggiungere GitHub. Rifletti l'azione sul tuo Gitea e riferiscilo con l'URL completo. |
| Il valore della chiave segreta è vuoto nella job | Il nome inizia con GITEA_/GITHUB_o la chiave segreta è definita su un altro repository. |
security: SecKeychainItemImport: MAC verification failed |
sbagliato .p12 password, o un .p12 creato senza OpenSSL 3 senza -legacy. Ri-esportalo. |
No profiles for 'com.example.app' were found |
Il profilo nelle chiavi segrete non corrisponde all'ID bundle o al certificato. Ri-esegui build init e ri-esporta. |
| L'upload è stato rifiutato per la versione SDK | Il Mac runner ha ancora un Xcode più vecchio di 26. |
cap sync ios avverte sui CocoaPods su Linux |
Come previsto. La macchina di costruzione Capgo installa i pods. |
Per ulteriori modalità di fallimento, vedi CI/CD per Capacitor: trappole comuni e il Capgo Build troubleshooting guide.
Riepilogo
Gitea Actions gestisce l'iOS bene una volta deciso dove Xcode si esegue. Un Mac runner in modalità host offre il controllo completo e la piena responsabilità. Un lavoro Linux che chiama la costruzione Capgo mantiene invariato il setup di Gitea, non richiede accesso in ingresso e trasforma una rilascio di TestFlight in un push di tag. Quale che sia la scelta, conservare il materiale di firma in segreti di Gitea, proteggere i tag di rilascio e spedire le modifiche web-only come aggiornamenti live in modo che la pipeline nativa si esegua solo quando è necessario.