Detailliertes Protokolle der Schritte zum Update von hbmhealthcare Drupal 8 auf Drupal 10, vom ersten Schritt bis zum Golive bei mva.
Intro
- Ziel: hbmhealthcare.com (hci) von Drupal 8.9.8 auf D10 bringen (Zwischenhalt). D11 kommt später als sauberer Neuaufbau.
- Arbeitskopie: Portiert wird auf einer Kopie
hci-updatemit eigener DB. Das Original bildet den Livestand ab und bleibt unberührt. - Diese Etappe: Grossreinigung vor dem D9-Sprung. Composer 2, alle Module unter Composer, ungenutzte Module entfernt, eigene Sachen D9-tauglich, Core auf 8.9.20.
1 -- Arbeitskopie anlegen
1.1 -- Projektordner duplizieren
- Im Finder
~/01_webroot/10-drupal-dev/drupal-8/hbmhealthcare.comkopieren, daneben einfügen und inhci-updateumbenennen. - Versteckte Dateien (
.git,.htaccess) kopiert der Finder automatisch mit.
1.2 -- Datenbank duplizieren
- Neue DB und Benutzer anlegen (
hci_update_drupal), Dump des Originals importieren. - Danach einen eigenen Dump der Kopie machen (Sequel Ace > Export).
1.3 -- Settings anpassen
- Multisite-Ordner
web/sites/dev-hbmhealthcare.selfip.netinweb/sites/local.hci-update.testumbenennen. - DB-Zugang auf die neue DB umstellen und
trusted_host_patternsauf den neuen Hostnamen anpassen. - MAMP: neuer Host
local.hci-update.test, Site folderhci-update/web, PHP 7.4.
1.4 -- Drush-Alias für die Kopie
- ### WICHTIG: Der Alias
drushhbmzeigt mit absolutem Pfad aufs Original. Für die Kopie braucht es einen eigenen, sonst arbeitet man aus Versehen am Original. - In
~/.zshrcergänzen: alias drushci='/Users/pierre/01_webroot/10-drupal-dev/drupal-8/hci-update/vendor/drush/drush/drush -l local.hci-update.test'- Pfad endet auf
/drush/drush/drush(die eigentliche Datei, nicht der Ordner). - Aktivieren und prüfen:
source ~/.zshrc usephp74 drushci status- Kontrolle: Site URI
local.hci-update.test, DB name der Kopie, Drupal root inhci-update.
2 -- Repository von Bitbucket auf GitLab
Das alte Repo lag auf Bitbucket (Atlassian), der Rückfallpunkt war nur lokal. Neu liegt alles auf GitLab, damit ein externes Backup besteht.
2.1 -- GitLab-Projekt anlegen
- Gruppe: per-net > web
- Projektname: hci-update
- Sichtbarkeit: privat
- Keine Readme-Datei erstellen (Projekt muss leer sein, sonst Konflikt beim Push)
2.2 -- Remote umstellen
git remote set-url origin git@gitlab.com:per-net/web/hci-update.git git remote -v- ### WICHTIG:
set-url, nichtadd.originexistiert schon (zeigte auf Bitbucket),git remote add originbricht mit «remote origin already exists» ab.
2.3 -- Kontrolle: keine DB-Dumps im Git
git ls-files | grep -iE "\.sql|\.gz|\.zip"- Keine Ausgabe = sauber.
2.4 -- Push
git push -u origin master- Altes Projekt: Branch heisst
master, nichtmain. Danach reichtgit push. - ### TIPP: «Your branch is ahead of 'origin/master' by 1 commit» heisst nur: lokaler Commit, noch nicht gepusht. Mit «working tree clean» muss nichts committet werden.
- ### TIPP: Warnung «too many unreachable loose objects» beim Push:
git pruneausführen, danach im Finder.git/gc.loglöschen (Cmd + Shift + . für versteckte Dateien). gc.log ist nur das Protokoll und bleibt stehen, bis man es löscht.
3 -- Composer 2 Umstellung
3.1 -- Das Problem
composer requirebricht ab mit «requires composer-plugin-api ^1.0». Vier alte Plugins verstehen Composer 2 nicht:composer/installersundcweagans/composer-patches: alte Versionendrupal-composer/drupal-scaffold: veraltet, ersetzt durchdrupal/core-composer-scaffolddrupal/console: tot, gibt es für D10 nicht
- ### WICHTIG: Nicht auf Composer 1 zurückwechseln. Packagist hat die Daten für Composer 1 eingefroren, und D10 verlangt Composer 2.
- Schritte 3.2 bis 3.5 ändern nur die
composer.json(--no-update), erst 3.7 installiert. Die Plugin-Warnungen bis dahin ignorieren.
3.2 -- Drupal Console entfernen
composer remove drupal/console --no-update
3.3 -- Scaffold-Plugin austauschen
composer remove drupal-composer/drupal-scaffold --no-update composer require drupal/core-composer-scaffold:^8.9 --no-update
3.4 -- composer.json von Hand anpassen
- Im Block
"scripts"die Zeile des alten Plugins löschen: "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold",- Im Block
"extra"zuoberst den Webroot angeben und die automatischen .gitignore-Dateien abschalten (wie im Bauplan, siehe Starter 1.7): "drupal-scaffold": { "gitignore": false, "locations": { "web-root": "web/" } },- Im Block
"config"die Plugins freigeben (Composer 2 verlangt das ausdrücklich): "config": { "sort-packages": true, "allow-plugins": { "composer/installers": true, "cweagans/composer-patches": true, "drupal/core-composer-scaffold": true, "dealerdirect/phpcodesniffer-composer-installer": true } },- Das vierte Plugin kommt über
drupal/core-dev. Ohne Eintrag fragt Composer mittendrin nach.
3.5 -- installers und patches anheben
composer require composer/installers:^1.9 cweagans/composer-patches:^1.7 --no-update- Ab diesen Versionen Composer-2-tauglich. Bewusst in der 1er-Reihe bleiben.
3.6 -- composer.json prüfen
composer validate --no-check-publish- «is valid, but with a few warnings» ist ok. Die «Lock file errors» behebt 3.7.
3.7 -- Gezieltes Update
composer update composer/installers cweagans/composer-patches drupal/core-composer-scaffold drupal-composer/drupal-scaffold "drupal/console*" --with-dependencies- Fasst nur diese Pakete an. Core und Module werden neu ausgepackt, bleiben aber in denselben Versionen (Wechsel Composer 1 > 2).
- Anführungszeichen um
"drupal/console*"nötig, sonst versteht das Terminal das Sternchen falsch. - ### WICHTIG: Dabei wurde
enshrined/svg-sanitizeentfernt, weil das Modul SVG image von Hand reinkopiert war und Composer nichts davon wusste. Behoben in 4.2.
3.8 -- Test, .gitignore-Reste, Commit
drushci cr- Site und Backend durchklicken.
- Falls Composer vor dem Eintrag
"gitignore": falseschon gelaufen ist: Zusatzzeilen in der Haupt-.gitignoremitgit restore .gitignorezurücknehmen und die neuen.gitignore-Dateien unterweb/im Finder löschen. - Die neuen Scaffold-Dateien (
.editorconfig,.gitattributes, README-Dateien) werden versioniert. git add -A git commit -m "Composer 2: Console entfernt, core-composer-scaffold, Plugins angehoben, Scaffold-Dateien versioniert" git push
4 -- Handkopierte Module in Composer übernehmen
Etliche aktive Module lagen in web/modules/contrib, standen aber nicht in der composer.json. Sie wurden damals von Hand reinkopiert. Vor dem D9-Sprung muss Composer alle Module kennen.
4.1 -- Abgleich und Sicherheitskopie
- Ordnerliste mit der
composer.jsonvergleichen: ls web/modules/contrib- Vorher
web/modules/contribim Finder ausserhalb des Projekts sichern (hier:00--backups--hci-update--8-10/modules/contrib). Composer überschreibt die Handkopien, undcontribist nicht im Git.
4.2 -- SVG image
composer require drupal/svg_image:^1.16- Bringt
enshrined/svg-sanitizeselbst wieder mit. Test: Seite mit SVG-Logos (Portfolio).
4.3 -- Die übrigen Module in exakt der installierten Version
composer require drupal/backup_migrate:5.0.1 drupal/better_exposed_filters:5.2.0 drupal/entity_reference_unpublished:1.1.0 drupal/feeds:3.0.0-beta2 drupal/feeds_tamper:2.0.0-beta3 drupal/flood_control:2.3.0 drupal/jquery_ui_datepicker:1.4.0 drupal/jquery_ui_slider:1.1.0 drupal/jquery_ui_touch_punch:1.1.0 drupal/tamper:1.0.0-alpha4 drupal/twig_tweak:2.10.0- Exakte Versionen, damit sich am Code nichts ändert. Aus
8.x-5.2wird für Composer5.2.0.
4.4 -- Kontrolle auf Handänderungen
diff -rq /Users/pierre/01_webroot/10-drupal-dev/drupal-8/00--backups--hci-update--8-10/modules/contrib web/modules/contrib echo $?- Keine Ausgabe und
0= identisch, es gab keine Handänderungen.1= Unterschiede,2= Fehler (z. B. falscher Pfad). - Danach Test (
drushci cr, Structure > Feed types) und Commit.
5 -- Upgrade Status
5.1 -- Installieren und aktivieren
- Upgrade Status braucht eine neuere
webflo/drupal-finder, darum zuerst diese gezielt anheben: composer update webflo/drupal-finder composer require drupal/upgrade_status:^3.0 --dev drushci pm:enable upgrade_status- ### WICHTIG: In Drush 9.2.1 wurde die Kurzform
drushci enmitentity:updatesverwechselt. Darum ausgeschriebenpm:enableverwenden. - Upgrade Status ist ein Prüfwerkzeug (
--dev). Vor dem nächstencexdeaktivieren, damit es nicht in die Config gelangt.
5.2 -- Scannen
- Reports > Upgrade status
- Nur die Gruppe SCAN (eigene Module und Theme) anwählen und Scan selected. Die übrigen Gruppen beurteilt Upgrade Status über die Daten von drupal.org.
- Die rote Drush-Warnung «DRUSH_BASE_PATH» oben ist harmlos, sie kommt vom alten Drush 9.
5.3 -- Eigene Sachen D9-tauglich machen
web/themes/custom/hbm/hbm.info.ymlundweb/modules/custom/ckeditor_div/ckeditor_div.info.yml: untercore: 8.xergänzen:core_version_requirement: ^8 || ^9web/modules/custom/hbmcustom/src/Plugin/Block/hbmcustomSharePriceBlock.php, Zeile 61:path.alias_managerersetzen durchpath_alias.manager.- Test: Aktienkurs im Hero der Startseite (Block-Referenz auf
hbmsharepriceblock). Die Platzierung im Blocklayout (hbmsharepriceblock_2, nur/node/7) ist ein Überbleibsel, node/7 gibt es nicht mehr. - Die drei Twig-Warnungen zu
pernet/nodebleiben vorerst: Die Library aus dem alten, nicht aktiven pernet-Theme wird schon heute nicht geladen.
6 -- Aufräumen und Module anheben
6.1 -- Ungenutzte Module entfernen
- Nicht aktiv, per Composer entfernt:
composer remove drupal/draggableviews drupal/config_installerdevel_php: nie in Composer, Ordner im Finder gelöscht.- Aktiv, aber nicht mehr gebraucht: zuerst deinstallieren, dann aus Composer entfernen:
drushci pm:uninstall search_kint composer remove drupal/search_kint drushci pm:uninstall contentimport composer remove drupal/contentimport- Content Import war nur ein Formular für manuelle CSV-Importe. Team und Portfolio kommen über Feeds, die News über den Aggregator.
- ### WICHTIG: Vor dem ersten Deinstallieren DB-Dump machen, ab hier wird die Datenbank verändert.
6.2 -- Module auf D9-taugliche Versionen anheben
- Spielraum «alte oder neue Hauptversion» vorgeben, Composer wählt die höchste Version, die noch mit D8.9 läuft:
composer require "drupal/crop:^1.5 || ^2.0" "drupal/csv_serialization:^1.4 || ^2.0 || ^3.0" "drupal/entity_reference_unpublished:^1.1 || ^2.0" "drupal/libraries:^3.0@alpha || ^4.0" "drupal/login_emailusername:^1.1 || ^2.0 || ^3.0" --no-update composer update drupal/crop drupal/csv_serialization drupal/views_data_export drupal/entity_reference_unpublished drupal/libraries drupal/login_emailusername -w drushci updb- Anführungszeichen wegen
||nötig. - Test: Bilder (Startseite, Team, Portfolio), Login mit E-Mail-Adresse.
6.3 -- Core auf 8.9.20
- Letzte D8-Version vor dem Sprung auf D9 (empfohlen von Upgrade Status und drupal.org):
composer update drupal/core drupal/core-dev --with-all-dependencies drushci updb drushci cr--with-all-dependencies(kurz-W) erlaubt auch Abhängigkeiten, die selbst in dercomposer.jsonstehen, mitzuziehen. Grenze bleibt^8.8, ein Sprung auf D9 ist so nicht möglich.--with-dependencies(kurz-w) zieht nur Abhängigkeiten mit, die nicht selbst in dercomposer.jsonstehen.
✎ Claude – offen:
- Nächste Etappe: D8.9.20 > D9.5 (Core, Drush 10/11, exakte Versionen aus 4.3 lockern).
- Vor
cex: Upgrade Status deaktivieren. - Aufräumen (Fahrplan 5): pernet-Theme (war Startvorlage, kopiert, nicht abgeleitet), Twig-Verweise
pernet/node,__pernet_helpim Theme, Focal Point (evtl. ungenutzt), Blockplatzierunghbmsharepriceblock_2. - D10-Merkpunkte: CKEditor 4 > 5 (
ckeditor_divfällt weg), Aggregator aus dem Core (News!), Theme-Basisstable, jQuery-UI-Module, PHP 8.3 für Martins Feed-Skript.
7 -- D8.9.20 > D9.5 (PHP 7.4)
Core, Drush und Module auf D9 heben, danach Datenbank nachziehen. Erledigt am 15.10.2026, Ergebnis: Core 9.5.11, Drush 11.6.0.
### WICHTIG: Rückfallpunkt vorher: Dump --d8-9--8-9-20 + Commit.
7.1 -- PHP 7.4 prüfen
- Terminal und MAMP müssen beide auf PHP 7.4 stehen:
usephp74 php -v composer --version
7.2 -- composer.json anpassen
drupal/core,drupal/core-composer-scaffold,drupal/core-devauf^9.5drush/drushauf^11.0,webflo/drupal-finderauf^1.2drupal/upgrade_statusauf^4.0,drupal/develauf^4.1symfony/debugaus require-dev entfernt- Exakte Versionen aus 4.3 mit
^gelockert, Hauptversion beibehalten - In
config > allow-pluginsergänzt (braucht core-dev 9.5): "phpstan/extension-installer": true
7.3 -- Toolbar-Falle
- Erstes Update brach ab mit «drupal/toolbar requires drupal/core ^11.5». Toolbar gibt es seit 2026 auch als Contrib-Projekt (für D11.5+/D12), Composer griff fälschlich dazu statt zum Toolbar im Core.
- Lösung in der
composer.json, nach demconflict-Block: "replace": { "drupal/toolbar": "*" },
### WICHTIG: Gilt für D9 und D10. Beim D11-Neuaufbau wieder entfernen.
7.4 -- Composer-Update
composer update --with-all-dependencies- Fehleranalyse, was eine Version blockiert:
composer why-not drupal/core 9.5.11- Symfony-, Twig- usw. Zeilen sind dort Lärm, die zieht das volle Update ohnehin mit.
### WICHTIG: Nicht composer require "drupal/core:^9.5" --dry-run verwenden. Das ist ein Teil-Update, hält alle anderen Pakete fest und liefert eine lange, irreführende Fehlerliste.
7.5 -- Scaffolding nachholen
- Update brach am Schluss ab: «Could not delete .../web/sites/default/default.services.yml». Ordner ist schreibgeschützt (macht Drupal so).
chmod u+w web/sites/default composer drupal:scaffold
7.6 -- PHP-7.4-Fallen
- Neuere Modul-Versionen geben D9 an, brauchen aber PHP 8. Composer merkt das nicht. Symptom bei
drushci status: «ParseError: syntax error, unexpected ')'». - Alle Contrib-Module auf einmal prüfen (liest nur):
find web/modules/contrib -type f \( -name "*.php" -o -name "*.module" -o -name "*.inc" -o -name "*.install" \) -not -path "*/tests/*" -exec php -l {} \; | grep -v "No syntax errors"- Funktionen, die es erst ab D10.1 gibt (fiel nur im Status Report auf):
grep -rl "DeprecationHelper" web/modules/contrib --include="*.php" --include="*.module" --include="*.inc" --exclude-dir=tests- Betroffene Module auf die vorherige, laufende Version zurückgesetzt:
composer require "drupal/diff:1.0.0" composer require "drupal/backup_migrate:5.0.1" "drupal/field_group:3.1.0" "drupal/auto_entitylabel:3.0.0-beta3" "drupal/crop:2.2.0" "drupal/redirect:1.6.0" composer require "drupal/superfish:1.4.0" --update-with-all-dependencies composer require --dev "drupal/upgrade_status:3.19.0" --update-with-all-dependencies- Superfish 1.4.0 holt die alte Bibliothek
mehrpadin/superfishzurück. - tamper:
src/Attribute/Tamper.phpmeldet Fehler, wird von D9 aber nicht geladen. So gelassen.
### TIPP: Die alte Version steht im Composer-Log bei «Upgrading x (alt => neu)».
7.7 -- Datenbank nachziehen
drushci status drushci updatedb drushci cache:rebuild- Warnung «Your PHP installation is too old» ist bei 7.4 normal.
- «Uninstall Stable if it is no longer needed»: blieb installiert, weil Theme hbm darauf aufbaut.
- Dump danach:
--d8-9--9-5-updb
7.8 -- Status Report
- Erwartete Errors: Core security coverage / update status (D9 ohne Support), Module update status (Sicherheitsupdates), PHP 7.4, rebuild_access.
- «Mismatched entity and/or field definitions» für
user.field_pending_expire_sent(Password Policy) behoben mit: drushci php:eval '$d = \Drupal::service("entity_field.manager")->getFieldStorageDefinitions("user")["field_pending_expire_sent"]; \Drupal::entityDefinitionUpdateManager()->updateFieldStorageDefinition($d);' drushci cache:rebuild- Kein Feedback = gut.
7.9 -- Sichern
- Dump:
--d8-9--9-5-ok git add --all git commit --message "D8.9.20 > D9.5.11: Core, Drush 11, Module D9, updatedb" git push
✎ Claude – offen:
- Nächste Etappe: D9.5 > D10. Vorschlag: zuerst auf D9.5 PHP 8.1 einstellen, fixierte Module aus 7.6 wieder lockern und anheben, Upgrade Status 4, dann Core-Sprung mit PHP 8.3.
- Vor
cex: Upgrade Status deaktivieren. - Aufräumen (Fahrplan 5): pernet-Theme (war Startvorlage, kopiert, nicht abgeleitet), Twig-Verweise
pernet/node,__pernet_helpim Theme, Focal Point (evtl. ungenutzt), Blockplatzierunghbmsharepriceblock_2,rebuild_accessin den lokalen Settings ausschalten, Google Analytics prüfen (nicht konfiguriert), Sicherheitskopie00--backups--hci-update--8-10löschen. - D10-Merkpunkte: CKEditor 4 > 5 (
ckeditor_divfällt weg), Aggregator aus dem Core (News!), Color, Quick Edit, RDF aus dem Core, Themes Bartik, Seven (> Claro) und Stable aus dem Core, Theme-Basisstable, jQuery-UI-Module, PHP 8.3 für Martins Feed-Skript und hbmcustom,replacefür Toolbar bleibt.
8 -- D9.5 > D10, Teil 1: PHP 8.1 und Module heben
8.1 -- PHP 8.1 einrichten
- MAMP PRO: Settings > Languages > PHP, bei PHP 8.1 die neueste Version (8.1.34) installieren.
- Alias in
~/.zshrcergänzt (Muster wie usephp74): usephp81() { export PATH="/Applications/MAMP/bin/php/php8.1.34/bin:$PATH"; php -v; }- MAMP PRO: Site
local.hci-update.test> PHP version 8.1.34, dann Save (oben steht sonst «Unsaved»). - Terminal umschalten und prüfen:
source ~/.zshrc usephp81 composer --versioncomposer --versionzeigt am Schluss die PHP-Version, mit der Composer läuft. Muss 8.1.34 sein.- Prüfen, ob composer.json eine PHP-Version vorgibt (keine Ausgabe = gut):
grep -n -A 3 platform composer.json
8.2 -- Erster Test auf PHP 8.1
- Site lief sofort (Status Report: Drupal 9.5.11, PHP 8.1.34).
- Auf allen Seiten «Deprecated function: parse_str()» aus dem alten Redirect 1.6.0. Harmlos, verschwindet mit dem Modul-Update.
8.3 -- Fixierte Module heben
- Ablauf pro Modul:
composer requiremit «^»-Version (Composer nimmt die neueste passende), dann updatedb, Cache, kurz testen, Commit. - Versions-Angabe immer in Anführungszeichen, sonst stolpert zsh über das
^. composer require "drupal/redirect:^1.9" --update-with-all-dependencies drushci updatedb drushci cache:rebuild git add --all git commit --message "PHP 8.1, redirect 1.6.0 > 1.11.0" git push- Weitere Module nach gleichem Muster:
composer require "drupal/field_group:^3.1" --update-with-all-dependencies composer require "drupal/diff:^1.0" --update-with-all-dependencies composer require "drupal/backup_migrate:^5.0" --update-with-all-dependencies composer require "drupal/auto_entitylabel:^3.0" "drupal/crop:^2.2" --update-with-all-dependencies- Ergebnis: redirect 1.11.0, field_group 3.6.0, diff 1.6.0, backup_migrate 5.1.4, auto_entitylabel 3.1.0, crop 2.6.0.
- Tests: Field Group = Manage form display (Tabs da?), Diff = Inhalt > Revisions > zwei Versionen > Compare selected revisions (durchgestrichen = entfernt, unterstrichen = neu), Backup and Migrate = Configuration > Development > Backup and Migrate, Auto Entity Label = Structure > Paragraphs types > Par textblock > Automatic label.
- Superfish bleibt auf 1.4.0 (neuere brauchen D10.1). Läuft auf PHP 8.1 ohne Meldung. Heben nach dem Core-Wechsel.
8.4 -- Conditional Fields (war nicht fixiert, aber zu alt)
- Auf Manage form display: «Warning: Trying to access array offset on value of type null» in
conditional_fields/src/DependencyHelper.php. Unter PHP 7.4 nur ein stiller Hinweis. - Version 1.0.0-alpha10 war uralt, Nachfolger 4.x läuft mit D9, D10 und D11. Ohne stabile Version braucht es
@alpha: composer require "drupal/conditional_fields:^4.0@alpha" --update-with-all-dependencies- 1.0.0-alpha10 > 4.0.0-alpha6, keine DB-Updates nötig. Neuer Reiter «Manage Dependencies» bei den Typen ist normal.
- Structure > Conditional fields listet nur die Inhaltsarten, nicht die Bedingungen.
8.5 -- Upgrade Status 4
- Zuerst prüfen, wie fixiert wurde:
grep -n -E "upgrade_status|php-parser" composer.json- Nur upgrade_status war fixiert, php-parser nicht. Also:
composer require --dev "drupal/upgrade_status:^4.0" --update-with-all-dependencies- 3.19.0 > 4.3.10, nikic/php-parser 4 > 5. Laminas-servicemanager und laminas-text wurden entfernt (brauchte nur das alte Upgrade Status). News und Aggregator laufen weiter.
8.6 -- Cron und Team-Import
drushci cronlief lange und brachte 25 Warnungen: «Non-translatable fields can only be changed when updating the original language», HBM Team Import DE-EN.- Ursache: Nach dem PHP-Wechsel hielt Feeds einmalig alle Einträge für geändert (Portfolio: 101 neu gespeichert, harmlos). Live meldete gleichzeitig «keine neuen Einträge», die Quelle war also unverändert.
- Dabei kam ein alter, versteckter Fehler zum Vorschein: Beim Team ist «Hide non translatable fields on translation forms» aktiv. Dann darf die englische Übersetzung keine nicht übersetzbaren Felder ändern (Area, Email, First name, Last name, Order of List, HBM Global Node ID, Hide CV page), der Import tut das aber.
- Prüfweg: Reports > Recent log messages, Filter Type
feeds, lokal und auf Live vergleichen. - Lösung: Configuration > Regional and language > Content language and translation > Content > Team: Häkchen «Hide non translatable fields on translation forms» entfernen, Save configuration.
- Danach
drushci cron: «Aktualisiert 25 Einträge vom Typ Team». - Einstellung steckt in der DB, nicht im Git.
8.7 -- Sichern
- Dump:
--d9-10--9-5-php81 git add --all git commit --message "upgrade_status 3.19.0 > 4.3.10, nikic/php-parser 5" git push
✎ Claude – offen:
- Nächster Schritt: Reports > Upgrade status durchlaufen lassen und alles D10-tauglich machen. Dann Dump + Commit, Core auf ^10, Drush ^12 oder ^13, PHP 8.3, updatedb, testen. Danach Superfish heben.
- D10-Go-live: Auf Live wird Feeds nach dem PHP-Wechsel ebenfalls alles neu einlesen. Team-Einstellung «Hide non translatable fields on translation forms» muss dort aus sein.
- Test nach D10: in hbmglobal bei einem Team-Mitglied etwas ändern, prüfen ob es in hci ankommt (DE und EN). Portfolio und News analog.
- Vor
cex: Upgrade Status deaktivieren. - Aufräumen (Fahrplan 5): pernet-Theme (war Startvorlage, kopiert, nicht abgeleitet), Twig-Verweise
pernet/node,__pernet_helpim Theme, Focal Point (evtl. ungenutzt), Conditional Fields und Auto Entity Label (prüfen, ob genutzt), Blockplatzierunghbmsharepriceblock_2,rebuild_accessin den lokalen Settings ausschalten, Google Analytics prüfen (nicht konfiguriert), Sicherheitskopie00--backups--hci-update--8-10löschen. - D10-Merkpunkte: CKEditor 4 > 5 (
ckeditor_divfällt weg), Aggregator aus dem Core (News!), Color, Quick Edit, RDF aus dem Core, Themes Bartik, Seven (> Claro) und Stable aus dem Core, Theme-Basisstable, jQuery-UI-Module, PHP 8.3 für Martins Feed-Skript und hbmcustom,replacefür Toolbar bleibt, Superfish heben.
9.1 -- Upgrade Status: Übersicht
- Reports > Upgrade status: Core 9.5.11, PHP 8.1.34, Datenbank und Drush grün.
- Offen: veraltete Core-Erweiterungen (Aggregator, Color, Quick Edit, RDF, Bartik, Seven, Stable), Remove: 5, Update: 16, Fix manually: 1 (Theme hbm, 3 Probleme).
- Status Report bestätigt: aktiv ist «CKEditor 4 (contrib)» 1.0.2. CKEditor 5 ist in Core 9.5 schon drin, Umstellung daher noch auf D9.5.
- Rote Sicherheitsmeldung und «81 security vulnerability advisories» vorerst ignoriert: lokale Kopie, betrifft meist Core 9.5 und alte Module.
9.2 -- Remove: ungenutzte Module entfernt
composer remove drupal/devel drupal/jquery_ui_accordion drupal/magnific_popup drupal/views_slideshow- devel stand in require-dev, Composer fragt nach, mit Enter bestätigen.
- Dabei 6 Doctrine-Pakete mit entfernt (nur von devel gebraucht).
- pernet-Theme bewusst noch nicht entfernt: 3 Twig-Templates im Theme hbm verweisen noch darauf.
- Kein Dump nötig, Module waren deinstalliert.
9.3 -- Methode für Modul-Updates
- Die neuesten Versionen auf drupal.org brauchen oft schon D10.1+ oder D11. Gesucht ist jeweils eine Brückenversion für D9.5 und D10.
Composer nur die Hauptversionen vorgeben, er wählt selbst die höchste Version, die mit Core 9.5.11 läuft:
composer require "drupal/MODUL:^ALT || ^NEU" --update-with-all-dependencies drushci updatedb drushci cache:rebuild- Danach testen und committen. Der breite Constraint bleibt in composer.json und erlaubt beim Core-Wechsel den Sprung auf die D10-Version.
9.4 -- Modul-Updates
- admin_toolbar 2.5.0 > 3.6.3 (8 DB-Updates, doctrine/lexer 1 > 2). Test: Toolbar und Dropdowns.
- allowed_formats 1.5.0 > 2.0.0 (Brückenversion). Test: Textformat-Auswahl im Editor.
- better_exposed_filters 5.2.0 > 6.0.6 (Brückenversion). Test: Filter-Links auf Team-Seite (bef-links, Ajax) und Media Release.
- csv_serialization: bleibt 3.0.1, nur league/csv 9.8.0 > 9.28.0. Keine Brückenversion (3.x nur D9, 4.x nur D10), muss mit dem Core-Wechsel mit.
- editor_advanced_link 1.9.0 > 2.2.4 (Brückenversion). Test: Link-Dialog im Editor.
- flexslider 2.0.0 > 3.0.0. Test: Slider Startseite (automatisch und auf Klick).
9.5 -- Prüfen, ob ein Modul benutzt wird
drushci sql:query "SELECT name FROM config WHERE data LIKE '%MODULNAME%'"- Erwartet sind nur core.extension und die eigenen Einstellungen des Moduls. Taucht
views.view.…odercore.entity_view_display.…auf, wird es benutzt. Details:
drushci config:get CONFIGNAME | grep -i -B 3 MODULNAME- FlexSlider: benutzt in
views.view.view_hbm_news(Startseite).views.view.view_team_paragraph_sliderist vermutlich ein Überbleibsel (Team-Seite ist heute ein Raster). Achtung: Ein Modul kann auch versteckt benutzt werden, zum Beispiel als Effekt in einem Bildstil (Focal Point). Dann taucht
image.style.…auf. Ob der Bildstil selbst benutzt wird, zeigt diese Abfrage (s:4= Länge des Namens «team»):drushci sql:query "SELECT name FROM config WHERE data LIKE '%\"image_style\";s:4:\"team\"%'"- Die Kette dabei: Feld > Ausgabe «Bild» > Bildstil > Effekt im Bildstil. Im Formular und bei «Manage display» sieht man den Effekt nicht.
Wirksame Einstellungen inklusive Überschreibungen aus settings.php:
drushci config:get CONFIGNAME --include-overridden
9.6 -- Sichern
Nach jedem Modul:
git add composer.json composer.lock git commit --message "MODUL alt > neu" git push- Dumps:
--d9-10--9-5-module-teil1,--d9-10--9-5-module-teil2,--d9-10--9-5-module-teil3,--d9-10--9-5-theme-hbm,--d9-10--9-5-core-ext(aktueller Rückfallpunkt) - Nach reinen Deinstallationen (pm:uninstall, theme:uninstall) gibt es nichts zu committen, weil sich nur die Datenbank ändert. Dann nur Dump.
9.7 -- Mehrere Module in einem Durchgang
Harmlose Module lassen sich bündeln. Der Rückweg ist einfach: Dump zurückspielen und
composer.json/composer.lockaus Git holen.composer require "drupal/MODUL1:^1 || ^2" "drupal/MODUL2:^1 || ^2" --update-with-all-dependencies- In der Ausgabe auf «Upgrading» achten. Findet Composer keine passende Version, bleibt das Modul einfach auf der alten Version stehen.
- Einzeln machen, was Daten oder viel Config umbaut (simple_sitemap, viewsreference) oder im Theme verwendet wird (twig_tweak).
Nützt eine Änderung an composer.json nichts (Composer hat nichts geändert), zurücknehmen:
git checkout composer.json composer.lock git status- Gibt es von einem Modul nur Beta-Versionen, nimmt Composer sie nicht. Dann muss
@betain die Versionsangabe, z.B."drupal/viewsreference:^1 || ^2.0@beta".
9.8 -- Upgrade Status per Drush
Der Scan im Browser bricht bei grossen Themes/Modulen mit Fehler 500 ab (zu lange, zu viel Speicher). Per Drush gibt es diese Grenzen nicht:
drushci upgrade_status:analyze MODULNAME- Bei grep mit
--includedas Muster in Anführungszeichen setzen, sonst meldet zsh «no matches found»:--include="*.twig"
9.9 -- Theme für D10 anpassen
- info.yml:
core_version_requirement: ^9 || ^10, und die Zeilecore: 8.xmuss dann zwingend weg, sonst läuft die Site nicht mehr. jQuery-once fällt weg. In libraries.yml
core/jquery.oncedurchcore/onceersetzen, im JavaScript nach diesem Muster:vorher: $(context).find('.selektor').once('name').each(function() { nachher: $(once('name', '.selektor', context)).each(function() {In VS Code mit Suchen/Ersetzen, Regex eingeschaltet (Symbol
.*):Suchen: \$\(context\)\.find\(('[^']+')\)\.once\(('[^']+')\) Ersetzen: $$(once($2, $1, context))Danach mit
grep -n "\.once(" DATEI.jsnach Resten suchen und diese von Hand anpassen.- Testen mit der Chrome-Konsole: Cmd+Option+J, Seite neu laden. Rote Meldungen = JavaScript-Fehler.
Bilder nach einem Modul-Update neu rechnen lassen, damit der Test etwas aussagt:
drushci image:flush BILDSTIL
9.10 -- Besonderheiten Etappe 06
- Simple Sitemap im Backend erzeugen, nicht per Drush (Drush erzeugt
http://-Adressen, und die Tabellen-Darstellung fehlt):
Configuration > Search and metadata > Simple XML Sitemap > Sitemaps > Status > «Rebuild queue & generate» - Google Analytics war mit einer GA3-ID (
UA-…) konfiguriert, die seit Juli 2023 nichts mehr sammelt. Entfernt. - Claro als Admin-Theme: Vertikale Reiter mit verschachtelten Paragraphs werden abgeschnitten. Deshalb bleibt für D10 Seven (Contrib-Version beim Core-Wechsel).
✎ Claude – offen:
- Nächster Schritt: CKEditor 4 > 5, noch auf D9.5.
- Danach: Upgrade Status nochmals komplett per Drush scannen, Dump + Commit, dann Core-Wechsel. Mit dabei: csv_serialization 4.x, viewsreference 2.x Beta (Dump vorher, Daten werden umgewandelt), Contrib-Versionen von Aggregator, Stable und Seven.
- Nach dem Core-Wechsel heben: superfish, allowed_formats, better_exposed_filters, editor_advanced_link, twig_tweak.
9.11 -- CKEditor 4 > 5 (noch auf D9.5)
- D9.5 ist dafür gebaut: CKEditor 4 und 5 können gleichzeitig aktiv sein, jedes Textformat einzeln umstellen und testen.
- Vorgehen:
drushci en ckeditor5, dann pro Format unter Configuration > Content authoring > Text formats and editors den Editor auf «CKEditor 5» umstellen. Die grüne Meldung zeigt, was übernommen wurde. Zusätzliche Buttons (z.B. Kursiv, Code, Quelltext) nicht entfernen: Sie halten erlaubte Tags am Leben. - Formate ohne Filter «Limit allowed HTML tags» (meist Full HTML) lassen in CKEditor 5 beliebiges HTML durch. Divs, Klassen, Inline-Styles bleiben beim Speichern erhalten.
- Test komplexer Seiten: Quelltext vor dem Öffnen und nach dem Speichern kopieren und in VS Code diffen.
- Zusatzmodule ohne CKEditor-5-Version (z.B. ckeditor_div) deinstallieren. Neue Divs gehen über den Button «Quelltext».
- editor_file (PDF-Upload) kann CKEditor 5 erst ab 2.x, und 2.x läuft nur auf D10. Deshalb vorübergehend ohne Upload-Button, Version 2.x beim Core-Wechsel. Bestehende PDF-Links bleiben, die Dateinutzung zählt der Core.
- Danach CKEditor 4 deinstallieren:
drushci pmu ckeditor(nimmt editor_file 1.x mit). - Eigenheit CKEditor-5-Einstellungen: Plugin-Felder melden teilweise «field is required», obwohl ausgefüllt. Trotzdem speichern, es wird korrekt übernommen.
9.12 -- Core-Wechsel D9.5 > D10
- Vorher: Upgrade Status per Drush komplett scannen. Entscheidend sind nur Module, deren
core_version_requirementD10 nicht zulässt, der Rest ist meist Rauschen (Tests, nicht installierte Untermodule). composer.json von Hand anpassen (core, core-composer-scaffold, core-dev auf
^10, Drush^13, Module ohne Brückenversion auf die D10-Version), dann zuerst Probelauf:composer update --with-all-dependencies --dry-run- Drush 13 braucht PHP 8.3. Composer im Terminal mit 8.3 laufen lassen, MAMP erst nach dem Composer-Durchgang auf 8.3 stellen, dann
updb. - Composer-Ausgaben nie in eine Datei umleiten: Fragt Composer etwas (z.B. allow-plugins), sieht man es nicht und es hängt. Für D10 in
allow-plugins:"php-http/discovery": true,"tbachert/spi": false. - D10 verlangt
core_version_requirementin jedem info.yml, auch bei deinstallierten Themes und Modulen. Alte Ordner vorher löschen. Twig 3.30 ist mit Drupal (Stand Okt. 2026) nicht kompatibel: Fehler
EscaperRuntime::escape(): Argument #4 ($autoescape) must be of type bool, null given. Lösung:composer require "twig/twig:<3.30" --update-with-all-dependenciesSperre wieder entfernen, sobald Drupal den Fix ausgeliefert hat.
- Twig-Fehler nach Updates: Ordner
web/sites/default/files/phplöschen, danndrushci cr. - Scaffold überschreibt
.htaccessundrobots.txt. Mitgit diff web/.htaccessprüfen, ob eigene Regeln verloren gingen. - Alte composer.json-Vorlagen haben oft
"replace": {"drupal/toolbar": "*"}. Ab Core 10.6.15 blockiert das Core-Updates (beide ersetzen dasselbe Paket). Block löschen. Wenn Composer eine Version nicht nimmt, zeigt das den Grund (why-not reicht nicht immer):
composer require "paket:version" --update-with-all-dependencies --dry-run
9.13 -- Nach dem Core-Wechsel
- Statusbericht (Reports > Status report) und verfügbare Updates (Reports > Available updates) durchgehen. Hauptversionssprünge (z.B. Metatag 1 > 2, ctools 3 > 4) hebt Composer nicht von selbst, die müssen per
composer require "drupal/modul:^2"geholt werden. - Veraltete Module deinstallieren: Tour, Admin Toolbar Links Access Filter (im Core seit 10.3), Transliterate Filenames (im Core seit 10.2).
- Transliteration im Core einschalten, sonst gehen Umlaute und Leerzeichen in Dateinamen wieder durch:
Configuration > Media > File system > Sanitize filenames, alle 5 Häkchen, Replacement character «Dash». - Wichtig: D10 gibt Datumsfelder als
<time datetime="...">…</time>aus. Wo eine View ein Datum per Custom text, Gruppierung oder JavaScript weiterverarbeitet, beim Feld unter Rewrite results «Strip HTML tags» und «Remove whitespace» setzen. Symptome: Code-Reste im Text, Gruppierung pro Eintrag statt pro Jahr, JS-Fehler mit<time datetime=. - Fehlersuche: Chrome-Konsole zuerst. Die Meldung zeigt oft direkt, welcher Wert falsch ankommt.
- Drush 13: Kurzformen funktionieren wieder (
cr,updb,en,pmu,st). - D10 hat Sicherheits-Support bis 9. Dezember 2026.
9.14 -- Besonderheiten Etappe 07
- Superfish 1.4 > 1.17 ging problemlos (Theme nutzt nur sf-menu, sf-depth, sf-accordion). Neue Bibliothek unter
libraries/drupal-superfish. CanvasJS 3.17.7: Donut-Kreise waren unterschiedlich gross (Grösse hing von den Beschriftungen ab). Lösung in main.js:
radius: chart_wrapper.width() < 450 ? "60%" : "100%", indexLabelFontSize: chart_wrapper.width() < 450 ? 12 : 14,
✎ Claude – offen:
- Offene Tests: Medienmitteilung Backup erfassen, Sitemap im Backend (230 URLs, https://), News per Cron, Team- und Portfolio-Import, Feed-Skript im Webroot und hbmcustom unter PHP 8.3.
- Twig-Sperre prüfen und entfernen, sobald Drupal den Fix hat.
- Danach Aufräumen (Liste im Übergabeprotokoll 08).
9.15 -- Tests nach D10
- Feeds-Import testen: In der Quelle (hbmglobal) eine unauffällige Änderung machen (zweiter Punkt, Doppelpunkt hinter Überschrift), lokal Configuration > System > Cron > Run cron. Erwartet: «Updated 1 Team». Danach in der Quelle über Revisions zurücksetzen und nochmals Cron.
- Log nach Problemen filtern: Reports > Recent log messages, Severity «Error» bzw. «Warning» wählen und Filter. Der Button «Reset» zeigt, dass ein Filter aktiv ist.
- Martins Feed-Skript (web/hbmcustomFeed.php) läuft unter PHP 8.3. Lokal meldet es «SSL certificate verify failed», weil es sich selbst über https aufruft und MAMP ein selbst ausgestelltes Zertifikat hat. Auf Servern mit echtem Zertifikat tritt das nicht auf.
- Backup-Medienmitteilungen prüfen ohne Feed: /json-medienmitteilung-backup/de bzw. /en aufrufen.
[]= keine veröffentlichten Einträge. Datum muss als reines ISO-Datum kommen (z.B.2023-01-17T00:00:00Z), ohne<time>. Twig-Sperre entfernen, sobald der Core den Fix hat (hier ab 10.6.18):
composer remove twig/twig«Removal failed, twig/twig is still present» ist normal: Die Sperrzeile ist weg, Twig selbst bleibt, weil der Core es braucht.
- VS-Code-Diff: links alt, rechts neu. Ohne Farben lesbar: «–» und Schraffur rechts = gelöscht, «+» und Schraffur links = neu.
9.16 -- Aufräumen
Vor dem Entfernen eines deinstallierten Moduls prüfen, ob noch Verweise bestehen (keine Ausgabe = sauber):
drushci sql:query "SELECT name FROM config WHERE data LIKE '%block_class%'"- Alles unter web/ ist öffentlich abrufbar, auch .md- und .html-Notizen im Theme. Interne Notizen gehören nach
--pernet-docsim Projektroot (in .gitignore:/--pernet-docs/). Variablen aus hbm.theme werden nur in Twig-Templates verwendet. Prüfen in Theme und Custom-Modulen:
grep -rn "NAME" web/themes/custom/hbm --include="*.twig" grep -rn "NAME" web/modules/custom --include="*.twig"node_modules/ohne führenden Schrägstrich in .gitignore gilt in jeder Tiefe. Gulp-Ordner selbst bleibt im Git (Build-Einstellungen), nur die Pakete sind ausgeschlossen.
9.17 -- Datenbank-Voraussetzungen D10 / D11
- DB-Version des Servers: Reports > Status report (Zeile Datenbank). «5.5.5-10.3.39-MariaDB» heisst MariaDB 10.3.39.
- D10 läuft mit MariaDB 10.3. D11 verlangt mindestens MariaDB 10.6 (harte Grenze, Installation und Update brechen sonst ab). Beim Hoster eine Langzeit-Version verlangen (10.11 oder 11.4).
D8 auf MariaDB 10.6: Risiken sind das reservierte Wort OFFSET (Spaltennamen) und COMPRESSED-Tabellen (ab 10.6.6 behoben). Prüfen mit Gegenprobe:
SELECT DATABASE(), TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND COLUMN_NAME = 'nid'; SELECT TABLE_NAME, COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND COLUMN_NAME = 'offset';Erste Abfrage muss Zeilen liefern (richtige DB), zweite leer bleiben.
10 -- Settings nach per.net-Konzept (Multisite entfernt)
- Vorlage: Starter. Für hci nur local, stage, live (kein dev). Pfade mit
dirname($app_root), weil Composer-Root = Projektordner (im Starter zwei Ebenen, hier eine). - Alte Werte vorher sichern: Kopie der alten
sites/default/settings.phpausserhalb von web/ ablegen (enthält DB-Passwort). - In web/sites alles löschen, was nicht Original ist: Multisite-Ordner (local.hci-update.test, dev-…, test.hbmhealthcare.com), simpletest, sites.php. Die Originale legt Composer selbst ab (
composer drupal:scaffold),default/filesbleibt. - In web/sites/default: neue settings.php, settings.local.php (Hash, DB-Zugang aus alter Datei), settings.development.php. In web/sites: development.pernet.services.yml. Ordner
private/tmpanlegen. - settings.stage.php und settings.live.php nie in web/sites/default lokal ablegen (Umgebungsweiche bricht ab). Sie liegen in
private/deployment-settings. - Test:
drushci status> Site path sites/default, Files Private/Temp in private/. Seitenname im Browser-Tab zeigt «(Local)» (kommt aus settings.local.php, wird nicht exportiert). .gitignore:
# Ignore sensitive information /web/sites/*/settings.local.php /web/sites/*/settings.stage.php /web/sites/*/settings.live.php # Interne per.net-Dateien (nur lokal, nicht versioniert) /--pernet-docs/- Alter DB-Treiber
Drupal\Core\Database\Driver\mysqlläuft unter D11 nicht mehr. Neu:Drupal\mysql\Driver\Database\mysqlplusautoload-Zeile. - Vor dem ersten Deployment:
drushci cex, nur config/sync darf sich ändern (Kontrolle:git status --short | grep -v "config/sync"ohne Ausgabe). Dev-Module (Upgrade Status) vorher deinstallieren.
11 -- Stage einrichten (Plesk-Server hbm01)
- Plesk: Subdomain stage, Dokumentenstamm
stage.hbmhealthcare.com/web. PHP 8.3, upload/post 64M, max_execution_time 120. Let's Encrypt. Apache & nginx gleich wie live (Proxymodus an). Terminal-PHP ist ein eigener Wert (hier 7.4). In
~/.bash_profile(wird beim Login geladen, .bashrc gibt es hier nicht):usephp83() { export PATH="/opt/plesk/php/8.3/bin:$PATH"; php -v; } PS1='\n\[\e[31m\][MVA] \u@\h \w\[\e[0m\]\n\$ ' alias dr='./vendor/bin/drush'Gilt pro Terminal-Fenster. Ohne 8.3 bricht
composer installab, Drush 13 läuft nicht.Deploy Key (Server liest bei GitLab, nur dieses Projekt, nur lesen), in ~/.ssh:
ssh-keygen -t ed25519 -f id_ed25519_gitlab_hbm -C "HBM Healthcare - GitLab Deploy"Passphrase leer.
~/.ssh/config:Host gitlab.com HostName gitlab.com User git IdentityFile ~/.ssh/id_ed25519_gitlab_hbm IdentitiesOnly yeschmod 600 config. Öffentlichen Schlüssel in GitLab: Projekt > Settings > Repository > Deploy keys (ohne write). Test:ssh -T git@gitlab.com, Frage mityesausgeschrieben beantworten (Enter = nein, «Host key verification failed»).Clone braucht leeren Ordner. Plesk-Platzhalter index.html löschen, web/ (mit .php-ini, .php-version) zur Seite stellen, clonen, die beiden Dateien zurück nach web/:
cd ~ mv stage.hbmhealthcare.com/web stage-web-tmp cd stage.hbmhealthcare.com git clone git@gitlab.com:per-net/web/hci-update.git . mv ../stage-web-tmp/.php-ini ../stage-web-tmp/.php-version web/ rmdir ../stage-web-tmpusephp83, danncomposer install --no-dev. Meldung «path-util is abandoned» harmlos. «Create a sites/default/files directory with chmod 0777» legt nur den leeren Ordner an (Rechte später korrigieren).- settings.stage.php nach web/sites/default hochladen (DB-Host 127.0.0.1, Plesk zeigt localhost:3306, beides geht). hash_salt wie live.
Dateien direkt auf dem Server kopieren (Transmit lädt auch bei Kopien auf demselben Server über den Mac, 3 GB dauert ewig und blieb hängen).
cpverändert die Quelle nie:cp -a ../httpdocs/private . mkdir private/tmp cp -a ../httpdocs/web/sites/default/files/. web/sites/default/files/Mit Fortschrittsanzeige:
rsync -a --info=progress2 QUELLE/ ZIEL/(zweiter Lauf kopiert nur Änderungen).
Danach in web/sites/default/files (pwd prüfen!) Caches löschen:rm -rf php css js tmp.private/tmpnicht vergessen (private/ ist nicht im Git, .gitkeep hilft nicht). Fehlt er: «Temporary file could not be created», z.B. beim Laden von Übersetzungen.Collation: Lokal MySQL 8 legte bei den Updates neue Tabellen mit
utf8mb4_0900_ai_cian, MariaDB kennt das nicht (ERROR 1273). Die Collation der neuen DB hilft nicht, der Dump bringt sie pro Tabelle mit. Lösung an der Quelle, lokal pro betroffene Tabelle:ALTER TABLE tabellenname CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;Betroffene Tabellen finden: in Sequel Ace / phpMyAdmin nach Kollation sortieren.
- DB-Import in Plesk: Datenbanken > DB > Dump importieren, Häkchen «Datenbank neu erstellen» (leert vorher, Benutzer bleibt).
- Prüfen:
dr status(Database: Connected, sonst Zugangsdaten in settings.stage.php), danndr updb,dr cr. - Bibliotheken: web/libraries ist nicht im Git. Was Composer nicht holt (hier flexslider), fehlt auf dem Server. Symptom: «flexslider is not a function» in der Konsole. Vergleich
ls web/librarieslokal und Server, fehlendes kopieren. - Shield nachrüsten: lokal
composer require drupal/shield,drushci en shield,drushci cex, Commit/Push. Stage:git pull,composer install --no-dev,dr cim(nur core.extension und shield.settings bestätigen),dr cr.
12 -- Inhalte von live nachtragen
- Live-DB (D8) lässt sich nicht auf D10-Code hochziehen. Darum: Änderungen seit der Kopie von Hand übertragen. News, Team, Portfolio kommen per Import automatisch.
- Finden: auf live Content, Filter pro Inhaltstyp (ohne Team/Portfolio), nach «Updated» sortieren. Was geändert wurde: Revisions > zwei Versionen > Compare selected revisions (Diff).
- Als Kunden-Benutzer erfassen: Masquerade (Block im Footer des Frontend-Themes, im Backend nicht sichtbar) oder beim Speichern «Authored by» setzen.
- Nach D10-Go-live gilt: Inhalte von live nach unten holen, Code und Konfiguration über Git und cim nach oben.
✎ Claude – offen:
- Stage: Rechte setzen, Status report, Cron einrichten, Backup-Medienmitteilung bis in die News-Liste testen, Seiten durchklicken.
- Flexslider-Bibliothek dauerhaft lösen (Composer oder Git).
- Go-live-Ablauf festlegen (Checkliste im Übergabeprotokoll 09), Kunde informieren (Shield-Zugang, Doppel-Erfassung).
- Danach D10 > D11 (Hoster: MariaDB ≥ 10.6), Altlasten auf dem Server entsorgen.
13 -- Stage testen: open_basedir und private Pfade
- Symptom: Cron meldet «Cron-Lauf erfolgreich», im Protokoll aber
RuntimeException: Feeds directory "private://feeds/in_progress/1" either cannot be created or is not writable. Kein Zeit- oder Speicherproblem, die Meldung sagt genau, was fehlt. - Ursache: Bei absolutem privatem Pfad (
/var/www/…) prüft Drupal beim Anlegen mehrerer Ordner jede Stufe ab «/». open_basedir (Plesk-Standard{WEBSPACEROOT}{/}{:}{TMP}{/}) erlaubt nur den Webspace, darum scheitert schon/var. Der öffentliche Pfad ist relativ (sites/default/files) und daher nicht betroffen. Lokal (MAMP) gibt es kein open_basedir. - Ordner von Hand anlegen hilft nicht: Feeds legt
in_progress/<Feed-ID>bei jedem Import neu an und löscht ihn danach. - Test: open_basedir auf
none> Import läuft. Damit ist die Ursache bewiesen, aber der Schutz ist weg. Lösung mit Schutz: Pfade in settings.php relativ zu web/ angeben, open_basedir bleibt auf Standard:
// Relative to web/ so that open_basedir (Plesk) does not block directory creation. $settings['file_private_path'] = '../private/files'; $settings['file_temp_path'] = '../private/tmp';Kontrolle:
dr statuszeigt «Files, Private: ../private/files» und «Files, Temp: ../private/tmp».- Testen ohne neue Daten: Inhalt > Feeds > Feed > «Importieren» (nicht «Import in background»). Die Zwischendatei wird bei jedem Import angelegt, auch wenn danach «keine neuen Einträge» kommt. Temp und private zusammen testet ein Backup and Migrate-Backup ins «Private Files Directory».
- Gesperrter Feed: keine Statusspalte. Erkennbar nur am Eintrag «Entsperren» im Aufklappmenü neben «Bearbeiten». Nach einem abgebrochenen Import entsperren.
14 -- Git pull mit Schreibschutz, Shell-Prompt
Drupal schützt sites/default (555) und settings.php (444). Ändert ein Pull dort etwas, kommt
error: unable to unlink old 'web/sites/default/settings.php': Permission denied. Ablauf:chmod u+w web/sites/default web/sites/default/settings.php git pull chmod 555 web/sites/default chmod 444 web/sites/default/settings.php git statusDrush färbt seine Meldungen selbst ein («success» grün), Git nicht. Fehlschläge im Prompt sichtbar machen (~/.bash_profile, ersetzt die PS1-Zeilen, schwarz auf gelb statt rot/grün):
__set_prompt() { local rc=$? local mark='' if [ $rc -ne 0 ]; then mark="\[\e[1;30;43m\] FEHLER $rc \[\e[0m\] " fi PS1="\n${mark}"'\[\e[31m\][MVA] \u@\h \w\[\e[0m\]\n\$ ' } PROMPT_COMMAND=__set_promptDanach
source ~/.bash_profile, Test mitls gibtsnicht.- web/.php-ini und web/.php-version schreibt Plesk pro Domain. Sie sind im Git und erscheinen auf dem Server als «modified». Harmlos, aber noch aus dem Git nehmen (siehe offen).
15 -- Feeds und Übersetzung (Team)
- Der Import kommt pro Sprache als eigener Eintrag: erste Sprache «Erstellt», zweite «Aktualisiert» (Übersetzung derselben Person).
- Fehler «Non-translatable fields can only be changed when updating the original language»: Einstellung «Hide non translatable fields on translation forms» beim Inhaltstyp Team ausschalten (Konfiguration > Regionale Einstellungen und Sprache > Inhaltssprache und Übersetzung > Team). Sonst kommt nur die erste Sprache an.
16 -- Go-live D10 (19.10.2026)
- Ablauf: D8-Cron in Plesk deaktivieren (Aufgabe anklicken > Haken «Aktiv» weg), Backup-Ordner anlegen, Dumps sichern, Stage-Ordner kopieren (
cp -a stage.hbmhealthcare.com httpdocs-neu), settings.stage.php löschen, settings.live.php hochladen, Rechte schliessen,git status, Stage-Dump in neue Live-DB, umschalten, PHP 8.3 in Plesk,dr status,dr updb,dr cr. - Backup-Schema:
_backups_website/drupal/2026/, rückwärts nummeriert (95---19.10.2026--…), darinDB/und die Dateikopie. Alter Live-Ordner nicht löschen, sondern ins Backup schieben: das ist der Rückweg. - Stolperstein 403 Forbidden: httpdocs braucht Gruppe
psaservund 750 (drwxr-x---). Eine Kopie bekommtpsacln, undchgrp psaservist als Benutzer nicht erlaubt. Notbehelfchmod 755 httpdocs. Saubere Lösung: Plesk-Ordner nie ersetzen, nur Inhalte tauschen. Alten Ordner leeren, leer zurückholen, neuen Inhalt hineinschieben:
mv -- * .[!.]* ../ziel/.[!.]*nimmt die versteckten Dateien mit.--ist nötig, weil Namen wie--gulp-2.1-susy2sonst als Option gelesen werden. Kontrolle:ls -ld httpdocs.- Vor jedem
mvins Backup prüfen, ob der Zielordner genau so heisst und noch keinen gleichnamigen Unterordner hat (sonst landet alles eine Ebene tiefer). - Cron neu: Plesk > Geplante Aufgaben > Aufgabe hinzufügen > «URL abrufen», URL aus Konfiguration > System > Cron,
*/2 * * * *. Der Cron-Schlüssel kam mit der DB und ist derselbe wie unter D8. Der alte find-Job fürfeeds_http_fetcherist unter D10 überflüssig. - Sitemap: «Rebuild queue» stellt nur in die Warteschlange, abgearbeitet per Cron oder «Resume generation». Danach sitemap.xml prüfen: Links mit https://www, keine Stage-Adressen.
- Änderung erst nach Shift-Reload sichtbar: Browser-Cache, kein Drupal-Problem (Konfiguration > Entwicklung > Leistung > Browser- und Proxy-Cache maximales Alter). Windows: Ctrl+F5 oder Ctrl+Shift+R, F5 allein genügt nicht.
- Ergebnis: Statusbericht live mit 1 Warnung (D10-Support bis 9.12.2026), sonst grün. Seit einer Woche ohne Probleme. Hoster hat MariaDB auf 11.8 aktualisiert.
✎ Claude – offen:
- 95er-Backup zippen, alte D8-Cronjobs löschen, Altlasten entsorgen (test., dev., privat-hbm).
- web/.php-ini und web/.php-version aus dem Git nehmen (
git rm --cached, .gitignore). Vorsicht: Der nächste Pull löscht sie auf dem Server. - Flexslider-Bibliothek dauerhaft lösen, Terminal-Standard auf PHP 8.3.
- D10 > D11 als eigener Eintrag (Übergabeprotokoll 10). Erster Schritt: frischer Live-Dump nach lokal und stage.