Vai al contenuto principale

Cosa sono le Github Actions

Impara a automatizzare test, build e deploy con GitHub Actions! Scopri come creare workflow CI/CD efficienti e ottimizzare il tuo sviluppo.

Nel mondo dello sviluppo moderno, il tempo è una risorsa preziosa. Scrivere codice è solo una parte del lavoro: bisogna testarlo, rilasciarlo, distribuire aggiornamenti e assicurarsi che tutto funzioni correttamente. Tutte queste operazioni possono diventare ripetitive e soggette a errori se eseguite manualmente. È qui che entra in gioco l'automazione.

L'automazione consente di eseguire processi in modo sistematico e senza intervento manuale, garantendo efficienza, affidabilitĂ  e rapiditĂ  nello sviluppo software. Ad esempio, con l'automazione possiamo:

  • Eseguire test automaticamente ogni volta che viene modificato il codice.
  • Distribuire nuove versioni di un'applicazione senza dover caricare manualmente i file su un server.
  • Mantenere la qualitĂ  del codice con strumenti di linting e analisi statica.

Tra i vari strumenti di automazione disponibili, GitHub Actions si distingue perchÊ è perfettamente integrato in GitHub. Questo significa che puoi configurare processi automatici direttamente nei tuoi repository, senza bisogno di strumenti esterni.

Nel corso di questa lezione vedremo cos'è GitHub Actions, come funziona e come puoi sfruttarlo per migliorare il tuo flusso di sviluppo, riducendo il lavoro manuale e aumentando la produttività.

Cos'è GitHub Actions?

GitHub Actions è un sistema di CI/CD (Continuous Integration e Continuous Deployment) integrato direttamente in GitHub. Permette di automatizzare operazioni come test, build e deploy senza bisogno di configurare servizi esterni. Con GitHub Actions, puoi definire flussi di lavoro che si attivano in base a eventi specifici nel repository, migliorando la produttività e riducendo gli errori manuali.

Concetti chiave di GitHub Actions

Per capire come funziona GitHub Actions, è importante conoscere alcuni concetti fondamentali:

  • Workflow: Un workflow è un insieme di job che vengono eseguiti automaticamente in risposta a un evento (ad esempio, un push o una pull request). I workflow vengono definiti all’interno della directory .github/workflows/ del repository, utilizzando file YAML.
  • Job: Un job è una singola unitĂ  di lavoro all’interno di un workflow. Ogni job può essere indipendente o dipendere dall'esecuzione di altri job. Ad esempio, un workflow potrebbe avere un job per eseguire i test e un altro per il deploy, con il secondo che parte solo se il primo ha avuto successo.
  • Step: Ogni job è composto da una serie di step, ovvero le singole operazioni che devono essere eseguite. Un step può eseguire comandi shell o utilizzare azioni predefinite disponibili nel GitHub Marketplace.
  • Runner: Un runner è l’ambiente in cui viene eseguito un workflow. GitHub fornisce runner preconfigurati (GitHub-hosted) basati su sistemi operativi come Ubuntu, Windows e macOS, ma è anche possibile utilizzare runner self-hosted per avere maggiore controllo sull’infrastruttura.
  • Trigger: I workflow vengono attivati da trigger, cioè eventi che avviano l’esecuzione. Alcuni esempi di trigger comuni includono:
    • Push: quando viene eseguito un push su un branch specifico.
    • Pull request: quando viene aperta o aggiornata una PR.
    • Schedule: esecuzione periodica tramite cron job.
    • Manuale: attivazione manuale tramite interfaccia o API.

Grazie a questi elementi, GitHub Actions offre una soluzione potente per automatizzare le operazioni nei progetti software, garantendo un'integrazione continua ed efficiente.

Struttura di un workflow in GitHub Actions

I workflow di GitHub Actions sono definiti utilizzando file YAML all'interno della cartella .github/workflows/ del repository. Un workflow è un insieme di job e step che vengono eseguiti automaticamente in base a eventi specifici.

Esempio di un workflow base

Vediamo un esempio semplice di workflow, salvato nel file .github/workflows/main.yml:

name: CI Example

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: Run a script
        run: echo "Hello, GitHub Actions!"

Spiegazione della sintassi YAML

