12 ago 2025·8 min di lettura

GitHub Actions vs GitLab CI per backend, web e mobile

Confronto tra GitHub Actions e GitLab CI per monorepo: setup dei runner, gestione dei segreti, caching e pattern pratici di pipeline per backend, web e mobile.

GitHub Actions vs GitLab CI per backend, web e mobile

Cosa crea problemi nella CI multi-app

Quando un repository contiene backend, app web e app mobile, la CI smette di essere "solo eseguire i test". Diventa un controllore del traffico per toolchain diverse, tempi di build diversi e regole di rilascio diverse.

Il problema più comune è semplice: una piccola modifica scatena troppo lavoro. Una modifica alla documentazione innesca la firma iOS, o una variazione al backend forza una ricostruzione completa del web, e all'improvviso ogni merge sembra lento e rischioso.

In setup multi-app emergono presto alcuni problemi:

  • Runner drift: le versioni degli SDK differiscono tra macchine, quindi le build si comportano diversamente tra CI e locale.
  • Segreti che si moltiplicano: API key, certificati di firma e credenziali degli store vengono duplicati tra job e ambienti.
  • Confusione sulla cache: la chiave sbagliata genera build stale, ma l'assenza di cache rende tutto dolorosamente lento.
  • Regole di rilascio miste: i backend vogliono deploy frequenti, mentre le release mobile sono soggette a gating e controlli extra.
  • Leggibilità della pipeline: la configurazione cresce fino a diventare un muro di job che nessuno vuole toccare.

Ecco perché la scelta tra GitHub Actions e GitLab CI conta di più nei monorepo che nei progetti single-app. Hai bisogno di modi chiari per separare il lavoro per percorso, condividere artifact in modo sicuro e impedire che job paralleli si pestino i piedi.

Un confronto pratico si riduce a quattro aspetti: setup e scalabilità dei runner, memorizzazione e scoping dei segreti, caching e artifact, e quanto è facile esprimere "costruire solo ciò che è cambiato" senza trasformare la pipeline in una zuppa fragile di regole.

Questo riguarda l'affidabilità e la manutenibilità quotidiana, non quale piattaforma ha più integrazioni o una UI più gradevole. Non sostituisce neanche le decisioni sugli strumenti di build (Gradle, Xcode, Docker, ecc.). Aiuta a scegliere il CI che rende più semplice mantenere una struttura pulita.

Come sono strutturati GitHub Actions e GitLab CI

La differenza più grande è come ciascuna piattaforma organizza le pipeline e il riuso, e questo comincia a contare quando backend, web e mobile condividono lo stesso repo.

GitHub Actions conserva l'automazione in file YAML sotto .github/workflows/. I workflow si attivano su eventi come push, pull request, schedule o esecuzioni manuali. GitLab CI si concentra su .gitlab-ci.yml nella root del repo, con file opzionali inclusi, e le pipeline generalmente partono su push, merge request, schedule e job manuali.

GitLab è costruito attorno agli stage. Definisci stage (build, test, deploy) e poi assegni i job agli stage che corrono in ordine. GitHub Actions è costruito attorno ai workflow che contengono job. I job girano in parallelo per default e aggiungi dipendenze quando qualcosa deve aspettare.

Per eseguire la stessa logica su molti target, i matrix build di GitHub sono un adattamento naturale (iOS vs Android, più versioni di Node). GitLab può ottenere un fan-out simile usando job paralleli e variabili, ma spesso ti ritrovi a collegare più pezzi da solo.

Anche il riuso appare diverso. In GitHub, i team tendono a fare affidamento su reusable workflows e composite actions. In GitLab, il riuso arriva spesso da include, template condivisi e anchor/extends YAML.

Approvals e ambienti protetti differiscono anche loro. GitHub usa spesso ambienti protetti con revisori obbligatori e secret specifici per l'ambiente così i deploy di produzione si fermano finché non sono approvati. GitLab combina comunemente branch/tag protetti, ambienti protetti e job manuali in modo che solo ruoli specifici possano eseguire un deploy.

Setup dei runner ed esecuzione dei job

