Hugo è un generatore di siti statici distribuito come singolo binario compilato in Go: per fare la build non serve un interprete né un gestore di pacchetti sulla macchina. La versione corrente, mentre scrivo, è la 0.9.0, rilasciata da Steve Francia il 15 novembre 2013. Il progetto sta su GitHub come spf13/hugo, licenza Apache 2.0.
Contesto
I generatori statici risolvono un problema preciso: produrre l’HTML una volta sola, per contenuti che cambiano di rado, e togliere il livello applicativo a runtime. Jekyll (2008) è la scelta di riferimento, anche perché GitHub Pages lo gira lato server: scrivi in Markdown, applichi i template Liquid, e l’output è HTML che qualsiasi web server sa servire. Il prezzo è l’ambiente Ruby — ruby, bundler, le gemme con le loro versioni — e tempi di build che crescono in modo visibile man mano che le pagine aumentano.
Le due cose vanno insieme. La toolchain Ruby va installata e tenuta allineata tra la macchina di sviluppo e quella che pubblica; e su un sito di qualche migliaio di pagine la rigenerazione completa non è istantanea. Hugo attacca tutti e due i punti partendo dal linguaggio: Go compila in un eseguibile autosufficiente, e la libreria standard offre già il parsing dei template e la gestione concorrente.
Architettura
Quello che scarichi è un file. Su Linux, macOS o Windows prendi il binario della tua piattaforma e lo metti nel PATH; intorno non c’è niente da installare. Un sito Hugo è una convenzione di cartelle: content/ per i contenuti in Markdown, layouts/ per i template, static/ per gli asset copiati così come sono, config (in TOML, YAML o JSON) per le impostazioni globali.
Ogni file Markdown ha in testa un frontmatter — un blocco di metadati — che Hugo accetta in TOML, YAML o JSON. Il corpo lo converte in HTML Blackfriday, l’implementazione Markdown in Go usata dal progetto. I template girano su html/template della libreria standard di Go, che applica l’escaping in base al contesto; oltre alla sintassi Go, la 0.9.0 accetta anche template scritti in Amber.
I contenuti si organizzano tramite gli indexes: è il meccanismo con cui Hugo costruisce raggruppamenti come tag o categorie a partire dai valori dichiarati nel frontmatter, e ne genera le pagine di elenco. Il comando ha un’interfaccia a sottocomandi sul modello di git; hugo server avvia un server locale che tiene d’occhio i file e fa LiveReload, così a build finita il browser si aggiorna senza che tu debba ricaricare.
Punto critico
La differenza che si misura davvero è il tempo di build, e arriva da due scelte sommate: il linguaggio compilato e l’assenza di un avvio dell’interprete a ogni invocazione. A parità di contenuti, una rigenerazione che con una toolchain interpretata richiede decine di secondi o minuti, con Hugo si chiude in una frazione di secondo. Il numero esatto dipende dalla macchina e dal sito, quindi va misurato caso per caso con un time hugo sul proprio progetto invece che dedotto dal benchmark di qualcun altro.
Più del cronometro conta la conseguenza sul flusso di lavoro. Quando la build è quasi istantanea, il watch sui file più LiveReload rendono continua l’anteprima: salvi un file Markdown e la pagina nel browser è già aggiornata. È un ciclo di riscontro stretto, vicino a quello di un’applicazione con ricaricamento a caldo, ma l’output resta HTML statico, senza un processo in ascolto in produzione.
Poi c’è la distribuzione. Il binario unico semplifica la riproducibilità: la stessa versione di hugo produce lo stesso output in sviluppo e in build, senza un grafo di dipendenze da congelare in un lockfile. Lo svantaggio speculare è che il motore Markdown e le funzioni dei template sono quelli compilati dentro quel binario — per cambiarli aggiorni Hugo, non sostituisci una gemma.
Implicazioni
Lo schema operativo è facile da automatizzare: il sorgente vive in un repository Git, una macchina con il binario di Hugo fa la build, l’HTML prodotto viene copiato su un web server o su un hosting statico. Sul server di produzione non serve nessun runtime applicativo, e la superficie esposta è quella di file statici, senza un livello dinamico da aggiornare per ragioni di sicurezza.
Per chi arriva da Jekyll il cambio di template ha un costo: si passa da Liquid alla sintassi di html/template, che ha regole proprie su funzioni, pipeline e contesto dei dati. Il modello mentale dei contenuti — Markdown più frontmatter, raggruppamenti derivati dai metadati — resta riconoscibile, ma i layout vanno riscritti. Per un sito nuovo questo costo non c’è; per una migrazione va messo in conto.
Limiti
Hugo alla 0.9.0 è un progetto giovane, poco più di due anni di storia e un numero di versione che lo dichiara. Il bacino di temi e di documentazione è molto più piccolo di quello che Jekyll ha accumulato dal 2008, e diverse convenzioni si muovono ancora da una release all’altra. Chi adotta ora mette in conto di seguire le note di rilascio: ho visto cambiare nomi e comportamenti tra versioni vicine, e parte della documentazione di terze parti rimanda a impostazioni già spostate.
Restano fuori dal raggio d’azione i siti che chiedono logica a runtime — autenticazione, contenuti personalizzati per utente, scritture lato server: lì un generatore statico è lo strumento sbagliato a prescindere dal linguaggio. Hugo dà il meglio quando il contenuto è prevalentemente in lettura e cambia per pubblicazioni successive: blog, documentazione, pagine di progetto. Per tutto il resto, il guadagno sul tempo di build non ripaga l’assenza di un livello dinamico.
- https://github.com/spf13/hugo
- https://gohugo.io/
- https://spf13.com/project/hugo/
- https://github.com/russross/blackfriday
- https://golang.org/pkg/html/template/
- https://jekyllrb.com/
- https://www.noze.it/insights/hugo-static-site/
Immagine di copertina: La mascotte ufficiale del linguaggio Go, il gopher azzurro disegnato da Renee French, in versione a colori ad alta risoluzione — illustrazione di Renee French, CC BY 3.0 — https://commons.wikimedia.org/wiki/File:Gogophercolor.png