Questo file definisce un workflow chiamato "CI Example". Analizziamo le sue parti:

  • name: CI Example: Il nome del workflow, utile per identificarlo all’interno di GitHub Actions.
  • on: [push]: Definisce il trigger del workflow. In questo caso, il workflow verrĂ  eseguito automaticamente ogni volta che viene effettuato un push su qualsiasi branch del repository.
  • jobs:: Contiene l'elenco dei job che devono essere eseguiti.
  • **build:**È il nome del job (può essere qualsiasi nome descrittivo).
  • runs-on: ubuntu-latestSpecifica il tipo di runner, ovvero l’ambiente in cui verrĂ  eseguito il job. Qui viene utilizzato un server con Ubuntu fornito da GitHub.
  • steps: Contiene l'elenco di step che compongono il job.
  • name: Checkout repository: Ogni step ha un nome descrittivo. Questo step utilizza un'azione predefinita (actions/checkout@v3) che clona il repository nel runner, rendendo i file disponibili per i successivi comandi.
  • name: Run a script: Questo step esegue un comando shell (echo "Hello, GitHub Actions!"). Il comando viene eseguito nel runner specificato (ubuntu-latest).

Cosa Succede quando il workflow viene Eseguito?

Quando viene eseguito un push nel repository:

  1. GitHub avvia un runner Ubuntu per eseguire il workflow.
  2. Il runner esegue il job build, che è composto da due step:
    • Checkout del repository: clona il codice per poterlo usare nei passaggi successivi.
    • Esecuzione di un comando: stampa "Hello, GitHub Actions!" nella console.
  3. Al termine dell'esecuzione, GitHub mostra lo stato del workflow (successo o errore) nella sezione "Actions" del repository.

Questo è un esempio basilare, ma già utile per comprendere il funzionamento di GitHub Actions. Nei prossimi step vedremo come personalizzare i workflow per casi d’uso più complessi!

Creazione e debug di un workflow personalizzato

In questa sezione vedremo come configurare GitHub Actions per il deploy automatico di una web app su GitHub Pages. Useremo Vite.js, un moderno tool per lo sviluppo frontend, per creare la web app e generare i file di build da pubblicare.

Creazione di una web app con Vite

Come prima cosa inizializziamo il progetto con la CLI di Vite eseguendo:

npm create vite@latest mia-app --template vanilla
cd mia-app
npm install

Questo comando crea un progetto molto basilare, perfetto per il prossimo passaggio. L’unica modifica che dobbiamo fare è creare un file vite.config.js

import { defineConfig } from 'vite';

export default defineConfig({
  base: '/<nome-repo>/',
  build: {
    outDir: 'dist'
  }
});

Dove <nome-repo> è il nome della repository che avete creato su GitHub.

Configurazione del workflow di deploy su GitHub Pages

Ora creiamo il file .github/workflows/deploy.yml per automatizzare il deploy:

name: Deploy to GitHub Pages

on:
  push:
    branches:
      - main

permissions:
  contents: read # Permette al workflow di leggere i contenuti del repository
  pages: write # Concede i permessi per scrivere sulle GitHub Pages
  id-token: write # Abilita l’autenticazione per il deploy sicuro

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '22'

      - name: Install dependencies
        run: npm install

      - name: Build project
        run: npm run build

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: dist
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        uses: actions/deploy-pages@v4

Cosa fa questo workflow:

  • Trigger (<code>on: push</code>): Il workflow si avvia ogni volta che si esegue un push sul branch main.
  • Setup Node.js (<code>setup-node@v3</code>): Installa Node.js nella versione specificata.
  • Installazione delle dipendenze (<code>npm install</code>): Installa i pacchetti dal package.json.
  • Build del progetto (<code>npm run build</code>): Genera i file statici nella cartella dist.
  • Upload dell’artefatto (<code>upload-pages-artifact@v3</code>): Carica i file della build per il deploy.
  • Deploy finale (<code>deploy-pages@v4</code>): Pubblica il sito su GitHub Pages.

Configurare GitHub Pages nel repository

Prima di effettuare il primo deploy, abilita GitHub Pages:

  1. Vai su Settings > Pages.
  2. Seleziona "GitHub Actions" come Build and deployment source.
  3. Dopo il primo deploy, il sito sarà disponibile all’URL: https://&lt;tuo-username>.github.io/&lt;repository-name>/

Attenzione! Le GitHub Pages sono previste nel piano GitHub Pro che costa 4$ al mese. Gratuitamente avete la possibilitĂ  di creare una repo con GitHub Pages creando una repository speciale chiamata <tuo-username>.github.io.

E se volessimo aggiungere anche i test per completare la nostra pipeline CI/CD? Vediamo.

