Contesti: la mappa dei valori disponibili
Un workflow non contiene quasi mai valori scritti a mano: li legge dai contesti, oggetti che GitHub espone prima e durante l’esecuzione. Quelli da riconoscere a colpo d’occhio sono github (metadati della run: repository, ref, sha, evento che l’ha innescata), env e vars — i primi definiti nel YAML, i secondi configurati come configuration variables a livello di repository, organizzazione o environment — poi secrets, runner (sistema operativo, architettura, cartelle temporanee), job e steps per lo stato di ciò che sta girando, needs per gli output dei job da cui si dipende, inputs per i parametri di un workflow riusabile o di un workflow_dispatch, matrix e strategy per le varianti, e jobs, che esiste soltanto dentro un workflow riusabile per costruirne gli output. Si accede con la notazione a punto oppure con le parentesi quadre e il nome fra apici, utile quando la chiave contiene caratteri non ammessi in un identificatore.
Quello che l’esame verifica non è l’elenco ma la tabella di disponibilità: non tutti i contesti sono leggibili in ogni chiave del file. run-name vede solo github, inputs e vars. Una condizione if a livello di job può usare github, needs, vars e inputs, ma non secrets, steps, runner o env, perché in quel momento il job non è ancora stato assegnato a una macchina; la stessa if scritta su uno step li vede invece tutti. Seconda trappola classica: le default environment variables impostate da GitHub non stanno nel contesto env. In un’espressione si scrive github.ref, non env.GITHUB_REF; nello shell, al contrario, la variabile $GITHUB_REF esiste eccome.
Valutazione: prima della run e durante la run
Le espressioni si scrivono dentro il delimitatore di interpolazione e supportano operatori di confronto e logici, funzioni su stringhe come contains, startsWith, endsWith, format e join, le conversioni toJSON e fromJSON (indispensabile per ricostruire un oggetto passato come stringa fra job), hashFiles per le chiavi di cache, gli object filter con l’asterisco e le status check function success, failure, cancelled e always.
La distinzione operativa è fra ciò che viene risolto quando il workflow viene preparato e ciò che è noto solo mentre gira. Nomi di job, matrice, condizioni di livello superiore vengono valutati presto, quando i contesti “di runtime” sono ancora vuoti; steps, job e runner hanno senso solo a job avviato. Da qui la regola di sicurezza più importante del dominio: l’interpolazione avviene sostituendo il valore nel testo dello script prima che la shell lo esegua. Se in un run si interpola direttamente un campo controllato dall’utente — titolo di una pull request, messaggio di commit, nome di un branch — un contenuto ostile diventa codice eseguito. La mitigazione documentata è sempre la stessa: assegnare il valore a una variabile d’ambiente intermedia con la chiave env dello step e riferirla nello script come variabile shell. Vale anche per i segreti: la redazione automatica nei log non è garantita, i segreti “strutturati” in JSON o YAML sfuggono al mascheramento perché la redazione cerca una corrispondenza esatta, e un segreto finito in chiaro nei log va considerato compromesso e ruotato.
Portare dati da uno step, da un job, da un workflow
Gli strumenti sono tre e non sono intercambiabili. GITHUB_ENV crea variabili d’ambiente per gli step successivi dello stesso job: non sono visibili nello step che le scrive. GITHUB_OUTPUT pubblica output di step, leggibili con steps.<id>.outputs.<nome> a patto che lo step abbia un id. Per uscire dal job servono gli job outputs, dichiarati nella chiave outputs del job e letti dal job dipendente tramite needs.
jobs:
build:
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.tag }}
steps:
- id: meta
run: echo "tag=sha-$GITHUB_SHA" >> "$GITHUB_OUTPUT"
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- run: echo "Deploy $TAG"
env:
TAG: ${{ needs.build.outputs.image-tag }}
Un workflow riusabile dichiara i propri output sotto on.workflow_call.outputs, e il valore deve puntare a un output di job, non di step: si mappa prima step verso job, poi job verso workflow. Con una matrice il chiamante riceve l’output dell’ultima esecuzione riuscita che abbia effettivamente impostato un valore, dettaglio che rende inaffidabile l’uso degli output come accumulatore. Le variabili scritte in GITHUB_ENV da un workflow chiamato, invece, non arrivano mai al chiamante.
Artifact, cache, riepiloghi e cancelli di ambiente
Artifact e cache risolvono problemi diversi e la confusione fra i due è materia d’esame. L’artifact conserva un risultato del lavoro — binari, report di test, log — perché sia scaricabile a run conclusa o consumato da un altro job; nella versione corrente dell’azione di upload è immutabile, quindi un secondo caricamento con lo stesso nome non lo sovrascrive. La cache serve a rigenerare più in fretta ciò che sarebbe comunque ricostruibile, tipicamente le dipendenze, con key, restore-keys come fallback progressivo e l’output cache-hit. Lo scope è per branch: una run può ripristinare le cache del proprio branch, del branch di default e del branch base di una pull request, mai quelle di branch figli o fratelli, e i trigger meno affidabili ottengono accesso in sola lettura per evitare il cache poisoning. Le cache scadono per inattività e vengono sfrattate dalla meno usata quando il repository supera il limite di dimensione; gli artifact seguono invece una retention impostabile con la chiave retention-days, che non può superare il tetto definito da repository, organizzazione o enterprise. Entrambi si ispezionano ed eliminano dall’interfaccia, con gh cache e via REST API.
Restano due superfici di comunicazione. GITHUB_STEP_SUMMARY accetta Markdown che compare nella pagina di riepilogo della run: il buffer è isolato per step, si accoda con l’operatore di append e si azzera con quello di redirezione semplice, e un errore di caricamento non fa fallire il job. Il workflow status badge si genera dalla scheda Actions del workflow e mostra l’esito filtrabile per branch ed evento. Infine gli environment, che non sono solo etichette: applicano protezioni — required reviewers, wait timer, deployment branch policy e custom protection rules — e i loro segreti diventano leggibili al job solo dopo che l’approvazione è passata.