# Post-Update-Hook und Migrationen ## Überblick Nach einem erfolgreichen Ausrollen führt der Client einen Post-Update-Schritt aus. Er besteht aus zwei unabhängigen Mechanismen, die einzeln oder gemeinsam genutzt werden: 1. **Migrationen** – geordnete, einmalig laufende Skripte, die mit dem Release ausgeliefert werden. Der übliche Ort für Datenbankänderungen. 2. **Projekt-Callback** – eine Funktion des Projekts, die nach jedem Update läuft. Für Cache leeren, abgeleitete Dateien neu bauen, Rechte setzen. Relevante Dateien: - `manage-client/lib/hooks.php` – beide Mechanismen - `MANAGE_MIGRATIONS_DIR`, `MANAGE_MIGRATIONS_STATE`, `MANAGE_UPDATE_POST_HOOK` Reihenfolge: erst die Migrationen, dann der Callback – damit der Callback sich auf das neue Schema verlassen kann. Scheitert eine Migration, wird der Callback **nicht** ausgeführt. ## Migrationen ### Ablage Migrationen liegen im Verzeichnis aus `MANAGE_MIGRATIONS_DIR` (Standard `migrations/` im Anwendungsstamm) und werden **mit dem Release-Paket ausgeliefert**. ```text migrations/ 2026-08-20-01-add-orders-index.php 2026-08-21-01-backfill-categories.php ``` Ausgeführt wird in **Dateinamen-Reihenfolge**. Ein Datum als Präfix mit laufender Nummer sortiert zuverlässig. Der Dateiname ohne `.php` ist die Kennung der Migration; wird eine bereits ausgeführte Datei umbenannt, läuft sie erneut. ### Aufbau Empfohlene Form – die Datei gibt eine Funktion zurück: ```php exec("ALTER TABLE orders ADD INDEX idx_created (created_at)"); }; ``` Alternativ definiert die Datei eine Funktion `up()`: ```php "/var/www/meinprojekt", "instance" => "meinprojekt-prod", "from_version" => "v1.2.3", "to_version" => "v1.3.0", "backup_dir" => "/…/data/manage/updates/20260820-092114-v1.3.0", "run_id" => "20260820-092114", "migration_id" => "2026-08-20-01-add-orders-index", "pdo" => PDO, // nur wenn MANAGE_BACKUP_DATABASE konfiguriert ist ] ``` `pdo` verwendet die Zugangsdaten, die ohnehin für den Datenbank-Dump konfiguriert sind. Eine zweite Konfiguration ist nicht nötig. Projekte ohne Datenbank arbeiten mit `app_root`. ### Beispiel: Datenbank ```php query( "SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'orders' AND index_name = 'idx_created'" )->fetchColumn(); if ((int) $exists === 0) { $pdo->exec("ALTER TABLE orders ADD INDEX idx_created (created_at)"); } }; ``` ### Beispiel: JSON-Daten ```php $product) { if (!array_key_exists("category_id", $product)) { $data[$index]["category_id"] = null; } } file_put_contents( $file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ); }; ``` ### Zustand Ausgeführte Migrationen werden in `MANAGE_MIGRATIONS_STATE` festgehalten: ```json { "applied": [ { "id": "2026-08-20-01-add-orders-index", "applied_at": "2026-08-20T09:21:14+00:00", "version": "v1.3.0", "duration_ms": 42 } ] } ``` Diese Datei liegt im Datenverzeichnis und ist damit von Updates ausgenommen. Sie sollte im Backup enthalten sein, wenn `data/manage/` in den Quellen steht – ist es standardmäßig nicht, weil das Backup-Verzeichnis sich sonst selbst sichern würde. Wer den Migrationszustand mitsichern will, nimmt ihn einzeln auf: ```php ["as" => "manage", "file" => "data/manage/migrations.json"], ``` ## Projekt-Callback ```php define("MANAGE_UPDATE_POST_HOOK", [ "file" => MANAGE_APP_ROOT . "/includes/after-update.php", "callback" => "myProjectAfterUpdate", ]); ``` ```php false, "error" => "…"]`. Alles andere gilt als Erfolg. Der Callback erhält denselben Kontext wie eine Migration, zusätzlich `migrations` mit der Liste der in diesem Lauf ausgeführten Kennungen. ## Fehlerverhalten Der wichtigste Punkt: **Zu diesem Zeitpunkt sind die Dateien bereits ausgerollt, und es gibt keine Rücknahme.** Ein Fehler wird deshalb laut gemeldet statt still verschluckt. Konkret: - Der Lauf stoppt bei der ersten fehlgeschlagenen Migration. Die folgenden bleiben offen und werden nicht versucht. - Der Callback wird bei einer fehlgeschlagenen Migration übersprungen. - `manageUpdateApply()` kehrt **normal zurück**, mit `deployed => true` und `hook["success"] => false`. - Die Kommandozeile beendet sich mit Exit-Code `1` und nennt die betroffene Datei – meldet aber ausdrücklich, dass das Ausrollen erfolgreich war. - Die Oberfläche zeigt einen roten Hinweis mit dem Namen der Migration. - Beides steht im Client-Protokoll. Wiederherstellung nach einem Fehler: ```bash # 1. Ursache beheben (Migration korrigieren, Rechte setzen, Datenbank prüfen) # 2. Offene Migrationen ansehen php manage-client/bin/manage-client.php migrate --dry-run # 3. Nachziehen php manage-client/bin/manage-client.php migrate ``` Ein erneutes `update --force` rollt die Dateien nochmals aus, führt aber **keine bereits ausgeführten Migrationen erneut aus**. ## Idempotenz Migrationen sollen mehrfach ausführbar sein. Grund: Wenn eine Migration mittendrin scheitert – etwa nach der Hälfte einer Datenumstellung – wird sie nicht als ausgeführt vermerkt und läuft beim nächsten `migrate` erneut von vorn. Nur eine idempotente Migration übersteht das unbeschadet. Praktisch heißt das: vor dem Ändern prüfen, ob die Änderung schon da ist, und Datenumstellungen so schreiben, dass bereits umgestellte Sätze übersprungen werden. ## Ohne Post-Update-Schritt ausrollen ```bash php manage-client/bin/manage-client.php update --skip-hook ``` Rollt nur die Dateien aus. Die Migrationen bleiben offen und können später mit `migrate` nachgezogen werden. Nützlich, wenn die Dateien dringend gebraucht werden, die Datenbankänderung aber in ein Wartungsfenster gehört. ## Weiter - [06_UPDATE_PACKAGING](06_UPDATE_PACKAGING.md) – Migrationen ins Paket bekommen - [04_FUNCTION_API](04_FUNCTION_API.md) – `manageUpdateRunMigrations()`