Aggiungere i test alla pipeline CI/CD con GitHub Actions

Un'automazione efficace non si limita solo al deploy, ma include anche una fase di test per garantire che il codice funzioni correttamente prima di essere distribuito. Aggiungiamo quindi una fase di test alla nostra pipeline CI/CD utilizzando Vitest per eseguire test unitari sul nostro progetto.

Per iniziare, installiamo Vitest, il suo UI runner e jsdom, che ci permette di simulare il DOM in ambiente Node.js:

npm install -D vitest @vitest/ui jsdom

Aggiorniamo package.json per aggiungere uno script dedicato all'esecuzione dei test:

"scripts": {
  …
  "test": "vitest"
}

Configuriamo l'ambiente di test nel file vite.config.js, specificando jsdom come ambiente per garantire la compatibilitĂ  con i test che coinvolgono il DOM:

import { defineConfig } from 'vite';

export default defineConfig({

  test: {

    environment: 'jsdom'

  }

  …

});

Questa configurazione assicura che i test abbiano accesso a un DOM virtuale, necessario per verificare il comportamento di componenti che interagiscono con il documento HTML.

Creiamo una cartella tests/ nella root del progetto e all'interno un file counter.test.js. Nel file tests/counter.test.js scriviamo i test per verificare il comportamento della funzione setupCounter

import { beforeEach, describe, expect, it } from 'vitest';

import { setupCounter } from '../src/counter';

describe('setupCounter', () => {

  let button;

  beforeEach(() => {

    // Creiamo un elemento fittizio (mock) per simulare il pulsante

    button = document.createElement('button');

    document.body.appendChild(button);

    // Inizializziamo il contatore sulla nostra simulazione di elemento

    setupCounter(button);

  });

  it('Dovrebbe inizializzare il contatore a 0', () => {

    expect(button.innerHTML).toBe('count is 0');

  });

  it('Dovrebbe incrementare il contatore quando viene cliccato', () => {

    button.click(); // Simuliamo un click

    expect(button.innerHTML).toBe('count is 1');

    button.click(); // Un altro click

    expect(button.innerHTML).toBe('count is 2');

  });

});

Questi test verificano che:

  1. Il contatore venga inizializzato a 0.
  2. Si incrementi correttamente ogni volta che il pulsante viene cliccato.

Eseguiamo i test in locale con:

npm run test

Ora che i test sono configurati e funzionano localmente, li integriamo nella pipeline CI/CD per verificare il codice automaticamente prima di ogni build e deploy.

Modifichiamo il file .github/workflows/deploy.yml per aggiungere un job di test:

name: CI/CD con GitHub Pages

on:

  push:

    branches:

      - main

  pull_request: # Esegue il workflow anche sulle pull request

    branches:

      - main

permissions:

  contents: read

  pages: write

  id-token: write

jobs:

  test:

    runs-on: ubuntu-latest

    steps:

      - name: Checkout repository

        uses: actions/checkout@v3

      - name: Setup Node.js

        uses: actions/setup-node@v3

        with:

          node-version: '22'

      - name: Install dependencies

        run: npm install

      - name: Run tests

        run: npm run test

  build:

    needs: test # Esegue la build solo se i test sono passati con successo

    runs-on: ubuntu-latest

    steps:

      - name: Checkout repository

        uses: actions/checkout@v3

      - name: Setup Node.js

        uses: actions/setup-node@v3

        with:

          node-version: '22'

      - name: Install dependencies

        run: npm install

      - name: Build project

        run: npm run build

      - name: Debug build output

        run: ls -la dist/assets/

      - name: Upload artifact

        uses: actions/upload-pages-artifact@v3

        with:

          path: dist

  deploy:

    needs: build # Il deploy viene eseguito solo se la build è andata a buon fine

    runs-on: ubuntu-latest

    steps:

      - name: Deploy to GitHub Pages

        uses: actions/deploy-pages@v4

Cosa cambia?

  • Aggiunto un job <code>test</code> che verifica il codice prima di ogni build.
  • La build (<code>build</code>) ora dipende dal job <code>test</code> (needs: test), quindi se i test falliscono, la build non viene eseguita.
  • Il deploy (<code>deploy</code>) dipende dalla build (needs: build), garantendo che venga eseguito solo se tutto è andato a buon fine.
  • I test vengono eseguiti automaticamente sia su push che su pull request, migliorando la qualitĂ  del codice prima che venga unito nel branch main.

