ppag / contao-autoupdate-bundle
Daily security and patch-level updates for Contao 5.3/5.7 installations, with backup, health check and rollback.
Package info
gitlab.com/pp_cto_modules/contao-autoupdater
Type:contao-bundle
pkg:composer/ppag/contao-autoupdate-bundle
Requires
- php: ^8.1
- ext-curl: *
- ext-json: *
- contao/core-bundle: ^5.3
- symfony/config: ^6.4 || ^7.0
- symfony/console: ^6.4 || ^7.0
- symfony/dependency-injection: ^6.4 || ^7.0
- symfony/http-kernel: ^6.4 || ^7.0
Requires (Dev)
- contao/manager-plugin: ^2.3
- phpunit/phpunit: ^10.5 || ^11.0
Suggests
None
Provides
None
Conflicts
- contao/manager-plugin: <2.3 || >=3.0
Replaces
None
README
Tägliche Sicherheits- und Patch-Updates für Contao 5.3 und 5.7, mit Backup, Health-Check und Rollback. Jede Installation aktualisiert sich selbst (als ihr eigener System-User) und schreibt einen JSON-Report, den ein separater Orchestrator einsammelt und per Mail zusammenfasst.
Installation
composer require ppag/contao-autoupdate-bundle:^1.0
Das Bundle aktualisiert sich selbst innerhalb der Major-Version (siehe Selbst-Update).
Eine fest gepinnte Version (1.0.4) verhindert das, der Report weist dann darauf hin.
Voraussetzungen:
- Contao 5.3 oder 5.7, PHP ≥ 8.1,
ext-curl - Composer: Ist der Composer auf dem Server älter als 2.8 (z. B. 2.2 aus dem Debian/Ubuntu-Paket), lädt der
Autoupdater beim ersten Lauf eine aktuelle
composer.pharins Datenverzeichnis (Prüfsumme wird geprüft) und aktualisiert sie wöchentlich. Dafür braucht der Server ausgehenden Zugriff auf getcomposer.org. proc_opendarf nicht indisable_functionsstehenein System-Cron, der
contao:cronjede Minute als Site-User aufruft:* * * * * /usr/bin/php /pfad/zur/installation/vendor/bin/contao-console contao:cron
Konfiguration (.env.local)
AUTOUPDATE_MODE=check # off | check | security | patch (Standard: check)
AUTOUPDATE_SITE_KEY= # optional, Standard: Domain aus /home/www-<kunde>/<domain>/...
AUTOUPDATE_HOUR=6 # optional 0–23, Stunde des Update-Fensters (Standard 6 = 06:00–06:59)
AUTOUPDATE_SLOT= # optional 0–59, überschreibt den Hash-Slot
AUTOUPDATE_CHECK_URLS= # optional, Standard: /,/contao/login (z. B. /,/kontakt,/contao/login)
AUTOUPDATE_BASE_URL= # optional, Standard: https://<AUTOUPDATE_SITE_KEY>
AUTOUPDATE_DATA_DIR= # optional, Standard: <root>/shared/autoupdater (lokal: var/autoupdate)
AUTOUPDATE_COMPOSER= # optional, eigener Composer (ab 2.8, sonst wird composer.phar geladen)
AUTOUPDATE_SIZE_TOLERANCE=0.3 # optional, erlaubte Grössenabweichung im Health-Check
AUTOUPDATE_KEEP_REPORTS=30 # optional, Anzahl Tagesreports
AUTOUPDATE_HTTP_AUTH= # optional "user:passwort" für Seiten mit Server-Auth (sonst Health-Check über den Kernel)
AUTOUPDATE_VERIFY_TLS= # optional, Standard: 1; 0 nur lokal (selbstsignierte Zertifikate)
AUTOUPDATE_ACCEPT_404= # optional, Standard: 1; 0 = 404 aus dem Kernel nicht als Ausgangslage akzeptieren
AUTOUPDATE_SELF_UPDATE=on # on | early | off (Standard: on), siehe Selbst-Update
AUTOUPDATE_SELF_UPDATE_DELAY=3 # optional, Wartezeit in Tagen für "on"
Der Site-Key bestimmt den Slot (Minute innerhalb von AUTOUPDATE_HOUR), den Namen im Report und die
Health-Check-URL (https://<site-key>). Liegt die Installation nach der Server-Konvention unter
/home/www-<kunde>/<domain>/ (auch mit releases/<n> oder current), wird die Domain automatisch
verwendet. Nur bei abweichendem Pfad AUTOUPDATE_SITE_KEY setzen, und AUTOUPDATE_BASE_URL nur, wenn
die Seite nicht unter https://<domain> erreichbar ist (z. B. mit www.).
| Modus | Verhalten |
|---|---|
off | nichts prüfen, nur einen Report skipped schreiben |
check | Advisories und Contao-Patches melden, nichts ändern |
security | nur Sicherheitsfixes einspielen |
patch | Sicherheitsfixes + Contao-Patch-Updates (z. B. 5.3.38 → 5.3.39) |
Seiten, die per Git/Deployer ausgerollt werden, im Modus check betreiben oder die lock_diff aus dem
Report ins Repository übernehmen. Sonst dreht das nächste Deployment das Update zurück.
Reports, Tagesstatus und Backups liegen ausserhalb des Releases in <root>/shared/autoupdater, damit sie ein
Deployment überleben. <root> ist /home/www-<kunde>/<domain> (wie PP_*_ROOT) bzw. das Verzeichnis über
current oder releases/<n>. Nur ausserhalb dieser Struktur (lokal) bleibt es bei var/autoupdate im Projekt.
Das Verzeichnis wird bei Bedarf angelegt. Beim ersten Lauf werden state.json, self-update.json, latest.json
und die Tagesreports aus dem alten var/autoupdate übernommen.
Ablauf
- Der Cronjob läuft jede Minute (nur im CLI-Scope, nie über den Webcron) und wird nur innerhalb von
AUTOUPDATE_HOURaktiv (Standard 06:00–06:59, Server-Zeitzone von PHP, z. B.Europe/Zurich). - Ab dem Slot der Installation (
crc32(AUTOUPDATE_SITE_KEY) % 60) startet er einmal pro Tagppag:autoupdate:runals separaten Prozess. - Der Command prüft mit
composer auditundcomposer outdated "contao/*" --patch-only. - Gibt es etwas einzuspielen: Health-Check vorher → Backup (
composer.json/.lock+contao:backup:create) → Maintenance-Mode →composer update→ Migrations-Check →cache:clear/cache:warmup→ Maintenance-Mode aus → Health-Check nachher. - Neue Datenbank-Schemaänderungen werden nie automatisch ausgeführt: Rollback, Status
rolled_back. - Schlägt der Health-Check fehl (Status ≥ 500, fehlender
<title>, Grössenabweichung oder neueCRITICAL/ERROR-Einträge invar/logs/prod-*.log), wird zurückgerollt. Gezählt werden nur Log-Einträge ab dem Moment, in dem der neue Code live ist. Einträge zu Dateien des alten, durchcache:cleargelöschten Containers (var/cache/prod/Container<hash>/) stammen von Prozessen, die vor dem Update gestartet wurden (Messenger-Worker,contao:cron), und werden ignoriert. Die Worker werden mitmessenger:stop-workersneu gestartet. - Ist die Seite über HTTP nicht erreichbar (HTTP-Auth ohne
AUTOUPDATE_HTTP_AUTH, IP-Sperre, DNS), läuft der Health-Check über den Contao-Kernel: Jede URL wird in einem eigenen PHP-Prozess wie überpublic/index.phpabgearbeitet, ohne Webserver und ohne HTTP-Cache. Ein 404 aus dem Kernel gilt dabei als gültige Ausgangslage (z. B. unveröffentlichte Seiten auf Staging), nach dem Update wird dann die 404-Seite verglichen (abschaltbar mitAUTOUPDATE_ACCEPT_404=0). Nur wenn auch das fehlschlägt (Status 0, 5xx oder ein anderer 4xx), wird nichts aktualisiert.
Selbst-Update
Neue Versionen des Bundles werden gestaffelt ausgerollt, damit ein fehlerhaftes oder kompromittiertes Release nicht am nächsten Morgen auf allen Installationen läuft:
AUTOUPDATE_SELF_UPDATE | Verhalten |
|---|---|
early | Testgruppe (z. B. eigene Seiten): neue Version beim nächsten Lauf |
on | alle anderen: erst AUTOUPDATE_SELF_UPDATE_DELAY Tage (Standard 3), nachdem die Installation die Version zum ersten Mal gesehen hat |
off | nie, z. B. als Notbremse |
- Die Wartezeit zählt ab der ersten Sichtung (
self-update.jsonim Datenverzeichnis), nicht ab dem Release-Datum: Das Datum eines Git-Tags lässt sich fälschen. Ein fehlerhaftes Release in dieser Zeit zurückziehen (Tag löschen bzw. eine korrigierte Version taggen). - Nur stabile Versionen derselben Major-Version, nie ein Downgrade. Nur das Bundle selbst wird aktualisiert, nicht seine Abhängigkeiten (z. B. Contao).
- Nur in den Modi
securityundpatch. Im Moduschecksteht eine neue Version nur im Report. - Das Selbst-Update kommt vor dem Paket-Update, damit ein Fix im Bundle nicht von einem scheiternden Paket-Update
blockiert wird. Nach einem erfolgreichen Selbst-Update läuft das Paket-Update in einem neuen Prozess mit dem neuen
Code (
--after-self-update). Rollt das Selbst-Update zurück, folgt das Paket-Update mit dem bisherigen Code. - Der Report enthält das Ergebnis des Selbst-Updates in
self_update(status,backup,rollback, ...). Der Gesamtstatus ist der schlechtere von beiden: Ein zurückgerolltes Selbst-Update bleibt sichtbar. - Gleicher Ablauf wie jedes Update: Backup, Maintenance-Mode, Health-Check, bei Problemen Rollback. Der laufende Prozess arbeitet mit dem alten Code weiter, eine kaputte neue Version wird also vom alten Code zurückgerollt.
- Startet der neue Prozess mit der neuen Version nicht, steht
failedim Report. Startet die neue Version am nächsten Tag gar nicht mehr, fehlt der Tagesreport. Beides meldet der Orchestrator als kritisch.
Neue Features also einfach taggen: Die Testgruppe hat sie am nächsten Tag, alle anderen nach der Wartezeit.
Report
<root>/shared/autoupdater/latest.json und <root>/shared/autoupdater/reports/YYYY-MM-DD.json (atomar geschrieben, Rechte 0644).
Der Orchestrator (www-data) braucht Leserechte auf diesen Pfad, inklusive der übergeordneten Verzeichnisse.
status: ok | updated | skipped | action_required | rolled_back | failed
self_update (nur wenn eine neuere Version existiert): installed, available (neueste Version), target
(Version, die jetzt installiert werden darf, sonst null) und due_at (ab wann available installiert wird).
Wurde das Selbst-Update ausgeführt, zusätzlich status, updates, backup, health_problems, rollback,
notes und errors.
Weitere Dateien in <root>/shared/autoupdater/: state.json (Tagesstatus), run.log (Ausgabe des letzten Cron-Laufs),
self-update.json (erste Sichtung neuer Bundle-Versionen), backup/<datum>/ (composer.json/.lock vor dem Update). Datenbank-Backups landen in var/backups/.
Manuell ausführen
vendor/bin/contao-console ppag:autoupdate:run --mode=check --no-report # nur anzeigen
vendor/bin/contao-console ppag:autoupdate:run # wie der Cron, mit Report
Entwicklung
composer install
vendor/bin/phpunit
Lokal in einer Contao-Installation testen (Path-Repository mit Symlink), in deren composer.json:
"repositories": [
{ "type": "path", "url": "../../_modules/contao-autoupdater", "options": { "symlink": true } }
],
"require": {
"ppag/contao-autoupdate-bundle": "@dev"
}
Lizenz
MIT