Arbeitspaket O. Der Teil, um den es geht, ist nicht das Sichern, sondern das
Zurueckspielen: eine Sicherungsfunktion, deren Wiederherstellung nie gelaufen
ist, ist eine Vermutung.
Die Datenbank wird nicht kopiert, sondern von SQLite ausgeschrieben (VACUUM
INTO). Ein copy() waehrend eines Schreibvorgangs ergibt eine Datei, die
vorhanden aussieht und sich am Tag der Wiederherstellung nicht oeffnen laesst.
Der Medienordner gehoert dazu - ohne die Dateien stellt die Sicherung eine
Website voller kaputter Bilder her. .env gehoert nicht dazu: darin steht
APP_SECRET, und eine Sicherung wird heruntergeladen und weitergereicht. Was
nicht drin ist, steht im Manifest.
Jede Sicherung traegt Zeitpunkt, Anlass, Fassung, Migrationsstand,
Inhaltszahlen und die SHA-256 der Datenbank. Die Uebersicht zeigt
"unversehrt" oder "Pruefsumme falsch" - eine Sicherung, von der man erst beim
Zurueckspielen erfaehrt, dass sie kaputt ist, waere schlimmer als keine.
Beim Zurueckspielen: neuere Fassung wird abgewiesen (mit --force geht es),
zuerst wird der aktuelle Stand gesichert, Wartungszustand an, Medien werden
ergaenzt statt ersetzt - eine Datei, die nach der Sicherung hochgeladen wurde,
bleibt liegen. Die Massnahme gegen Datenverlust darf nicht selbst welchen
verursachen. In der Verwaltung gibt es Liste und Anlegen-Knopf, aber kein
Zurueckspielen: ein Knopf, der die ganze Datenbank ersetzt, gehoert nicht
neben einen, der eine Seite speichert.
Nachgewiesen, zweimal: acht Tests gegen eine wegwerfbare Installation
(Sicherung, Seite und Bilddatei loeschen, zurueckspielen, beides wieder da) -
und von Hand auf der Entwicklungsinstallation mit einer eigens angelegten
Testseite: 5 Seiten, gesichert, geloescht (4), zurueckgespielt (5, Seite mit
ihrer Adresse wieder da), Testseite entfernt (4). Damit ist der aelteste
offene Punkt aus Paket I erledigt.
Ein Fall, den erst der Test zeigte: zwei Sicherungen in derselben Sekunde -
nicht akademisch, denn die Wiederherstellung legt selbst eine Sicherheitskopie
an. Der Name bekommt jetzt eine Nummer, statt dass die zweite scheitert.
Offen: MySQL (mysqldump fehlt) und ein Download ueber den Browser.
344 PHP-Tests / 1397 Assertions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Aus dem Plan in docs/UPDATE.md ist Code geworden: cms:update, cms:health, der
Bildschirm "System" und der Wartungszustand.
Die Vorprüfung ist die halbe Arbeit. Sie fragt nach proc_open, Git, sauberer
Arbeitskopie, Herkunft, Schreibrechten und Platz - und danach, ob Datenbank
und Medienordner wirklich von Git ausgenommen sind. Das ist der Punkt, an dem
ein Update Inhalte zerstören könnte, also wird er geprüft und nicht
angenommen. Jede Prüfung sagt, was zu tun ist, nicht bloß "fehlgeschlagen".
Der Rückweg ist Teil des Wegs: scheitert ein Schritt, wird auf den vorherigen
Commit zurückgesetzt. Die Datenbank nur dann zurückgeschrieben, wenn die
Migration schon begonnen hatte - sonst verlöre der Rückweg Inhalte, die
während des Updates entstanden sind. Antwortet der alte Stand im Rauchtest,
geht die Website wieder online; antwortet er nicht, bleibt der Wartungszustand,
weil eine kaputte Seite schlechter ist als eine ehrliche Meldung.
Der Rauchtest läuft als eigener Prozess, denn nur ein frischer Prozess führt
den neuen Code aus. Ein Test im laufenden Prozess hätte den alten geprüft und
"alles in Ordnung" gemeldet.
Kein symfony/process: der Runner sind dreißig Zeilen über proc_open mit dem
Befehl als Array - kein Shell, nichts zu maskieren, und "proc_open ist
abgeschaltet" wird die erste Antwort statt der Überraschung im dritten
Schritt.
285 PHP-Tests / 1244 Assertions. Sechs davon fahren den Updater gegen echte
Git-Repositorys in einem Temp-Verzeichnis, mit Platzhalterskripten für die
zwei Befehle - inklusive des Fehlschlags, nach dem Code und Datenbank
zurückgesetzt sein müssen. Nicht gegen dieses Projekt: ein Test, der die
Arbeitskopie auscheckt, in der er läuft, ist ein Weg, Arbeit zu verlieren.
Offen bleibt die Sicherung einer MySQL-Datenbank per mysqldump - dafür fehlt
die Installation; der Updater sagt das im Schritt "Sicherung".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>