f in x
GitHub Actions da zero: workflow, job, step e trigger per automatizzare il deployment
> cd .. / HUB_EDITORIALE > Visualizza in Inglese
Analisi dei dati e metriche

GitHub Actions da zero: workflow, job, step e trigger per automatizzare il deployment

[2026-06-07] Author: Ing. Calogero Bono

Hai mai lanciato un deploy manuale alle 18:00 del venerdì e scoperto, il lunedì dopo, che un test non era passato? O peggio: hai dimenticato di eseguire la migrazione del database e il sito è andato giù per mezza giornata? Noi, di Meteora Web, abbiamo visto queste scene decine di volte. La soluzione si chiama CI/CD automatizzato, e GitHub Actions è lo strumento più accessibile per iniziare — specialmente se il tuo codice è già su GitHub.

Questa guida parte da zero, ma non è per principianti assoluti: conosci già un po’ di Git e YAML? Bene. Qui capirai davvero come funzionano workflow, job, step e trigger, non solo come copiare un file da una repository template. Imparerai a distinguere cosa va in un job e cosa in uno step, quando usare un trigger su push o su pull request, e perché sbagliare la struttura può costarti tempo e risorse.

Cos’è un workflow? La scatola nera della tua automazione

Un workflow è un processo automatizzato che definisci in un file YAML dentro .github/workflows/. Ogni repository può avere più workflow, ognuno indipendente. Pensa a un workflow come a una “ricetta” che dice: “quando succede X, esegui questa sequenza di operazioni”.

La struttura minima è questa:

name: Deploy su produzione
on:
  push:
    branches: ["main"]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Esegui deploy
        run: echo "Deploy simulato"

Vediamo gli elementi:

  • name: nome leggibile (opzionale ma utile).
  • on: il trigger — quando far partire il workflow.
  • jobs: un insieme di job che girano in parallelo di default.
  • runs-on: l’ambiente (es. ubuntu-latest, windows-latest).
  • steps: comandi eseguiti uno dopo l’altro dentro il job.

Il file YAML è sensibile all’indentazione. Noi, di Meteora Web, abbiamo visto workflow bloccarsi perché un tab era finito al posto di due spazi. Usa sempre un editor che validi YAML.

Trigger: quando e perché far partire il workflow

Il trigger on è il cuore decisionale. Puoi usare eventi semplici come push, pull_request, schedule (cron), workflow_dispatch (manuale), oppure eventi da altri servizi tramite repository_dispatch.

Un errore comune è usare push su tutti i branch. Per la CI (Continuous Integration) spesso vuoi attivare solo su branch principali e pull request. Esempio:

on:
  push:
    branches: ["main", "develop"]
  pull_request:
    branches: ["main"]

Nota che pull_request attiva il workflow quando la PR viene aperta o aggiornata, non quando viene pushato sul branch della PR (a meno che non sia verso main). Questo evita doppie esecuzioni.

Altri trigger utili:

  • schedule: per backup notturni o pulizia log.
  • workflow_dispatch: per eseguire il workflow a piacere dal tab Actions.
  • release: per pubblicare automaticamente su npm o Docker Hub quando si crea una release.

Attenzione ai filtri path: puoi limitare i trigger a file specifici con paths. Esempio: attivare il deploy solo se cambia il file docker-compose.yml. Noi lo usiamo per evitare di far ripartire l’intera pipeline quando modifichi solo il README.

Job: le stanze separate della tua automazione

Un workflow può avere uno o più job. Ogni job gira su un runner indipendente e, di default, in parallelo. Questo è potente: puoi, per esempio, eseguire test su tre versioni di Node.js contemporaneamente, senza aspettare.

Esempio di matrice (matrix strategy):

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [16, 18, 20]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm test

Ogni job è isolato: se il job “test su Node 16” fallisce, gli altri continuano. Puoi anche definire dipendenze tra job con needs. Esempio:

jobs:
  test:
    runs-on: ubuntu-latest
    steps: [/* test */]
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps: [/* deploy */]

Qui deploy parte solo se test termina con successo. Attenzione: needs aspetta che tutti i job dichiarati siano completati, non necessariamente con successo. Per eseguire solo su successo, GitHub Actions già lo fa di default: se un job fallisce, i job successivi che lo need non partono.

Job runner e ambienti

Ogni job gira su un runner. I runner ufficiali sono ubuntu-latest, windows-latest, macos-latest (tutti con un set predefinito di tool). Puoi anche usare self-hosted runner se hai esigenze specifiche (es. GPU, database interni). Noi, di Meteora Web, abbiamo configurato runner self-hosted per un cliente che doveva compilare codice su un server on-premise con licenze particolari. La sicurezza è fondamentale: il runner ha accesso al codice, quindi usa solo runner fidati.

