Skip to content

refactor(ionos-core): make mu-plugin update safe with staging, backup and rollback - #961

Draft
lgersman wants to merge 3 commits into
ionos_core_loop_registerfrom
refactor/mu-plugin-update
Draft

lgersman wants to merge 3 commits into
ionos_core_loop_registerfrom
refactor/mu-plugin-update

Conversation

@lgersman

Copy link
Copy Markdown
Contributor

Beschreibung

Baut das Update des ionos-core MU-Plugins (MU_Plugin_Upgrader) so um, dass nach einem fehlgeschlagenen oder abgebrochenen Update wieder die alte Version installiert ist. Bezieht sich auf die Kriterien „clear destination instead of copydir“ und „test update behavior“ aus dem Ticket zu #956.

Bisher hat copy_dir() direkt im Live-Verzeichnis überschrieben. Ein Abbruch mittendrin hinterließ eine Mischung aus alter und neuer Version, und veraltete Dateien wurden nur nachträglich und ungesichert gelöscht.

Ablauf in MU_Plugin_Upgrader::install()

  1. Das Paket wird geprüft: ionos-core/ und ionos-core.php müssen vorhanden sein, ionos-core.php braucht einen Version-Header. Außerdem muss genug Speicherplatz frei sein (mindestens das Doppelte der Paketgröße) und das Dateisystem direct sein.
  2. Die neue Version wird nach mu-plugins/.ionos-core-staging-<id>/ kopiert. Bis hierhin ist das Live-Plugin unberührt.
  3. Die alten Einträge werden per move_dir() nach .ionos-core-backup-<id>/ verschoben, danach die gestagten Einträge ins Ziel. Währenddessen ist der Maintenance-Mode aktiv.
  4. Bei jedem Fehler wird zurückgerollt. Dafür ist während des Swaps ein shutdown-Hook registriert (restore_on_shutdown()), der auch bei einem Fatal Error oder Timeout die alte Version wiederherstellt.
  5. Nach Erfolg werden Staging und Backup gelöscht. Das Zielverzeichnis entspricht dann exakt dem Paket, es bleiben keine veralteten Dateien übrig.
  6. Schlägt der Rollback selbst fehl, bleibt das Backup erhalten, und die Fehlermeldung nennt den Pfad.

Bewusst nicht den Core-Mechanismus verwendet

WP_Upgrader::install_package() mit clear_destination eignet sich nicht für mu-plugins: Core schützt dort nur ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR und die Theme-Verzeichnisse, nicht WPMU_PLUGIN_DIR. Das Leeren des Ziels würde alle anderen MU-Plugins löschen. Von Core übernommen sind move_dir(), der Maintenance-Mode und die Idee des Rollbacks auf shutdown.

Bekannte Grenzen

  • Bei einem harten Kill (SIGKILL, Serverabsturz) läuft auch der shutdown-Hook nicht. Dann bleibt .ionos-core-backup-* liegen und das Plugin kann fehlen. Eine automatische Wiederherstellung gibt es nicht.
  • Unterstützt wird nur die Dateisystemmethode direct (der alte Code funktionierte mit lokalen Pfaden auch nur dort). Andere Methoden werden mit einer eindeutigen Fehlermeldung abgelehnt.
  • Der Faktor 2 bei der Speicherplatzprüfung ist ein Erfahrungswert, kein Core-Wert.

Checklist

  • Acceptance Criteria are fulfilled (or there is no ticket)
  • tests added if necessary
  • docs added if necessary

Zu den Acceptance Criteria: Die Kriterien „clear destination instead of copydir“ und „test update behavior“ sind mit diesem PR abgedeckt. Der Rest des Tickets liegt in #956, und die Frage der Loop-Daten-Synchronisation ist dort noch offen.

manual testing

  • done / trivial change (docs, ...)

Die Tests laufen mit pnpm test:php --php-opts "--filter MuPluginUpgraderTest". Sie decken ab: erfolgreiches Update, ungültiges Paket, fehlendes Versions-Header, zu wenig Speicherplatz, falsches Dateisystem, Fehler beim Backup und beim Installieren mit Rollback, fehlgeschlagenen Rollback, Maintenance-Mode, Verschieben über Dateisystemgrenzen und einen echten Fatal Error mitten im Swap (separater PHP-Prozess, tests/phpunit/fixtures/fatal-during-swap.php).

Reviewer checklist

  • if requested manual testing done
  • sufficient tests
  • sufficient docs
  • general Code Review

… rollback

Stage the new version, move the installed entries to a backup directory,
swap via rename and restore the previous version if anything fails.
Add tests for the success and failure paths.
Arm a shutdown handler during the swap so a fatal error or timeout restores
the previous version. Use core's move_dir(), check free disk space, enable
maintenance mode while swapping and reject non-direct filesystems and
packages without version header. Add tests including a real fatal error in
a child process.

Bean: vnzi
Copilot AI balanced review requested due to automatic review settings October 21, 2026 13:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Rollback bookkeeping can lose backups or leave mixed installations, and package validation accepts an invalid plugin-directory type.

3 open issues
What changed in this PR

Refactors ionos-core MU-plugin updates to use staging, backups, and rollback instead of overwriting live files.

Changes:

  • Adds package, disk-space, and filesystem checks.
  • Introduces maintenance-mode swaps and shutdown recovery.
  • Adds update failure tests, including a fatal-error subprocess.
File Description
packages/​wp-mu-plugin/​ionos-core/​ionos-core/​update/​tests/​phpunit/​MuPluginUpgraderTest.php Tests updates, validation, rollback, and filesystem moves.
packages/​wp-mu-plugin/​ionos-core/​ionos-core/​update/​tests/​phpunit/​fixtures/​fatal-during-swap.php Triggers a fatal error during installation.
packages/​wp-mu-plugin/​ionos-core/​ionos-core/​update/​class-mu-plugin-upgrader.php Implements staged installation and recovery.
.beans/​vnzi--ionos-core-harden-mu-plugin-update-staging-backup.md Records implementation scope and recovery limitations.

Review effort: Balanced

…upted moves

Validate package entry types and record backup/install attempts before moving,
rolling back from the actual disk state. Refs vnzi.
@lgersman
lgersman marked this pull request as draft October 26, 2026 09:49

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants