Cómo compilar apps iOS con Gitea Actions sin Mac propia
En pocas palabras: Se compilan con Gitea Actions en un runner ubuntu-latest que ejecuta apps:builds:create, mientras Capawesome Cloud hace el trabajo en máquinas Apple Silicon M4 (el chip vigente en la plataforma al momento de esta guía): compila, firma con un certificado subido una sola vez y sube el build a TestFlight sin necesitar una Mac propia.
En este artículo:
- En 30 segundos
- ¿Por qué Gitea Actions no puede compilar apps iOS por sí solo?
- ¿Cómo funciona Gitea Actions con apps iOS usando Capawesome Cloud?
- ¿Qué limitaciones y requisitos tiene cada enfoque?
- ¿Cómo se implementa el workflow de release en la práctica?
- Errores comunes al armar este pipeline
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- gitea.com solo registra runners para los repos de la organización “gitea”, así que cualquier otro usuario tiene que traer su propia infraestructura.
- Capawesome Cloud compila en máquinas Apple Silicon M4 y firma con un certificado que subís una sola vez a la plataforma.
- El workflow de release corre en ubuntu-latest con el comando apps:builds:create, sin necesidad de actions/checkout ni actions/setup-node.
- Si tu Gitea está detrás de firewall, el flag –path reemplaza a –git-ref y evita abrir puertos de entrada.
- Gitea Actions ignora jobs.<job_id>.environment, así que las aprobaciones de deployment hay que armarlas a mano con un workflow de promoción.
Gitea Actions es el sistema de integración continua integrado en Gitea, el gestor de repositorios open source, pensado para ejecutar workflows definidos en YAML dentro de la carpeta .gitea/workflows. Sirve para automatizar builds, tests y despliegues, pero por sí solo no incluye runners con macOS, que es justamente lo que exige compilar una app iOS con Xcode.
¿Por qué Gitea Actions no puede compilar apps iOS por sí solo?
Porque no hay ningún runner macOS disponible por defecto. Gitea Actions solo ejecuta jobs en los runners que alguien registró contra esa instancia, y ni gitea.com ni Gitea Cloud te dan uno con sistema operativo Apple listo para usar.
La guía de Capawesome es clara con esto: en gitea.com los runners quedan registrados únicamente para los repositorios de la organización “gitea”, así que cualquier otro proyecto arranca de cero. Gitea Cloud, la oferta gestionada de CommitGo, levanta runners on-demand, pero ni su página de producto ni la de pricing especifican qué sistema operativo corren. El plan Enterprise menciona runners con autoscaling en Kubernetes, que tampoco es sinónimo de Mac.
El problema de fondo no tiene vuelta: Xcode, xcodebuild y codesign corren en hardware Apple. Da igual si el proyecto es Swift nativo, Capacitor o Cordova. Esa restricción no la resuelve ningún runner Linux, por más potente que sea.
¿Cómo funciona Gitea Actions con apps iOS usando Capawesome Cloud?
Capawesome Cloud recibe el trabajo desde un job Linux común, clona el repo por su cuenta y termina la compilación en máquinas Apple Silicon M4. El job en Gitea solo necesita Node.js (que ya viene en la imagen docker.gitea.com/runner-images:ubuntu-latest) para correr apps:builds:create del CLI de Capawesome.
Ese comando dispara todo el flujo: Capawesome Cloud clona el commit desde tu servidor Gitea mediante la conexión Git que configuraste, corre el build web y el sync nativo si el proyecto usa Capacitor o Cordova, compila con Xcode, firma con el certificado que subiste una sola vez y, si le pasás –destination, sube el resultado a TestFlight. Como el servicio clona el repo directamente, el job no necesita actions/checkout.
El setup inicial tiene cuatro pasos, todos del lado de Capawesome Cloud:
- Conectar Gitea: creás un token en Settings → Applications con scopes read:user, read:organization y read:repository (write:repository si vas a usar Automations), y lo cargás como conexión Git en tu organización de Capawesome Cloud.
- Subir los assets de firma: el certificado de distribución (.p12) y su provisioning profile van con un nombre como “Production iOS”, detallado en la guía de certificados iOS.
- Agregar el destino: creás un destino Apple App Store llamado “TestFlight” con una API key de App Store Connect.
- Guardar el token del CLI: el API token de Capawesome se guarda en Gitea como secret CAPAWESOME_TOKEN, y el ID de la app como variable CAPAWESOME_APP_ID. Gitea rechaza nombres que arranquen con GITEA_ o GITHUB_, así que el prefijo CAPAWESOME_ es obligatorio.
Ponele que ya tenés esto configurado. A partir de ahí, cada tag v* que empujás dispara la compilación, la firma y el envío a TestFlight sin que nadie toque un teclado de Mac. Es un golazo para equipos que ya viven en Linux para todo lo demás.
¿Qué limitaciones y requisitos tiene cada enfoque?
El self-hosting con Mac te da control total pero te carga con todo el mantenimiento; Capawesome Cloud te saca ese peso de encima a cambio de depender de un servicio externo. Ninguna opción es gratis en términos de trabajo, la pregunta es dónde preferís pagarlo.
Gitea Runner (antes act_runner) tiene binarios oficiales para macOS, así que una Mac mini puede registrarse como runner con un label host y correr los jobs directo en el sistema, sin contenedor. El tema es que esos jobs corren sin aislamiento entre sí, y un docker:// action igual necesita un daemon Docker instalado en esa misma Mac.
Todo lo que viene después del archive queda en tus manos: actualizar Xcode cada vez que Apple sube el SDK mínimo para el App Store, desbloquear el keychain con el certificado de distribución para jobs no interactivos, renovar provisioning profiles, generar el ExportOptions.plist para xcodebuild -exportArchive y subir a TestFlight con credenciales de App Store Connect y una herramienta como fastlane. Sumale el mantenimiento del sistema operativo, el espacio en disco que se llena con DerivedData y builds que se pisan si solo hay una Mac en la cola.
Hay un caso más: si tu servidor Gitea vive detrás de un firewall, Capawesome Cloud no puede clonarlo por –git-ref porque no tiene acceso de entrada a tu red. La solución es que el propio runner de Gitea, que sí tiene acceso, haga el checkout con actions/checkout y suba el código con –path en vez de –git-ref. El CLI empaqueta el directorio respetando el .gitignore y lo sube, sin abrir ningún puerto entrante. Eso sí, ese build no muestra metadata de commit en la consola de Capawesome, no se puede reiniciar desde ahí y no funciona con Automations. Complementá con nuestra comparativa de plataformas de CI/CD en 2026.
Otra limitación real: Gitea Actions ignora jobs.<job_id>.environment, así que no hay aprobaciones de deployment nativas como en GitHub Actions. Si necesitás que alguien revise antes de mandar a producción, tenés que separar el build de la promoción con dos workflows distintos.
| Criterio | Mac self-hosted (Gitea Runner) | Capawesome Cloud |
|---|---|---|
| Hardware necesario | Mac mini o similar, propiedad tuya | Ninguno, corre en Apple Silicon M4 de Capawesome |
| Aislamiento entre jobs | No, corre en modo host | Sí, entorno fresco en cada build |
| Mantenimiento de Xcode | Manual, cada actualización de Apple | Se elige por flag –stack |
| Firma y keychain | Configuración manual por máquina | Certificado subido una vez a la plataforma |
| Runner en el job Gitea | macOS con label host | ubuntu-latest estándar |