Facciamo la commit e diamo un’occhiata ai nostri log per verificare che tutto funzioni come dovrebbe. Se tutto è ok. Possiamo andare oltre.

Debug e lettura dei log di un workflow

Anche se GitHub Actions è molto potente, può capitare che un workflow fallisca. Per individuare i problemi, GitHub fornisce log dettagliati che possiamo analizzare direttamente dall'interfaccia web.

Come accedere ai log di esecuzione?

  1. Apri il tuo repository su GitHub.
  2. Clicca sulla scheda "Actions".
  3. Seleziona il workflow che vuoi esaminare.
  4. Guarda la lista dei job e clicca su quello che è fallito.
  5. Scorri i log per trovare eventuali errori nei vari step.

Strategie di debug

Controlla la sintassi YAML: Un errore di indentazione può rompere tutto.

Aggiungi step di debug: Puoi stampare variabili e informazioni utili con echo:

- name: Debugging step
  run: echo "Current directory:" && pwd && ls -la

Usa il comando <code>set -e</code> nei tuoi script per interrompere l'esecuzione in caso di errori.

Verifica i permessi: Alcune azioni necessitano di token di autenticazione per funzionare.

Variabili, Secrets e personalizzazione in GitHub Actions

Quando si lavora con GitHub Actions, può essere necessario gestire dati sensibili come API Key, credenziali di accesso o token di autenticazione. Per proteggere queste informazioni ed evitare di inserirle direttamente nel codice sorgente, si utilizzano variabili d'ambiente e secrets.

Le variabili d'ambiente vengono utilizzate per memorizzare informazioni di configurazione non sensibili, come il nome del progetto o l'ambiente di deploy. I secrets, invece, sono utilizzati per archiviare dati sensibili come chiavi API e credenziali di autenticazione. A differenza delle variabili d'ambiente, i secrets sono nascosti nei log e possono essere accessibili solo dai workflow autorizzati.

Creare e usare secrets in GitHub Actions

Per aggiungere un secret in un repository su GitHub:

  1. Aprire il repository su GitHub.
  2. Accedere alla sezione Settings e selezionare Secrets and variables > Actions.
  3. Cliccare su New repository secret e inserire il nome e il valore del secret.
  4. Salvare il secret.

Una volta creato, il secret può essere utilizzato nei workflow facendo riferimento a secrets.&lt;NOME_SECRET>.

Utilizzo di un Secret in un Workflow

Esempio di workflow che utilizza un token API per il deploy sicuro su Netlify:

name: Deploy to Netlify

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '22'

      - name: Install dependencies
        run: npm install

      - name: Build project
        run: npm run build

      - name: Deploy to Netlify
        run: netlify deploy --prod --dir=dist
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.DEPLOY_API_KEY }}

Questo workflow prevede diversi passaggi:

  • Il repository viene clonato nel runner con checkout@v3.
  • Node.js viene installato nella versione specificata.
  • Le dipendenze del progetto vengono installate con npm install.
  • Il comando npm run build genera i file statici nella cartella dist.
  • Il comando netlify deploy utilizza il token memorizzato nel secret per autenticarsi senza renderlo visibile nei log.

Debug e sicurezza

Se un workflow fallisce per problemi di autenticazione, è necessario verificare che il secret sia stato creato correttamente e che l’azione di GitHub abbia accesso ai secrets.

Per migliorare la sicurezza, è consigliabile evitare di stampare direttamente i secrets nei log e revocare i token API se si sospetta una compromissione. È inoltre opportuno differenziare i secrets in base agli ambienti di sviluppo, test e produzione per evitare che credenziali sensibili vengano esposte o utilizzate involontariamente in contesti non appropriati.

Best practices e ottimizzazione in GitHub Actions

Quando si utilizzano GitHub Actions, è importante ottimizzare i workflow per ridurre i tempi di esecuzione, migliorare l'efficienza e garantire un livello adeguato di sicurezza. Un workflow ben strutturato permette di evitare sprechi di risorse, ridurre i tempi di attesa nelle pipeline di CI/CD e proteggere i dati sensibili.

Ridurre i tempi di esecuzione con la cache

L’uso della cache consente di ridurre significativamente i tempi di esecuzione dei workflow, evitando di dover ricostruire o reinstallare dipendenze già utilizzate in esecuzioni precedenti. GitHub Actions fornisce un'azione predefinita, actions/cache, per memorizzare e riutilizzare file tra le esecuzioni.

