06: YAML nella pratica
← 05 - Anchor, alias, merge key e chiavi · Indice · successivo → 07 - Strumenti, sicurezza, debug e reference
Ogni applicazione che usa YAML in realtà usa un parser concreto, con la sua semantica e le sue estensioni. Questa è la mappa dei quattro contesti che incontrerai più spesso.
| Contesto | Parser | Semantica |
|---|---|---|
| GitHub Actions | proprietario (non pubblicato) | 1.2 core |
| Home Assistant | PyYAML | 1.1 |
| Docker Compose | go-yaml v4 | 1.2 con concessioni 1.1 |
| Kubernetes | go-yaml v2 → conversione a JSON | 1.1 |
| Astro (frontmatter) | js-yaml 4 | 1.2 + date + merge |
| Obsidian (properties) | non documentato | - |
6.1 Home Assistant
È YAML 1.1 puro. La documentazione lo dice esplicitamente: “YAML tratta Y, true,
Yes, ON tutti come true… se vuoi impostare lo stato di un’entità a on devi
virgolettarlo come 'on'”.
# ✗ sbagliato: 'on' diventa il booleano true
- condition: state
entity_id: light.salotto
state: on
# ✓ corretto
- condition: state
entity_id: light.salotto
state: 'on'I tag custom. Home Assistant registra i suoi: !secret, !include,
!include_dir_list, !include_dir_merge_list, !include_dir_named,
!include_dir_merge_named, !env_var, !input.
# configuration.yaml
mqtt: !include mqtt.yaml
automation: !include_dir_merge_list automations/
recorder:
db_url: !secret db_urlCose da sapere, documentate:
- gli
!include_dir_*leggono solo i file.yaml(i.ymlvengono ignorati); !secretcercasecrets.yamlnella cartella del file e risalendo;!secretnon va usato inautomations.yamloscripts.yaml: se lo fai, l’editor grafico non riesce più a mostrare o modificare nessuna automazione;- gli anchor funzionano solo dentro lo stesso file: ogni file incluso viene parsato a
parte, quindi un alias definito altrove dà
found undefined alias.
I template Jinja2 vanno virgolettati quando iniziano con {{:
value_template: "{{ states('sensor.temperatura') | float(0) > 21 }}"
# per template lunghi, il blocco è più leggibile e non richiede escaping
value_template: >-
{% if is_state('binary_sensor.porta', 'on') %}
aperta
{% else %}
chiusa
{% endif %}>- è la scelta idiomatica per i value_template: unisce le righe e non lascia l’a capo
finale. Per i messaggi con a capo veri, usa |.
Chiavi duplicate: la documentazione avverte che vince l’ultima, in silenzio. In un
configuration.yaml cresciuto negli anni è un errore realistico.
Validazione: hass --script check_config (o ha core check in supervised).
6.2 GitHub Actions
Il caso on:. La chiave che apre ogni workflow è letteralmente on. GitHub la legge
correttamente come stringa (usa uno schema 1.2), ma ogni strumento basato su PyYAML la
legge come il booleano True. ✅ verificato: yaml.safe_load() su un workflow restituisce
{True: 'push', 'jobs': {...}}.
Non serve virgolettarla nel workflow: serve dire al linter di non lamentarsi
(truthy: {check-keys: false} in .yamllint).
Il quoting obbligatorio. La documentazione lo dice: quando un’espressione inizia con
!, devi usare la sintassi ${{ }} o virgolettare, perché ! in YAML avvia un tag.
if: ${{ !cancelled() }} # ✓
if: '!cancelled()' # ✓
if: !cancelled() # ✗ errore di sintassi YAMLInvece run: echo ${{ github.sha }} non ha bisogno di virgolette: inizia con $, che non
è un carattere speciale.
Anchor e alias: supportati da settembre 2025. La merge key << risulta invece non
supportata: quindi puoi riusare un blocco identico, ma non “estendilo e sovrascrivi”.
Per quello ci sono i reusable workflow (workflow_call) e le composite action.
(Non ho potuto eseguire un workflow reale per riverificarlo ad agosto 2026: se ti serve,
provalo su un branch prima di fidarti.)
run: va sempre con |:
- name: Build
run: |
npm ci
npm run build
npm testCon > i tre comandi diventerebbero una riga sola.
La regola di sicurezza da non violare: mai interpolare ${{ }} dentro run: con dati
che arrivano dall’esterno (titoli di PR, nomi di branch, commenti). La documentazione
raccomanda di passarli da una variabile d’ambiente:
- env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "$TITLE"Il motivo: dentro un blocco | non esiste escaping, quindi un titolo malevolo diventa
codice eseguito.
Booleani negli input: inputs preserva i booleani, github.event.inputs li converte a
stringa. Da cui il classico if: github.event.inputs.debug == 'true'.
6.3 Docker Compose
Anchor, alias e merge sono documentati e supportati. Con due limiti dichiarati: il merge
vale solo per i mapping (quindi environment in forma mappa sì, in forma lista no), e
le ancore si risolvono prima dell’interpolazione delle variabili, quindi non puoi
costruire un anchor con dentro ${VAR}.
Tag custom di Compose: !reset (azzera un attributo) e !override (sostituisce invece
di fondere), utili quando componi più file.
Interpolazione: ${VAR}, ${VAR:-default}, ${VAR:?errore}, ${VAR:+valore}.
Per un $ letterale si raddoppia: $$. Ordine mentale corretto: prima YAML parsa, poi
Compose interpola sulle stringhe risultanti.
command: echo $HOME # HOME dell'host, sostituito da Compose
command: echo $$HOME # HOME del container, Compose non lo toccaLe porte vanno virgolettate: '8080:80'. Il parser attuale di Compose non ha più il
problema sessagesimale, ma con i range e gli zeri iniziali le virgolette restano la scelta
sicura, e rendono il file leggibile da qualunque tool.
Nota: la chiave version: in cima è obsoleta e produce un warning.
Validazione: docker compose config espande interpolazione, anchor e merge e ti mostra il
file finale. È il modo migliore per capire cosa hai davvero scritto.
6.4 Kubernetes
Due caratteristiche cambiano tutto rispetto agli altri contesti:
1. Il manifest viene convertito in JSON. Quindi: niente tag custom, niente !!binary,
le chiavi sono sempre stringhe.
2. Il parser è 1.1, quindi vale il Norway problem. Un campo che lo schema vuole stringa
e che riceve un booleano produce l’errore classico
cannot unmarshal bool into Go value of type string.
→ In Kubernetes ogni valore stringa ambiguo va virgolettato: value: "true",
tag: "1.0", - "NO".
3. La validazione è stretta. L’API server rifiuta campi sconosciuti e chiavi
duplicate (a differenza di Home Assistant, che le ingoia). kubectl apply --dry-run=server
è il tuo amico.
Novità 2026: KYAML. Kubernetes ha introdotto (alpha in 1.34, beta e default in 1.35) un
sottoinsieme di YAML deliberatamente non ambiguo: flow style, tutte le stringhe fra doppi
apici, virgole finali ammesse. È YAML valido a tutti gli effetti, ma senza tipizzazione
implicita. Lo ottieni con kubectl ... -o kyaml. Vale la pena guardarlo anche solo come
segnale: anche chi ha scelto YAML sta standardizzando un modo di scriverlo che non
sorprenda.
6.5 Frontmatter: Astro e Obsidian
Astro usa js-yaml 4, quindi: schema 1.2 più i timestamp e la merge key. In pratica:
pubDate: 2026-08-21diventa un oggetto Date → in Zod usaz.coerce.date();yes/no/onrestano stringhe (a differenza di Python);- le chiavi duplicate sono un errore fatale (js-yaml protesta);
sha: 0e1234diventa il numero 0: un hash di commit corto va sempre virgolettato;- il frontmatter deve essere il primissimo contenuto del file.
Obsidian: il parser non è documentato, ma le regole dell’interfaccia Properties sì: niente proprietà annidate, niente Markdown nei valori, i wikilink vanno fra virgolette, e l’editor riscrive il file normalizzando quoting e stile delle liste (e mettendo a rischio i commenti).
La conseguenza pratica se le stesse note vivono in Obsidian e in Astro: tieni il frontmatter piatto, usa gli stessi nomi di campo, e non metterci commenti YAML.
6.6 Riepilogo: le regole per contesto
| Contesto | La regola che non puoi ignorare |
|---|---|
| Home Assistant | 'on' e 'off' sempre fra apici; template Jinja virgolettati; anchor solo intra-file |
| GitHub Actions | run: con |; mai ${{ }} dentro run: con input esterni; niente merge key |
| Docker Compose | porte fra apici; $$ per il $ letterale; docker compose config per verificare |
| Kubernetes | virgoletta ogni stringa ambigua; --dry-run=server prima di applicare |
| Frontmatter | valori ambigui fra apici; date con z.coerce.date(); schema piatto |
Esercizi
- Prendi una tua automazione Home Assistant e cerca
on/offnon virgolettati. Poi fai girarehass --script check_config. - Leggi un tuo workflow GitHub con
python3 -c "import yaml;print(yaml.safe_load(open('.github/workflows/ci.yml')))"e osserva la chiaveTrue. - Scrivi un
docker-compose.ymlcon anchor e${VAR}, poi confronta il sorgente con l’output didocker compose config. - Nel frontmatter di una nota metti
versione: 1.10ecodice: 0e1234, e leggi il risultato. Poi correggili.