Leggere un run: trigger, log e cronologia

Davanti a una spunta rossa l’istinto è riaprire il file YAML e rileggerlo dall’alto. È il percorso più lento. Un workflow run conserva già tutto quello che serve per risalire dal risultato alla causa: l’evento che lo ha generato, il commit su cui ha lavorato, la valutazione di ogni condizione e la cronologia dei tentativi. Questa unità percorre quel tragitto al contrario, dal fallimento alla riga di configurazione che lo ha prodotto.

Capire quale evento ha generato il run

Nella scheda Actions ogni riga riporta nome del run, branch ed evento. Il nome viene da run-name se il workflow lo definisce, altrimenti da informazioni legate all’evento, come il messaggio del commit o il titolo della pull request: due run con lo stesso titolo possono quindi appartenere a eventi diversi. La verità sta nel contesto github, che espone github.event_name, github.ref, github.sha, github.run_id, github.run_number e github.run_attempt; il payload completo dell’evento sta nel file indicato da github.event_path.

Il punto che genera più errori di diagnosi è che ref e commit dipendono dall’evento. Per un push il SHA è il commit in cima al ref aggiornato. Per un pull_request è l’ultimo commit di merge sul ref refs/pull/NUMERO/merge, cioè un commit che nel branch della PR non esiste. Per pull_request_target e per schedule, invece, si lavora sull’ultimo commit del branch di default: se il workflow “non vede” la modifica appena spinta nella PR, la causa è spesso questa e non il codice. I workflow pianificati, inoltre, girano solo sul branch di default e possono subire ritardi nei periodi di carico.

Un run che non compare affatto è un caso diverso. Gli eventi generati usando il GITHUB_TOKEN non creano nuovi run, con l’eccezione di workflow_dispatch e repository_dispatch: è la protezione contro i cicli infiniti. Oppure il commit contiene una parola chiave di salto — [skip ci], [ci skip], [no ci], [skip actions], [actions skip], o il trailer skip-checks:true — che vale per push e pull_request e lascia i check in stato “Pending”, bloccando i merge che li richiedono. Distinguere “non è partito” da “è partito ed è fallito” è esattamente ciò che l’esame verifica su questo punto.

I log di valutazione delle condizioni

Un job grigio, marcato come skipped, non è un errore: la sua condizione if è stata valutata falsa. Per vedere il calcolo apri il riepilogo del run, seleziona il job, usa il pulsante a forma di ingranaggio e scegli Download log archive; nell’archivio estratto il file NOME-JOB/system.txt contiene tre righe: Evaluating, con l’espressione così come è scritta, Expanded, con i valori effettivi sostituiti, e Result, con l’esito booleano. La riga Expanded è quella che chiude il caso, perché mostra il valore reale che il motore ha usato. Il limite da ricordare: questi log di espressione coprono solo le condizioni a livello di job. Per le condizioni degli step serve il debug logging, che puoi attivare con la casella Enable debug logging al momento della riesecuzione.

La cronologia completa il quadro. Filtra l’elenco sul singolo workflow e confronta il run fallito con l’ultimo verde: se la configurazione non è cambiata, la causa sta fuori dal repository, in una action aggiornata o in un servizio esterno.

Matrici: dal nome del job alla variante fallita

Una matrix strategy genera un job per ogni combinazione delle variabili dichiarate, meno quelle rimosse da exclude e più quelle aggiunte da include.

env: &shared-env
  NODE_ENV: test
  LOG_LEVEL: debug

jobs:
  test:
    runs-on: ${{ matrix.os }}
    env: *shared-env
    strategy:
      fail-fast: false
      matrix:
        node: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
        exclude:
          - node: 18
            os: windows-latest

Nell’interfaccia i job compaiono come test (18, ubuntu-latest), test (20, windows-latest) e così via: al job_id si affiancano fra parentesi i valori delle variabili, nell’ordine in cui compaiono nella matrice. Leggere quei nomi significa già isolare l’asse che rompe — se falliscono solo le varianti Windows il problema è il sistema operativo, se falliscono solo quelle con node: 22 è la versione del runtime.

Con fail-fast al valore predefinito, il fallimento di una variante annulla le altre: i job cancelled non sono guasti, sono conseguenze. Impostarlo a false, come sopra, serve proprio a vedere il quadro completo. Per ripartire non serve rilanciare tutto: il menu Re-run jobs offre Re-run all jobs e Re-run failed jobs, mentre l’icona accanto al singolo job nell’elenco laterale riesegue solo quella variante. La riesecuzione conserva il commit e il ref originali e i privilegi dell’attore iniziale, incrementa github.run_attempt e lascia invariato il run: per questo github.actor e github.triggering_actor possono differire.

Espandere anchor e alias prima di giudicare un file

Il YAML dei workflow supporta anchor e alias: & marca un nodo da riutilizzare, * lo ripete altrove. Nell’esempio, env: *shared-env non rimanda a nulla di dinamico — al parsing diventa una copia delle due variabili. Quando leggi il file di qualcun altro, espandi mentalmente ogni alias prima di contare job o valutare condizioni, altrimenti stai leggendo un documento più corto di quello che GitHub esegue davvero.

Due confusioni tipiche da evitare. La prima: anchor e alias sono risolti dal parser YAML, prima che il motore veda la configurazione, mentre le espressioni sono valutate a runtime; un alias non può quindi dipendere da un contesto. La seconda: un anchor vive dentro un singolo documento, non attraversa file, e non sostituisce il riuso vero, che passa da workflow_call e da uses. La documentazione rimanda alla specifica YAML 1.2.2 e mostra solo anchor e alias: non dare per scontato che la chiave di merge << funzioni in un workflow.