Du benennst im Schema author in creator um. Das Schema ist sofort aktuell — aber dreihundert Einträge tragen weiter author, und niemand merkt es, bis das Frontend leere Autorenzeilen zeigt. RustyCMS trennt die beiden Schritte bewusst: Das Schema ändert sich per Datei-Edit, der Content per Migration, die du liest, bevor sie läuft.
Eine Datei pro Änderung
// <content>/_migrations/20260504-120000-rename-author-to-creator.json5
{
id: "20260504-120000-rename-author-to-creator",
description: "rename author → creator on post",
operations: [
{ type: "rename_field", collection: "post", from: "author", to: "creator" },
{ type: "delete_field", collection: "post", field: "obsolete_flag" },
],
}
Zwei Operationen: rename_field und delete_field. Die Migration nennt die Collection, die CLI läuft über jeden Eintrag darin — in jedem Space, den sie findet, oder nur in dem, den du mit --env angibst. Der Zeitstempel im Dateinamen sorgt dafür, dass mehrere Migrationen in der richtigen Reihenfolge laufen.
Erst sehen, dann schreiben
# Dry-Run (Default) — zeigt, was sich ändern würde, schreibt nichts
migrate-schema --content-dir ./content
# Anwenden
migrate-schema --content-dir ./content --apply
Ohne --apply passiert nichts. Was angewandt wurde, steht pro Space in einer Datei — ein zweiter Lauf überspringt es. Ein Deploy-Skript kann die CLI daher bei jedem Durchlauf aufrufen: Der Exit-Code ist auch dann 0, wenn nichts zu tun war. Nach einem abgebrochenen Lauf erzwingt --force die Wiederholung.
Nichts wird überschrieben
rename_field legt den Wert nur um, wenn das Ziel noch nicht existiert — ein Eintrag, der schon creator hat, bleibt unangetastet. Lokalisierte Felder wandern mit ihrer kompletten Sprachkarte, { de, en } bleibt zusammen. delete_field entfernt bei einem lokalisierten Feld die ganze Map.
Migration und Schema im selben Commit
Die Migrationsdatei gehört in denselben Commit wie die Schema-Änderung. Wer später nach dem Fehler sucht, findet beides nebeneinander. Eine einmal angewandte Migration wird nicht mehr editiert — die Kennung wird als Text verglichen, ein bearbeiteter Eintrag liefe still noch einmal. Eine Korrektur ist eine neue Datei. Und ein Dry-Run in der CI fängt Tippfehler in der Collection oder kaputtes JSON5 ab, bevor der Content produktiv angefasst wird.
Was die CLI nicht kann
Kein Zusammenfalten mehrerer Felder in ein verschachteltes Objekt, keine Typumwandlung (string → integer), keine Standardwerte für fehlende Keys, kein Verschieben zwischen Collections, kein Rollback. Solche Umbauten sind ein eigenes Skript nach denselben Regeln: Dry-Run als Default, idempotent, Werte unverändert übernehmen. Und die Migration läuft ausschließlich per CLI — kein Endpoint, kein Button im Admin, der versehentlich tausend Dateien umschreibt.