Esempio di utilizzo della cache per le dipendenze di Node.js:

- name: Cache Node.js modules
  uses: actions/cache@v3

  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

In questo caso:

  • La cache viene salvata nella cartella ~/.npm, che contiene i moduli installati con npm.
  • Il valore della cache è determinato dal contenuto del file package-lock.json.
  • Se non viene trovata una cache corrispondente, viene usata una versione precedente basata sul prefisso runner.os-node-.

Questo approccio consente di evitare il download ripetitivo delle dipendenze, accelerando l’esecuzione dei job.

Strategie per workflow efficienti

Un workflow ben progettato deve ridurre il numero di esecuzioni inutili e ottimizzare il parallelismo tra job. Alcune strategie utili includono:

**Utilizzare trigger mirati: **non tutti i workflow devono essere eseguiti su ogni push. È possibile specificare trigger piÚ selettivi:

on:
  push:
    branches:
      - main
      - develop
  pull_request:
    types: [opened, synchronize]

In questo esempio, il workflow si attiva solo su push nei branch main e develop, o quando viene aperta o aggiornata una pull request.

**Eseguire job in parallelo: **Job indipendenti possono essere eseguiti in parallelo per ridurre i tempi di esecuzione.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: npm test

  lint:
    runs-on: ubuntu-latest
    steps:
      - run: npm run lint

**Condizioni per l'esecuzione dei job: **È possibile impostare condizioni per eseguire determinati job solo se i precedenti hanno avuto esito positivo.

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - run: npm run deploy

In questo caso, il job deploy viene eseguito solo se il job build ha avuto successo.

Sicurezza e gestione dei permessi

Un workflow deve essere configurato per minimizzare i rischi di sicurezza, specialmente quando manipola secrets, esegue codice esterno o interagisce con ambienti di produzione.

Limitare i permessi nei workflow

GitHub Actions consente di definire i permessi con granularitĂ  per ridurre i privilegi concessi ai job:

permissions:
  contents: read
  id-token: write

Questo impedisce ai workflow di modificare i contenuti del repository, riducendo il rischio di azioni non autorizzate.

Evitare l’esposizione di secrets nei log

Se un job stampa variabili d'ambiente, potrebbe accidentalmente esporre secrets nei log. Per evitarlo, non usare echo $MY_SECRET, ma affidarsi ai sistemi di logging di GitHub Actions.

Bloccare l’esecuzione su fork non autorizzati

Se un repository permette i fork, chiunque può creare una copia del repository e avviare workflow. Per evitare che i secrets vengano esposti, è possibile limitare l’accesso ai workflow in base all’origine della richiesta:

if: github.event.pull_request.head.repo.fork == false

Utilizzare token temporanei

Per ridurre il rischio di compromissione, i workflow dovrebbero utilizzare token a scadenza limitata invece di chiavi API statiche. GitHub fornisce il token GITHUB_TOKEN, che viene generato automaticamente e scade al termine dell’esecuzione del workflow.

Questo è tutto amici

GitHub Actions è un potente strumento di automazione che permette di migliorare l'efficienza nello sviluppo software attraverso workflow personalizzati. Nel corso di questa lezione, abbiamo visto come creare e ottimizzare workflow, utilizzando concetti fondamentali come job, step, runner e trigger. Abbiamo approfondito l'uso delle variabili e dei secrets per proteggere dati sensibili, oltre a strategie per ridurre i tempi di esecuzione e garantire la sicurezza dei workflow.

Per applicare quanto appreso, il passo successivo è provare GitHub Actions in un progetto reale. Puoi iniziare automatizzando il deploy di una web app su GitHub Pages, eseguendo test automatici su ogni push o ottimizzando il tuo flusso di lavoro con job paralleli e caching.

Esplorare le potenzialitĂ  di GitHub Actions ti permetterĂ  di integrare l'automazione nei tuoi progetti, migliorando la produttivitĂ  e la qualitĂ  del codice.

Unisciti a WebTea

Niente spam. Solo contenuti formativi su Software Engineering.

Ci sono due cose che non ci piacciono: lo spam e il mancato rispetto della privacy. Seleziona come vuoi restare in contatto:

Preferenze di contatto

Usiamo Mailchimp come piattaforma di marketing. Cliccando su iscriviti, accetti la nostra privacy policy e che le tue informazioni vengano trasferite a Mailchimp per l'elaborazione. Termini e Privacy. Puoi disiscriverti in qualsiasi momento.