Il setup dei runner è dove le due piattaforme iniziano a sentirsi diverse nell'uso quotidiano. Entrambe possono eseguire job su runner hosted (non gestisci la macchina) o su runner self-hosted (tu possiedi la macchina, gli aggiornamenti e la sicurezza). I runner hosted sono più semplici da iniziare; i runner self-hosted sono spesso necessari per velocità, strumenti speciali o accesso a reti private.

Una separazione pratica in molti team è runner Linux per backend e web, e macOS runner solo quando devi compilare iOS. Android può girare su Linux, ma è pesante, quindi dimensione del runner e spazio su disco contano.

Hosted vs self-hosted: cosa gestisci

I runner hosted sono adatti quando vuoi un setup prevedibile senza manutenzione. I runner self-hosted hanno senso quando hai bisogno di versioni specifiche di Java/Xcode, cache più veloci o accesso a reti interne.

Se scegli self-hosted, definisci i ruoli dei runner presto. La maggior parte dei repo va bene con un set ridotto: un runner Linux generale per backend/web, un runner Linux più potente per Android, un runner macOS per packaging e signing iOS, e un runner separato per i deploy con permessi più restrittivi.

Scegliere il runner giusto per job

Entrambi i sistemi ti permettono di indirizzare runner (labels in GitHub, tags in GitLab). Mantieni i nomi legati ai carichi di lavoro, come linux-docker, android o macos-xcode15.

L'isolamento è la causa di molte build instabili. File rimasti, cache condivise corrotte o strumenti installati "a mano" su una macchina self-hosted possono creare fallimenti casuali. Workspace puliti, versioni di tool bloccate e pulizie programmate dei runner ripagano rapidamente.

Capacità e permessi sono altri punti dolenti ricorrenti, specialmente con la disponibilità e il costo di macOS. Un buon default è: i runner di build possono compilare, i runner di deploy possono distribuire, e le credenziali di produzione vivono nel più piccolo insieme possibile di job.

Segreti e variabili d'ambiente

I segreti sono dove le pipeline CI multi-app diventano rischiose. I fondamenti sono simili (conservare i segreti nella piattaforma, iniettarli a runtime), ma lo scoping si sente diverso.

GitHub Actions tipicamente scopa i segreti a livello di repository e organizzazione, con un ulteriore livello Environment. Quel livello Environment è utile quando la produzione richiede un gate manuale e un set separato di valori rispetto allo staging.

GitLab CI usa variabili CI/CD a livello di progetto, gruppo o instance. Supporta anche variabili scoperte per ambiente e protezioni come "protected" (disponibile solo su branch/tag protetti) e "masked" (nascoste nei log). Questi controlli aiutano quando un monorepo serve più team.

La modalità di errore principale è l'esposizione accidentale: output di debug, un comando fallito che scrive variabili, o un artifact che include per sbaglio un file di configurazione. Tratta log e artifact come condivisibili per default.

Nelle pipeline backend + web + mobile, i segreti includono di solito credenziali cloud, URL di database e API key di terze parti, materiale di firma (certificati/provisioning iOS, keystore Android e password), token di registry (npm, Maven, CocoaPods) e token di automazione (provider email/SMS, bot chat).

Per più ambienti (dev, staging, prod), mantieni nomi consistenti e scambia i valori tramite lo scoping dell'ambiente invece di copiare job. Questo mantiene la rotazione e il controllo degli accessi gestibili.

Alcune regole che prevengono la maggior parte degli incidenti:

  • Preferisci credenziali a breve durata (come OIDC verso provider cloud quando disponibile) rispetto a chiavi a lunga durata.
  • Applica il principio del minimo privilegio: identità di deploy separate per backend, web e mobile.
  • Maschera i segreti ed evita di stampare variabili d'ambiente, anche in caso di errori.
  • Restringi i segreti di produzione a branch/tag protetti e revisori obbligatori.
  • Non conservare mai segreti negli artifact di build, neanche temporaneamente.

Un esempio semplice e ad alto impatto: i job mobile dovrebbero ricevere i segreti di firma solo su release taggate, mentre i job di deploy backend possono usare un token di deploy limitato sui merge in main. Quel cambiamento riduce da solo il raggio d'azione se un job è mal configurato.

Caching e artifact per build più veloci