¿Cómo se implementa el workflow de release en la práctica?
El workflow arranca con un push de tag v* y termina con el build firmado en TestFlight, sin que nadie ejecute nada a mano. Se guarda en .gitea/workflows/ios-release.yaml y corre en ubuntu-latest con un solo step.
El job usa el secret CAPAWESOME_TOKEN como variable de entorno y llama a npx @capawesome/[email protected] apps:builds:create con estos flags: –app-id con la variable CAPAWESOME_APP_ID, –platform ios, –type app-store, –certificate “Production iOS”, –destination “TestFlight” y –git-ref con ${ gitea.sha }, que es el contexto de Gitea para el commit del tag (${ github.sha } funciona como alias si estás portando un workflow de GitHub). El comando espera a que termine el build y devuelve código de error si falla, así que un build roto marca el run de Gitea Actions como fallido.
–type app-store genera el IPA para TestFlight y la App Store; los otros tipos disponibles son simulator, development, ad-hoc y enterprise. Fijar la versión del CLI con @4.22.0 mantiene los runs reproducibles en el tiempo.
Subís el tag, el workflow arranca solo, compila, firma y sube a TestFlight, y si algo falla en el medio (certificado vencido, provisioning profile mal configurado, lo que sea) el run queda marcado en rojo y nadie se entera por WhatsApp que la build no llegó. Cubrimos ese tema en detalle en cómo elegir entre Jenkins y GitHub Actions.
Para que no cualquiera pueda tirar un tag v* y mandar algo a producción, una regla de protected tag en Settings → Tags limita quién puede empujar ese patrón. Y como Gitea Actions no tiene aprobaciones nativas, el patrón que funciona es separar build de promoción: un workflow dispara en cada push a main sin el flag –destination, y otro workflow con workflow_dispatch toma un build_number como input y llama a apps:deployments:create para recién ahí mandarlo a TestFlight. Este dispatch manual con formulario de inputs necesita Gitea 1.23 o posterior.
Hay una tercera vía todavía más liviana: Capawesome Cloud Automations arranca builds desde un webhook en cada push, sin workflow file y sin runner. Necesita el token con scope write:repository para registrar el webhook, y una automation con trigger “Tag” y patrón v* (más un patrón !v*-* para saltear pre-releases como v1.4.0-rc.1).
Errores comunes al armar este pipeline
- Olvidar el prefijo CAPAWESOME_ en secrets y variables: Gitea rechaza directamente cualquier nombre que empiece con GITEA_ o GITHUB_, así que si copiaste el nombre de otro proyecto, el job falla antes de arrancar.
- Asumir que gitea.com te da un runner gratis: los runners de gitea.com están reservados para los repos de la organización “gitea”. Si tu proyecto vive ahí, necesitás registrar el tuyo propio, sea Linux o Mac.
- Correr jobs sin sandbox en label host y no separar los nombres de label: si un workflow pensado para ubuntu-latest se ejecuta sin querer contra tu Mac en modo host, corre sin aislamiento. Usar nombres de label distintos evita ese cruce.
- No fijar la versión del CLI de Capawesome: correr npx @capawesome/cli sin versión específica puede traer un comportamiento distinto entre un release y el siguiente. Pinnear la versión (como @4.22.0) mantiene los builds reproducibles.
- Esperar aprobaciones de deployment que Gitea no tiene: jobs.<job_id>.environment se ignora en Gitea Actions. Si necesitás ese control, armalo vos con el patrón de build más promoción manual.
Preguntas Frecuentes
¿Se puede compilar una app iOS sin tener una Mac?
Sí. Con Capawesome Cloud, un job en ubuntu-latest ejecuta apps:builds:create, y la compilación con Xcode, la firma y la subida a TestFlight corren en máquinas Apple Silicon M4 de la plataforma, sin que el equipo tenga hardware Apple propio.
¿Cómo funciona Gitea Actions con apps iOS?
Gitea Actions ejecuta el workflow YAML en un runner Linux estándar y ese job dispara el CLI de Capawesome, que clona el repo por su cuenta, compila en macOS externo y devuelve el resultado firmado. Gitea Actions nunca corre Xcode directamente salvo que registres un runner macOS propio.
¿Qué es Capawesome Cloud y para qué sirve?
Capawesome Cloud es un servicio que compila, firma y despliega apps iOS y Android en infraestructura Apple Silicon M4, expuesto a través de un CLI que se puede invocar desde cualquier CI, incluido Gitea Actions. Sirve para saltear la necesidad de mantener un runner macOS propio.
¿Cómo subo una app a TestFlight automáticamente desde Gitea?
Configurás un destino Apple App Store llamado TestFlight en Capawesome Cloud con una API key de App Store Connect, y agregás –destination “TestFlight” al comando apps:builds:create en tu workflow de Gitea Actions. Cada push de un tag v* dispara el build y, si pasa, el envío automático.
¿Necesito un runner macOS para compilar iOS en Gitea Actions?
No, si usás Capawesome Cloud: el job corre en ubuntu-latest y la parte macOS queda tercerizada. Sí lo necesitás si preferís self-hosting, registrando una Mac con Gitea Runner y el label host, asumiendo vos el mantenimiento de Xcode, firma y credenciales.
Conclusión
Gitea Actions iOS deja de ser un problema de infraestructura propia cuando el build macOS se delega a un servicio externo. La decisión real no es técnica, es de dónde preferís poner el esfuerzo de mantenimiento: en una Mac que administrás vos, con Xcode, certificados y keychain a tu cargo, o en un servicio que factura por eso y te devuelve un IPA firmado en TestFlight.
Si tu equipo ya corre todo en Gitea con runners Linux, sumar un job que llame a apps:builds:create es bastante menos trabajo que levantar y sostener una Mac mini como runner permanente. Ahora bien, si ya tenés Macs dando vueltas y alguien las mantiene, el modo host de Gitea Runner es una opción válida, siempre que documentes bien el proceso de firma y no dependas de la memoria de una sola persona para renovar certificados. Y si tu Gitea vive detrás de un firewall corporativo, el flag –path es la salida sin tener que exponer nada a internet. Para el hosting del propio servidor Gitea, si necesitás una instancia autoadministrada con infraestructura confiable en Argentina, donweb.com es una alternativa a evaluar.






