Il 18 agosto 2026 al Nottingham University Hospitals NHS Trust bisognava copiare un database di radioterapia per la reportistica. È stata usata una serie di istruzioni già scritte per un altro sistema dell’ospedale, e un’impostazione che andava cambiata prima del lancio è rimasta com’era. Il processo è partito sul database della maternità e lo ha sovrascritto. Le informazioni cliniche sono state recuperate; lo storico di chi ha consultato quelle cartelle fra settembre 2011 e novembre 2022, in gran parte no. Lo scrive l’ospedale nella sua dichiarazione ufficiale, ripresa da The Register.

Lo stesso script, un altro sito WordPress

Chi gestisce più siti WordPress ha lo stesso tipo di script in una cartella: la copia da produzione a staging, il search-replace dei domini, l’import del dump del cliente. Lo si copia per il cliente successivo e si cambia quello che serve. Il guaio è che con WP-CLI il sito su cui agisci spesso non è scritto nello script. Lo decide la cartella da cui lo lanci: la documentazione di WP-CLI spiega che il file wp-cli.yml viene cercato nella cartella corrente e poi risalendo verso l’alto, e da lì arrivano path, url e gli alias come @production. Basta un terminale aperto nella cartella sbagliata, o un alias rimasto quello del cliente precedente, e lo stesso comando parte su un altro database. Nessun errore, nessun avviso: WP-CLI fa esattamente quello che gli hai chiesto, sul sito che ha trovato.

La soluzione: lo script chiede al sito chi è

A Nottingham il guasto è stato un’impostazione dimenticata, e la memoria di chi lancia un comando non è un controllo. Lo script deve domandare al sito dove si trova, prima di scrivere qualsiasi cosa, e fermarsi se la risposta non è quella attesa.

#!/usr/bin/env bash
set -euo pipefail

ATTESO="https://staging.cliente.ch"   # l'unica riga da cambiare
REALE="$(wp option get home)"

if [ "$REALE" != "$ATTESO" ]; then
  echo "STOP: script per $ATTESO, qui risponde $REALE" >&2
  exit 1
fi

wp db export "prima-$(date +%Y%m%d-%H%M%S).sql" --porcelain
wp search-replace "https://www.cliente.ch" "$ATTESO" --dry-run

Il punto sta nella riga ATTESO. Se copi lo script per un altro cliente e dimentichi di cambiarla, lo script non parte sul sito nuovo: si ferma, perché il sito nuovo risponde con un altro indirizzo. Provato con un WP-CLI simulato su quattro casi: sito giusto, sito sbagliato, database irraggiungibile e indirizzo con la barra finale. Solo il primo arriva all’export; gli altri escono con codice 1 prima di qualsiasi scrittura, e il database spento lo ferma grazie a set -e. Il confronto è esatto apposta: nel dubbio, deve fermarsi.

I passaggi prima di toccare il database

La guardia è la prima riga dello script, non l’unica. Nell’ordine:

  1. Verifica del sito con wp option get home, come sopra. Se lavori con un alias, mettilo in ogni comando e non solo nel primo: wp @staging option get home controlla staging, ma un wp search-replace senza alias due righe dopo torna a dipendere dalla cartella.
  2. Dump con data e ora nel nome, con wp db export. L’opzione --porcelain stampa solo il nome del file creato, comodo da scrivere nel log.
  3. Prova a vuoto: wp search-replace con --dry-run esegue tutta l’operazione e mostra il report senza salvare niente.
  4. Sul server di produzione, un wp-cli.yml con disabled_commands che elenca db import, db reset e search-replace. Con WP-CLI 2.12 la risposta è The 'db import' command has been disabled from the config file, anche lanciando da una sottocartella. Protegge solo chi lavora dentro quell’albero di cartelle: la guardia nello script vale ovunque.

L’errore da evitare: fidarsi del dry-run

Il dry-run dà una sicurezza che non si è guadagnato. Ti dice cosa succederebbe al database su cui gira, non se quello è il database giusto. Sul sito sbagliato produce un report perfettamente credibile: tabelle, colonne, qualche centinaio di sostituzioni. Togli --dry-run, rilanci, e hai cambiato i domini nel sito di un altro cliente. Il dry-run risponde alla domanda “cosa cambia”, la guardia alla domanda “dove”: servono tutte e due, e nell’ordine giusto. Vale anche per il backup: un dump fatto dalla cartella sbagliata salva il sito che non stai toccando, ed è per questo che l’export viene dopo la verifica. Chi si fa scrivere lo script di migrazione da un assistente AI ha lo stesso problema, moltiplicato: il modello scrive comandi corretti ma non sa su quale macchina gireranno. Ne ho scritto a proposito dei replace proposti dall’AI: la prova a vuoto è necessaria, non sufficiente.

Take finale

Il dettaglio più utile di Nottingham è un altro. I dati clinici sono tornati; lo storico degli accessi in gran parte no. Su WordPress i plugin di activity log di norma scrivono quel registro nello stesso database del sito, quindi un import sul database sbagliato si porta via anche la prova di cosa era successo prima. La mia take: uno script che scrive su un sito deve saper dire di no da solo, senza contare su chi lo lancia. È lo stesso principio delle automazioni che leggono prima di agire, applicato al terminale. Poche righe di confronto costano un minuto la prima volta e zero le successive.

Da fare oggi: apri la cartella degli script di migrazione, cerca quelli che chiamano db import o search-replace e metti la guardia in testa a ciascuno. Uno script senza un indirizzo atteso è uno script che non sa dove si trova.