Distribuisci dove ti serve
Distribuisci su AppMaster Cloud o nel tuo cloud quando la pipeline è verde.
Distribuisci ora

La maggior parte delle pipeline lente lo sono per una ragione noiosa: scaricano e ricompilano le stesse cose ripetutamente. La cache evita lavoro ripetuto. Gli artifact risolvono un problema diverso: conservare gli output esatti di una specifica esecuzione.

Cosa cache-are dipende da cosa costruisci. I backend beneficiano di cache di dipendenze e del compilatore (per esempio la cache dei moduli Go). Le build web beneficiano della cache del package manager e degli strumenti di build. Le build mobile spesso hanno bisogno di Gradle più la cache dell'SDK Android su Linux, e cache di CocoaPods o Swift Package Manager su macOS. Fai attenzione con caching aggressivo degli output iOS (come DerivedData) a meno di non comprenderne i compromessi.

Entrambe le piattaforme seguono lo stesso schema: ripristina la cache all'inizio del job, salva la cache aggiornata alla fine. La differenza quotidiana è il controllo. GitLab rende esplicito il comportamento di cache e artifact in un unico file, includendo la scadenza. GitHub Actions spesso si affida ad action separate per il caching, che è flessibile ma più facile da configurare male.

Le chiavi di cache contano di più nei monorepo. Buone chiavi cambiano quando cambiano gli input, e restano stabili altrimenti. I lockfile (go.sum, pnpm-lock.yaml, yarn.lock e simili) dovrebbero guidare la chiave. Aiuta anche includere un hash della cartella specifica dell'app che stai costruendo invece che dell'intero repo, e mantenere cache separate per app così una modifica non invalida tutto.

Usa gli artifact per i deliverable che vuoi conservare da quella esecuzione: bundle di rilascio, APK/IPA, report di test, file di coverage e metadati di build. Le cache sono accelerazioni best-effort; gli artifact sono registri.

Se le build sono ancora lente, cerca cache sovradimensionate, chiavi che cambiano a ogni run (timestamp e SHA dei commit sono colpevoli comuni) e output cached che non sono riutilizzabili tra runner.

Adattamento al monorepo: più pipeline senza caos

Un monorepo diventa disordinato quando ogni push attiva test backend, build web e signing mobile, anche se hai cambiato solo una README. Il pattern pulito è: rileva cosa è cambiato, esegui solo i job che contano.

In GitHub Actions questo spesso significa workflow separati per app con filtri sui percorsi così ogni workflow parte solo quando cambiano file nell'area sua. In GitLab CI, spesso significa un unico file di pipeline che usa rules:changes (o child pipeline) per creare o saltare gruppi di job in base ai percorsi.

I package condivisi sono dove si rompe la fiducia. Se packages/auth cambia, sia backend che web potrebbero dover essere ricostruiti anche se le loro cartelle non sono cambiate. Tratta i percorsi condivisi come trigger per più pipeline e tieni chiare le boundary di dipendenza.