Step: i mattoni atomici del lavoro

Dentro ogni job ci sono gli step. Ogni step esegue un’azione atomica: può essere un comando shell (run) oppure un’azione predefinita (uses). Gli step sono sequenziali: se uno fallisce (exit code ≠ 0), gli step successivi non vengono eseguiti, a meno che non usi if: always() o if: failure().

Esempio con azioni della community:

steps:
  - name: Checkout codice
    uses: actions/checkout@v4
  - name: Setup PHP
    uses: shivammathur/setup-php@v2
    with:
      php-version: '8.2'
  - name: Copia .env
    run: cp .env.example .env
  - name: Esegui Composer
    run: composer install --no-interaction --prefer-dist
  - name: Esegui test
    run: php artisan test

Ogni step ha un name che appare nei log. Non sottovalutare i nomi: quando un workflow fallisce, un nome chiaro ti fa risparmiare minuti di debug.

Usare le azioni predefinite vs comandi raw

Le actions (nel marketplace) sono pacchetti riutilizzabili. Per operazioni comuni (checkout, setup linguaggio, deployment su cloud) conviene usarle. Per azioni personalizzate (es. script di migrazione), scrivi un comando run direttamente. Noi sconsigliamo di scrivere centinaia di righe di shell inline: meglio spostare la logica in uno script nel repository e chiamarlo con run: bash scripts/deploy.sh.

Errori comuni e come evitarli

1. YAML malformato: un errore di indentazione blocca tutto. Usa yaml-lint o la validazione integrata di GitHub. Noi abbiamo un workflow che lancia yamllint su tutti i file YAML prima di ogni deploy.

2. Trigger troppo ampi: se attivi il workflow su ogni push in ogni branch, ogni commit fa girare la pipeline — incluso il fix di un typo nel README. Filtra con branches e paths-ignore.

3. Variabili d’ambiente in chiaro: mai scrivere password o token direttamente nel file YAML. Usa i secrets di GitHub: ${{ secrets.MY_SECRET }}.

4. Dipendenze errate tra job: se usi needs, ricorda che non puoi scambiare dati tra job se non con artefatti o cache. Per passare un file da un job all’altro, usa actions/upload-artifact e actions/download-artifact.

Esempio completo: CI + Deploy condizionale

Mettiamo insieme tutto in un workflow realistico per un’app Laravel con test e deploy su server VPS via SSH:

name: CI/CD Laravel

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_DATABASE: app_test
          MYSQL_USER: test
          MYSQL_PASSWORD: test
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: pdo, pdo_mysql
      - run: cp .env.example .env
      - run: composer install -q --no-interaction --prefer-dist
      - run: php artisan key:generate
      - run: php artisan migrate --force
      - run: php artisan test

  deploy:
    needs: test
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy via SSH
        uses: easingthemes/ssh-deploy@v4
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
          remote-host: ${{ secrets.SSH_HOST }}
          remote-user: ${{ secrets.SSH_USER }}
          source: "."
          target: "/var/www/app"
      - name: Esegui migrazioni e ottimizzazioni
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /var/www/app
            php artisan migrate --force
            php artisan cache:clear
            php artisan config:cache

Nota: il deploy parte solo quando il push è su main e dopo che i test sono passati. Questo è il pattern che noi consigliamo a ogni cliente che inizia con CI/CD.

In sintesi — cosa fare adesso

  1. Crea il file .github/workflows/ci.yml nel tuo repository. Non serve altro per iniziare.
  2. Scegli i trigger giusti: almeno push sul branch principale e pull_request su quello stesso branch.
  3. Definisci almeno un job che esegua i test. Se usi più versioni di linguaggio, approfitta della matrix strategy.
  4. Separa CI e CD: un job per test, uno per deploy con needs: test e condizione if sul branch.
  5. Non esporre segreti: usa sempre ${{ secrets.NOME }} per password, token e chiavi SSH.
  6. Controlla i log dopo il primo run: GitHub mostra ogni step in tempo reale. Se qualcosa fallisce, l’output ti dice perché.

Se hai bisogno di una mano per configurare la pipeline o vuoi che ti aiutiamo a ripulire un workflow esistente, parlaci. Noi, di Meteora Web, lavoriamo su queste cose ogni giorno — dal dominio al fatturato, un unico interlocutore.

Sponsored Protocol

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Co-founder di Meteora Web. Ingegnere informatico, sviluppo ecosistemi digitali ad alte prestazioni. AI, automazione, SEO tecnica e infrastrutture web. Scrivo di tecnologia per rendere complesso… semplice.

[ Read Full Dossier ]

Hai bisogno di applicare questa strategia?

Esegui il protocollo di contatto per iniziare un progetto con noi.

> INIZIA_PROGETTO

Sponsored

> MW_JOURNAL

> READ_ALL()