Una mappatura di trigger semplice che riduce le sorprese:

  • I job backend corrono su cambiamenti in backend/** o packages/**.
  • I job web corrono su cambiamenti in web/** o packages/**.
  • I job mobile corrono su cambiamenti in mobile/** o packages/**.
  • Le modifiche solo alla documentazione eseguono controlli veloci (formatting, spellcheck).

Parallelizza ciò che è sicuro (unit test, linting, build web). Serializza ciò che deve essere controllato (deploy, release su store). Sia needs di GitLab sia le dipendenze dei job in GitHub ti aiutano a eseguire controlli rapidi all'inizio e fermare il resto se falliscono.

Mantieni il signing mobile isolato dalla CI di tutti i giorni. Metti le chiavi di firma in un ambiente dedicato con approvazione manuale e esegui il signing solo su release taggate o su branch protetti. Le pull request normali possono comunque costruire app non firmate per la validazione senza esporre segreti sensibili.

Passo dopo passo: una pipeline pulita per backend, web e mobile

Riduci il debito tecnico presto
Esplora come la rigenerazione mantiene il codice pulito quando i requisiti cambiano e le esecuzioni CI restano prevedibili.
Scopri come funziona

Una pipeline multi-app pulita parte da una nomenclatura che renda l'intento ovvio. Scegli un pattern e mantienilo così le persone possono leggere i log e capire cosa è stato eseguito.

Uno schema che resta leggibile:

  • Pipeline: pr-checks, main-build, release
  • Ambienti: dev, staging, prod
  • Artifact: backend-api, web-bundle, mobile-debug, mobile-release

Da lì, mantieni i job piccoli e promuovi solo ciò che è passato nei controlli precedenti:

  1. PR checks (ogni pull request): esegui test veloci e lint solo per le app che sono cambiate. Per il backend, costruisci un artifact distribuibile (un'immagine container o un bundle server) e conservalo così i passaggi successivi non lo ricostruiscano.

  2. Web build (PR + main): costruisci l'app web in un bundle statico. Nelle PR conserva l'output come artifact (o deploya in un ambiente preview se lo hai). Su main, produci un bundle versionato idoneo per dev o staging.

  3. Mobile debug builds (solo PR): costruisci un APK/IPA di debug. Non firmare per il release. L'obiettivo è feedback rapido e un file che i tester possano installare.

  4. Release builds (solo tag): quando viene pushato un tag come v1.4.0, esegui build complete di backend e web più build mobile firmate per il rilascio. Genera output pronti per gli store e conserva note di rilascio insieme agli artifact.

  5. Approvazioni manuali: posiziona approvazioni tra staging e prod, non prima dei test di base. Gli sviluppatori possono attivare build, ma solo i ruoli approvati dovrebbero distribuire in produzione e accedere ai segreti di produzione.

Errori comuni che fanno perdere tempo

Progetta il tuo backend velocemente
Modella visivamente i dati PostgreSQL e le API, poi lascia che il CI costruisca solo ciò che è cambiato.
Crea progetto

I team spesso perdono settimane per abitudini di workflow che creano build instabili in modo silenzioso.

Una trappola è affidarsi troppo a runner condivisi. Quando molti progetti competono per la stessa pool, ottieni timeout casuali, job lenti e build mobile che falliscono solo nelle ore di picco. Se backend, web e mobile sono importanti, isola i job pesanti su runner dedicati (o almeno code separate) e mantieni limiti di risorse espliciti.

I segreti sono un altro perditempo. Le chiavi di firma mobile e i certificati sono facili da gestire male. Un errore comune è conservarli troppo ampiamente (disponibili a ogni branch e job) o perderli nei log verbosi. Mantieni il materiale di firma limitato a branch/tag protetti ed evita qualsiasi step che stampi valori segreti (anche stringhe base64).

Il caching può ritorcersi contro quando i team cache-ano directory enormi o confondono cache e artifact. Cache-are solo input stabili. Conserva come artifact gli output di cui hai bisogno più tardi.

Infine, nei monorepo, attivare ogni pipeline a ogni cambiamento consuma minuti e pazienza. Se qualcuno modifica una README e ricostruisci iOS, Android, backend e web, le persone smettono di fidarsi della CI.

Una checklist rapida che aiuta:

  • Usa regole basate sui percorsi così solo le app interessate vengono eseguite.
  • Separa job di test da job di deploy.
  • Mantieni le chiavi di firma limitate ai workflow di release.
  • Cache-are input piccoli e stabili, non intere cartelle di build.
  • Pianifica capacità runner prevedibile per build mobile pesanti.

Controlli rapidi prima di scegliere una piattaforma

Prima di decidere, fai alcuni controlli che rispecchino come lavori davvero. Ti eviteranno di scegliere uno strumento che va bene per una app ma diventa doloroso quando aggiungi build mobile, più ambienti e processi di rilascio.

Concentrati su:

  • Piano runner: hosted, self-hosted o mix. Le build mobile spesso spingono i team verso un mix perché iOS richiede macOS.
  • Piano segreti: dove vivono i segreti, chi può leggerli e come funziona la rotazione. La produzione deve essere più stretta dello staging.
  • Piano cache: cosa cache-are, dove è memorizzata e come si formano le chiavi. Se la chiave cambia ad ogni commit, pagherai il costo senza il vantaggio in velocità.
  • Piano monorepo: filtri di percorso e un modo pulito per condividere passaggi comuni (lint, test) senza copy-paste.
  • Piano di rilascio: tag, approvazioni e separazione degli ambienti. Sii esplicito su chi può promuovere in produzione e quale prova serve.

Metti sotto pressione quelle risposte con uno scenario piccolo. In un monorepo con backend in Go, una web app Vue e due app mobile: una modifica solo alla documentazione dovrebbe fare quasi nulla; una modifica al backend dovrebbe eseguire i test backend e costruire un artifact API; una modifica UI mobile dovrebbe costruire solo Android e iOS.

Se non riesci a descrivere quel flusso in una pagina (trigger, cache, segreti, approvazioni), fai un pilot di una settimana su entrambe le piattaforme usando lo stesso repo. Scegli quella che risulta noiosa e prevedibile.

Esempio: un flusso realistico di build e rilascio per monorepo

Connetti pagamenti rapidamente
Collega Stripe e mantieni i workflow di rilascio prevedibili su web e mobile.
Aggiungi pagamenti

Immagina un repo con tre cartelle: backend/ (Go), web/ (Vue) e mobile/ (iOS e Android).

Nella giornata, vuoi feedback veloci. Nei rilasci, vuoi build complete, signing e passi di publish.

Una divisione pratica:

  • Branch feature: esegui lint + unit test per le parti cambiate, costruisci backend e web e opzionalmente esegui una debug build Android. Salta iOS a meno che non sia necessario.
  • Tag di rilascio: esegui tutto, crea artifact versionati, firma le app mobile e carica immagini/binari nello storage di rilascio.

La scelta dei runner cambia quando è coinvolto il mobile. Go e Vue vanno bene su Linux quasi ovunque. iOS richiede runner macOS, che possono guidare la decisione più di ogni altra cosa. Se il team vuole pieno controllo delle macchine di build, GitLab CI con runner self-hosted può essere più facile da gestire in flotta. Se preferisci meno lavoro operativo e setup rapido, i runner hosted di GitHub sono comodi, ma i minuti macOS e la loro disponibilità diventano parte della tua pianificazione.

Il caching è dove si risparmia davvero tempo, ma la cache migliore dipende dall'app. Per Go, cache-are download dei moduli e la cache di build. Per Vue, cache-are il package manager store e ricostruire solo quando cambiano i lockfile. Per mobile, cache-are Gradle e l'SDK Android su Linux; cache-are CocoaPods o Swift Package Manager su macOS, e aspettati cache più grandi e più invalidazioni.

Una regola di decisione che regge: se il codice è già ospitato su una piattaforma, comincia da lì. Cambia solo se i runner (soprattutto macOS), i permessi o la compliance ti costringono a farlo.

Prossimi passi: scegliere, standardizzare e automatizzare in sicurezza

Scegli lo strumento che corrisponde a dove sono già il codice e le persone. La differenza si manifesta spesso nell'attrito quotidiano: revisioni, permessi e quanto è veloce diagnosticare una build rotta.

Inizia semplice: una pipeline per app (backend, web, mobile). Una volta stabile, estrai i passaggi condivisi in template riusabili così riduci il copy-paste senza cancellare la responsabilità.

Scrivi lo scoping dei segreti come scriveresti chi ha le chiavi di un ufficio. I segreti di produzione non dovrebbero essere leggibili da ogni branch. Imposta un promemoria per la rotazione (ogni trimestre è meglio di mai) e accorda una procedura di revoca d'emergenza.

Se costruisci con un generatore no-code che produce codice sorgente reale, tratta generazione/export come uno step CI di prima classe. Per esempio, AppMaster (appmaster.io) genera backend in Go, web in Vue3 e app mobile in Kotlin/SwiftUI, così la pipeline può rigenerare codice su modifica e poi costruire solo i target interessati.

Una volta che hai un flusso di cui il team si fida, rendilo lo standard per i nuovi repo e mantienilo noioso: trigger chiari, runner prevedibili, segreti stretti e rilasci che vengono eseguiti solo quando lo vuoi davvero.

FAQ

Devo scegliere GitHub Actions o GitLab CI per un monorepo con backend, web e mobile?

Di solito conviene rimanere sulla piattaforma dove il codice e il team sono già attivi; cambia solo se i runner (soprattutto macOS), i permessi o i requisiti di conformità ti costringono a farlo. Il costo quotidiano è spesso legato alla disponibilità dei runner, al controllo dei segreti e a quanto è facile esprimere “costruire solo ciò che è cambiato” senza regole fragili.

Qual è la differenza pratica più grande tra come sono strutturati GitHub Actions e GitLab CI?

GitHub Actions tende a sembrare più semplice per un avvio rapido e per i matrix build, con workflow distribuiti su più file YAML. GitLab CI spesso risulta più centralizzato e guidato da stage, il che può facilitare la comprensione quando la pipeline cresce e vuoi un unico posto per controllare cache, artifact e l'ordine dei job.

Come dovrei pianificare i runner quando sono coinvolti build iOS?

Considera il macOS una risorsa scarsa e usalo solo quando serve realmente per packaging o signing iOS. Una baseline comune è: runner Linux per backend e web, un runner Linux più potente per Android, e un runner macOS riservato ai job iOS, con un runner separato per i deploy che abbia permessi più restrittivi.

Come evito il “runner drift” e build instabili tra macchine?

Il "runner drift" avviene quando lo stesso job si comporta diversamente perché SDK e tool variano tra le macchine. Risolvilo bloccando le versioni degli strumenti, evitando installazioni manuali sui runner self-hosted, usando workspace puliti e pulendo o ricostruendo periodicamente le immagini dei runner per non accumulare differenze invisibili nel tempo.

Qual è il modo più sicuro per gestire i segreti in una pipeline backend + web + mobile?

Rendi i segreti disponibili solo al più ristretto insieme di job che ne hanno bisogno e mantieni i segreti di produzione dietro branch/tag protetti e approvazioni. Per il mobile, la scelta più sicura è iniettare il materiale di signing solo per i release taggati, mentre le pull request possono produrre build di debug non firmate per la validazione.

Qual è la differenza tra caching e artifact, e quando usare ciascuno?

Usa le cache per velocizzare lavori ripetuti e gli artifact per conservare gli output esatti di una singola esecuzione. La cache è un aiuto best-effort che può cambiare nel tempo; un artifact è un deliverable conservato, come un bundle di rilascio, un report di test o un APK/IPA di cui vuoi tracciare la provenienza.

Come progetto chiavi di cache che funzionano bene in un monorepo?

Basare le chiavi di cache su input stabili come i lockfile e limitarle alla parte del repo che stai costruendo in modo che cambi non correlati non invalidino tutto. Evita chiavi che cambiano a ogni esecuzione (timestamp o SHA completo) e mantieni cache separate per ogni app così backend, web e mobile non si sovrascrivono a vicenda.

Come faccio a impedire che una modifica a README avvii build di backend, web e mobile?

Configura trigger basati sui percorsi in modo che cartelle di documentazione o altre cartelle non avviino job costosi, e tratta le cartelle condivise come trigger espliciti per le app che ne dipendono. Se una cartella condivisa cambia, è normale ricostruire più target, ma rendi esplicita quella mappatura così la pipeline resta prevedibile.

Come gestisco il signing mobile senza rallentare ogni pull request?

Metti le chiavi di signing e le credenziali store fuori dalle normali esecuzioni CI, proteggendole dietro tag, branch protetti e approvazioni. Per le pull request, costruisci varianti di debug senza signing di rilascio così ottieni feedback veloce senza esporre credenziali ad alto rischio.

La CI può gestire progetti in cui il codice è generato (per esempio con uno strumento no-code)?

Sì, ma tratta la generazione come uno step di prima classe con input e output chiari in modo che sia facile fare caching e rieseguire in modo prevedibile. Se usi uno strumento come AppMaster (appmaster.io) che genera codice reale, rigenera sulle modifiche rilevanti e poi costruisci solo i target interessati (backend, web o mobile) in base a quello che effettivamente cambia dopo la generazione.

Facile da avviare
Creare qualcosa di straordinario

Sperimenta con AppMaster con un piano gratuito.
Quando sarai pronto potrai scegliere l'abbonamento appropriato.

Iniziare