WordPress-Themes wie Software behandeln: Deployment mit GitHub Actions

Ein selbst entwickeltes WordPress-Theme ist Software.
Es enthält PHP, JavaScript, CSS, Templates, Assets und Konfiguration. Änderungen an einer Datei können Auswirkungen auf andere Teile des Systems haben. Fehler können die komplette Website unbrauchbar machen.
Trotzdem werden WordPress-Themes erstaunlich oft anders behandelt als andere Software:
Theme ändern
↓
ZIP erstellen
↓
WordPress öffnen
↓
Theme hochladen
↓
hoffen
Oder noch direkter:
FTP
↓
Datei überschreiben
↓
fertig
Für kleine Änderungen funktioniert das erstaunlich lange.
Bis es nicht mehr funktioniert.
Dann entstehen unangenehme Fragen:
- Welcher Stand läuft gerade auf dem Server?
- Entspricht dieser Stand überhaupt noch dem lokalen Projekt?
- Welche Dateien wurden beim letzten Update verändert?
- Liegen alte Dateien auf dem Server, die lokal längst gelöscht wurden?
- Welcher Commit entspricht der Produktion?
- Kann der vorherige Zustand wiederhergestellt werden?
- Wurde das Theme vor dem Deployment überhaupt geprüft?
- Funktioniert die Website nach dem Deployment tatsächlich?
Die Lösung ist nicht ein besserer FTP-Client.
Die Lösung besteht darin, das Theme wie das zu behandeln, was es ist:
ein deploybares Software-Artefakt.
Das eigentliche Problem: Wo liegt die Wahrheit?
Bei einem manuell gepflegten WordPress-Theme entstehen schnell mehrere Zustände:
Lokaler Rechner
│
├── Version A
│
GitHub
├── Version B
│
Webserver
└── Version C
Solange alle drei ungefähr gleich sind, fällt das kaum auf.
Nach einigen manuellen Änderungen sieht die Realität möglicherweise so aus:
Git Repository
├── functions.php
├── style.css
├── assets/
│ ├── site.css
│ └── browser.js
└── templates/
Produktionsserver
├── functions.php
├── style.css
├── assets/
│ ├── site.css
│ ├── browser.js
│ ├── category-sort.js
│ └── old-navigation.js
└── templates/
Die letzten beiden JavaScript-Dateien wurden im Repository längst entfernt.
Auf dem Server existieren sie aber weiterhin.
Ein klassischer Datei-Upload kennt keinen gewünschten Gesamtzustand. Er kopiert Dateien.
Ein Deployment sollte etwas anderes tun:
Der Zustand im Repository definiert den Zustand des deployten Themes.
Damit wird Git zum Source of Truth.
Git Repository
│
│ Source of Truth
▼
Validation
│
▼
Deployment
│
▼
WordPress Production
Produktion wird aus Git hergestellt.
Nicht umgekehrt.
Von Dateiübertragung zu CI/CD
An dieser Stelle lohnt sich die Trennung zweier Begriffe, die häufig zusammengeworfen werden:
- Continuous Integration
- Continuous Delivery beziehungsweise Continuous Deployment
Für ein WordPress-Theme lässt sich die Trennung sehr konkret zeigen.
Continuous Integration
CI beantwortet die Frage:
Ist dieser Stand grundsätzlich deploybar?
Zum Beispiel:
Commit
│
▼
PHP Syntax Check
│
▼
JavaScript Syntax Check
│
▼
Theme Structure Check
│
▼
weitere Tests
Erst wenn diese Prüfungen erfolgreich sind, darf der nächste Schritt beginnen.
Continuous Deployment
CD beantwortet anschließend:
Wie kommt der geprüfte Stand kontrolliert auf das Produktionssystem?
Validierter Commit
│
▼
SSH-Verbindung
│
▼
Backup
│
▼
Deployment
│
▼
Smoke Tests
GitHub Actions kann beide Aufgaben übernehmen.
Die einfachste mögliche GitHub Action
Eine sehr kleine Deployment-Pipeline könnte so aussehen:
name: Deploy WordPress Theme
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy
run: |
rsync -az --delete \
./ \
user@server:/path/to/wp-content/themes/my-theme/
Das Prinzip ist bereits sichtbar:
Push auf main
↓
GitHub Actions
↓
Repository auschecken
↓
rsync
↓
Produktionsserver
Aber produktionsreif ist das noch nicht.
Vor allem fehlen:
- Validierung
- sichere Credentials
- Schutz des Deployment-Ziels
- Vorschau der Änderungen
- Backup
- Verifikation nach dem Deployment
- brauchbares Feedback
Diese Punkte machen aus einem automatisierten Datei-Upload eine Deployment-Pipeline.
SSH statt Zugangsdaten im Workflow
GitHub Actions muss sich am Webserver authentifizieren können.
Dafür eignet sich ein separater SSH-Key.
Ein Ed25519-Key kann beispielsweise erzeugt werden mit:
ssh-keygen \
-t ed25519 \
-C "github-actions-wordpress" \
-f github-actions-wordpress
Es entstehen zwei Dateien:
github-actions-wordpress
github-actions-wordpress.pub
Die .pub-Datei enthält den öffentlichen Schlüssel.
Dieser wird auf dem Server autorisiert.
Der private Schlüssel gehört dagegen weder in das Theme noch in das Repository.
GitHub Secrets
GitHub Actions stellt dafür Repository Secrets bereit.
Für ein einfaches WordPress-Deployment können beispielsweise folgende Werte benötigt werden:
SSH_HOST
SSH_USER
SSH_PRIVATE_KEY
THEME_PATH
Der Zielpfad könnte konzeptionell so aussehen:
/home/user/public_html/wp-content/themes/my-theme
Der Workflow kann anschließend darauf zugreifen:
${{ secrets.SSH_HOST }}
${{ secrets.SSH_USER }}
${{ secrets.SSH_PRIVATE_KEY }}
${{ secrets.THEME_PATH }}
Der private Schlüssel taucht damit nicht im Repository auf.
Das ist ein grundlegendes Prinzip von Deployment-Konfiguration:
Code gehört ins Repository. Secrets nicht.
SSH vorbereiten
Der Runner von GitHub Actions existiert nur für die Dauer des Jobs.
Die SSH-Konfiguration muss deshalb während des Workflows erstellt werden.
Beispielsweise:
- name: Configure SSH
shell: bash
run: |
mkdir -p ~/.ssh
chmod 700 ~/.ssh
printf '%s\n' \
"${{ secrets.SSH_PRIVATE_KEY }}" \
> ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan \
-H "${{ secrets.SSH_HOST }}" \
>> ~/.ssh/known_hosts
Danach kann der Workflow den Server erreichen.
Ein separater Verbindungstest ist sinnvoll:
- name: Test SSH connection
run: |
ssh user@server "
echo 'SSH connection established'
hostname
whoami
"
Das wirkt zunächst redundant.
Für die Fehlersuche ist die Trennung aber wertvoll.
Scheitert später rsync, ist bereits bekannt, ob grundsätzlich ein SSH-Problem vorliegt oder tatsächlich das Deployment fehlschlägt.
Vor dem Deployment muss das Theme geprüft werden
Automatisierung sollte Fehler nicht nur schneller ausliefern.
Sie sollte bekannte Fehlerklassen verhindern.
Bei einem klassischen WordPress-Theme können bereits sehr einfache Prüfungen viel abfangen.
PHP-Syntax prüfen
PHP besitzt dafür bereits einen eingebauten Syntaxcheck:
php -l functions.php
Für alle PHP-Dateien:
find . \
-type f \
-name "*.php" \
-print0 \
| xargs -0 -n1 php -l
Ein Syntaxfehler führt zum Abbruch der Pipeline.
Damit kann beispielsweise ein versehentlich fehlendes Semikolon nicht mehr einfach auf Produktion landen.
JavaScript prüfen
Node kann JavaScript-Dateien zumindest syntaktisch prüfen:
node --check assets/js/browser.js
Automatisiert:
for file in $(find assets -type f -name "*.js"); do
node --check "$file"
done
Das ersetzt keine Tests und keinen Linter.
Es verhindert aber bereits triviale Syntaxfehler.
WordPress-Struktur prüfen
Auch die erwartete Theme-Struktur lässt sich kontrollieren.
Zum Beispiel:
REQUIRED_FILES=(
"style.css"
"functions.php"
"index.php"
"header.php"
"footer.php"
"404.php"
)
for FILE in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$FILE" ]; then
echo "Missing: $FILE"
exit 1
fi
done
Damit lässt sich eine weitere Klasse unangenehmer Fehler verhindern:
Eine zentrale Datei wurde beim Refactoring versehentlich gelöscht oder nicht committed.
Man kann noch weitergehen und beispielsweise leere PHP-Templates erkennen:
if [ ! -s "$FILE" ]; then
echo "Template is empty: $FILE"
exit 1
fi
Das ist simpel.
Aber genau solche simplen Guardrails sind wertvoll.
Poka-Yoke in der Deployment-Pipeline
Das Prinzip dahinter ist dasselbe wie bei Poka-Yoke in Softwarearchitektur und Entwicklungsprozessen:
Einen vermeidbaren Fehler nicht dokumentieren, sondern technisch erschweren oder unmöglich machen.
Eine README könnte sagen:
Vor dem Deployment bitte kontrollieren, ob
index.phpvorhanden ist.
Eine Pipeline kann stattdessen sagen:
index.php fehlt
↓
Pipeline rot
↓
kein Deployment
Die zweite Variante ist zuverlässiger.
Warum rsync?
Für ein klassisches WordPress-Theme ist rsync ein sehr passendes Werkzeug.
Es synchronisiert Verzeichnisse effizient und überträgt nur notwendige Änderungen.
Ein typischer Aufruf:
rsync -az \
./ \
user@server:/path/to/theme/
Interessant wird insbesondere die Option:
--delete
Damit werden Dateien auf dem Ziel entfernt, die im Quellverzeichnis nicht mehr existieren.
Warum --delete wichtig ist
Nehmen wir an, ein Theme enthielt früher:
assets/js/
├── browser.js
├── category-sort.js
└── old-navigation.js
Nach einem Refactoring existiert im Repository nur noch:
assets/js/
└── browser.js
Ein normaler Upload überschreibt möglicherweise browser.js.
Die anderen Dateien bleiben aber auf dem Server.
Mit:
rsync --delete
wird das Ziel tatsächlich synchronisiert.
Danach gilt:
Repository
=
Produktionsverzeichnis
Das ist genau das gewünschte Verhalten.
Warum --delete gleichzeitig gefährlich ist
Angenommen, der konfigurierte Zielpfad lautet versehentlich:
/wp-content/
statt:
/wp-content/themes/my-theme/
Dann bekommt der Begriff --delete plötzlich eine völlig andere Bedeutung.
Deshalb sollte eine produktive Pipeline das Deployment-Ziel überprüfen.
Beispielsweise:
TARGET="${THEME_PATH}"
EXPECTED_SUFFIX="/wp-content/themes/my-theme"
if [[ "$TARGET" != *"$EXPECTED_SUFFIX" ]]; then
echo "Unsafe deployment target"
exit 1
fi
Erst danach darf rsync --delete ausgeführt werden.
Das ist wieder Poka-Yoke:
Konfigurationsfehler
↓
Guard erkennt falschen Pfad
↓
Deployment wird gestoppt
statt:
Konfigurationsfehler
↓
rsync --delete
↓
Problem
Erst ansehen, dann verändern: rsync --dry-run
Ein weiterer sinnvoller Guardrail ist ein Dry Run.
rsync kann berechnen, was passieren würde, ohne tatsächlich etwas zu verändern:
rsync -azn \
--delete \
--itemize-changes \
./ \
user@server:/path/to/theme/
Das n steht für Dry Run.
Mit:
--itemize-changes
wird zusätzlich sichtbar, welche Änderungen geplant sind.
Beispielsweise:
>f.st...... assets/css/site.css
>f.st...... functions.php
>f+++++++++ inc/helpers.php
*deleting assets/js/category-sort.js
Damit besitzt die Pipeline vor dem eigentlichen Deployment bereits eine Art Änderungsplan.
Das ist insbesondere nach größeren Refactorings hilfreich.
Backup vor Deployment
Validierung reduziert das Risiko.
Sie beseitigt es nicht.
Deshalb sollte unmittelbar vor dem Deployment der aktuelle Produktionsstand gesichert werden.
Ein Backupname kann beispielsweise enthalten:
my-theme-20260913-112500-a91e42f
Darin stecken:
Theme
Timestamp
Commit
Konzeptionell:
Aktuelles Production Theme
│
├──────────────► Backup
│
▼
neues Deployment
Ein einfacher serverseitiger Backup-Schritt könnte so aussehen:
TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S")
cp -a \
/path/to/theme \
/path/to/backups/theme-${TIMESTAMP}
Damit hängt die Wiederherstellung nicht davon ab, ob irgendwo noch eine passende ZIP-Datei existiert.
Backups brauchen eine Retention Policy
Automatische Backups erzeugen ein neues Problem:
Sie funktionieren.
Bei jedem Deployment entsteht ein weiteres Verzeichnis.
Nach genügend Deployments sieht der Server so aus:
backups/
├── theme-001
├── theme-002
├── theme-003
├── ...
├── theme-184
└── theme-185
Deshalb gehört zur Backup-Strategie auch eine Retention Policy.
Beispielsweise:
Die letzten zehn Deployments werden behalten.
Ältere Backups werden automatisch entfernt.
Das Prinzip ist allgemeiner als WordPress:
Ein Backup-Konzept definiert nicht nur, wie Sicherungen entstehen, sondern auch, wie lange sie existieren.
Das eigentliche Deployment
Nach Validierung, Zielprüfung, Dry Run und Backup kann synchronisiert werden:
rsync -az \
--delete \
--itemize-changes \
--stats \
--human-readable \
--exclude=".git/" \
--exclude=".github/" \
--exclude=".gitignore" \
--exclude="README.md" \
./ \
user@server:/path/to/theme/
Mehrere Optionen verbessern dabei nicht das Deployment selbst, sondern dessen Beobachtbarkeit.
--itemize-changes zeigt die einzelnen Änderungen.
--stats liefert Statistiken.
--human-readable macht Größen verständlicher.
Dadurch ist im GitHub-Actions-Log nicht nur sichtbar:
Process exited with code 0
sondern auch, was tatsächlich verändert wurde.
Ein grünes rsync bedeutet nicht, dass die Website funktioniert
Das ist einer der wichtigsten Punkte der gesamten Pipeline.
Angenommen:
rsync
↓
Exit Code 0
Dann wissen wir:
Die Dateien wurden erfolgreich synchronisiert.
Wir wissen nicht:
WordPress funktioniert.
Ein PHP-Fehler könnte erst beim Ausführen eines bestimmten Templates auftreten.
Eine Funktion könnte zwar syntaktisch korrekt sein, aber zur Laufzeit scheitern.
Ein Template könnte falsche Daten erwarten.
Eine Seite könnte nach dem Deployment leer sein.
Deshalb endet das Deployment nicht mit rsync.
Smoke Tests gegen Produktion
Nach dem Deployment kann GitHub Actions die echte Website aufrufen.
Zum Beispiel:
curl \
--silent \
--show-error \
--location \
--output /tmp/homepage.html \
--write-out "%{http_code}" \
"https://example.com/"
Dann wird der HTTP-Status geprüft:
if [ "$HTTP_CODE" != "200" ]; then
exit 1
fi
Dasselbe kann für zentrale Seiten geschehen:
Homepage
Articles
wichtige Landingpage
Damit prüft die Pipeline erstmals nicht mehr nur Dateien.
Sie prüft das laufende System.
HTTP 200 reicht ebenfalls nicht
Auch das ist nur eine erste Näherung.
Eine Website kann HTTP 200 zurückgeben und trotzdem kaputt sein.
Beispielsweise könnte die Antwort enthalten:
Fatal error
oder:
There has been a critical error on this website.
Deshalb kann ein einfacher Smoke Test zusätzlich nach bekannten Fehlermustern suchen:
grep -Eqi \
'Fatal error|Parse error|Uncaught Error|critical error'
Werden solche Muster gefunden, schlägt die Pipeline fehl.
Zusätzlich kann erwartetes Markup kontrolliert werden:
grep -qi "<html" /tmp/homepage.html
grep -qi "<body" /tmp/homepage.html
Auch das beweist nicht, dass die Website vollständig korrekt funktioniert.
Das ist nicht der Anspruch eines Smoke Tests.
Der Smoke Test beantwortet eine kleinere Frage:
Ist das System nach dem Deployment grundsätzlich noch am Leben?
Validierung, Tests und Monitoring sind nicht dasselbe
Die Pipeline enthält inzwischen mehrere Schutzebenen:
Static Validation
│
│ PHP syntaktisch korrekt?
│ JS syntaktisch korrekt?
│ Theme vollständig?
▼
Deployment Guards
│
│ Zielpfad korrekt?
│ Änderungen plausibel?
▼
Backup
│
▼
Deployment
│
▼
Smoke Tests
│
│ Server erreichbar?
│ HTTP 200?
│ HTML vorhanden?
│ Fatal Error?
▼
Production
Jede Ebene beantwortet andere Fragen.
Keine davon macht die anderen überflüssig.
Deployment-Feedback ist Teil des Systems
Ein Deployment-Workflow sollte nicht nur funktionieren.
Er sollte verständlich erklären, was passiert ist.
Eine minimale Action zeigt vielleicht:
Validate ✓
Deploy ✓
Das ist besser als nichts.
Es fehlen aber wichtige Informationen.
Eine aussagekräftigere Zusammenfassung könnte so aussehen:
Production deployment succeeded
Theme my-theme
Previous version 1.3.1
Deployed version 1.4.0
Commit 41ef712
Planned changes 17
Planned deletions 4
Backup Created
Backups remaining 10
Homepage HTTP 200
Articles HTTP 200
Fatal-error scan Passed
GitHub Actions stellt dafür GITHUB_STEP_SUMMARY bereit.
Beispielsweise:
{
echo "## Production deployment succeeded"
echo ""
echo "| Item | Result |"
echo "|---|---|"
echo "| Homepage | HTTP 200 |"
echo "| Backup | Created |"
echo "| Fatal-error scan | Passed |"
} >> "$GITHUB_STEP_SUMMARY"
Damit wird die Pipeline selbst zu einem kleinen Deployment-Protokoll.
Die Theme-Version mitführen
WordPress-Themes besitzen in style.css bereits Metadaten:
/*
Theme Name: My Theme
Version: 1.4.0
*/
Die Pipeline kann diese Version auslesen:
VERSION=$(sed -n \
's/^[[:space:]]*Version:[[:space:]]*//p' \
style.css \
| head -1)
Vor dem Deployment lässt sich die produktive Version auslesen.
Danach die neue.
Das Ergebnis:
Previous version: 1.3.1
Deployed version: 1.4.0
Damit wird die Theme-Version Teil des Deployment-Prozesses statt nur eine dekorative Angabe in WordPress.
Was gehört nicht auf den Server?
Das Repository enthält möglicherweise Dateien, die zur Entwicklung gehören, aber nicht zum Theme.
Beispielsweise:
.git/
.github/
.gitignore
README.md
*.zip
*.tmp
*.bak
Diese können beim Deployment ausgeschlossen werden:
--exclude=".git/"
--exclude=".github/"
--exclude=".gitignore"
--exclude="README.md"
--exclude="*.zip"
--exclude="*.tmp"
--exclude="*.bak"
Damit wird aus dem Repository gezielt das Produktionsartefakt erzeugt.
Bei komplexeren Projekten wäre ein expliziter Build-Schritt noch sauberer.
Für ein klassisches PHP-WordPress-Theme reicht eine kontrollierte Synchronisation häufig aus.
main automatisch deployen oder Releases verwenden?
Ein Push auf main kann unmittelbar Produktion aktualisieren:
on:
push:
branches:
- main
Das ist echtes Continuous Deployment.
Der Ablauf:
Commit
↓
Push main
↓
Validation
↓
Deployment
↓
Production
Eine konservativere Variante verwendet Git-Tags:
on:
push:
tags:
- "v*"
Dann:
git tag v1.4.0
git push origin v1.4.0
Der Tag wird zum expliziten Release-Signal.
Beide Modelle sind legitim.
Die Entscheidung hängt davon ab, welche Bedeutung main im Entwicklungsprozess hat.
Noch eine Stufe weiter: GitHub Environments
GitHub unterstützt Environments wie:
development
staging
production
Ein Deployment-Job kann explizit einem Environment zugeordnet werden:
environment:
name: production
url: https://example.com
Darauf lassen sich weitere Regeln aufbauen.
Bei größeren Systemen entsteht daraus beispielsweise:
Pull Request
│
▼
CI
│
▼
Merge
│
▼
Staging
│
▼
Approval
│
▼
Production
Für einen persönlichen WordPress-Blog kann das überdimensioniert sein.
Das zugrunde liegende Konzept bleibt trotzdem wichtig:
Deployment-Ziele sind eigene Umgebungen mit eigenen Regeln und Secrets.
Was diese Pipeline nicht löst
Automatisierung sollte nicht mit Sicherheit verwechselt werden.
Die beschriebene Pipeline schützt beispielsweise nicht automatisch vor:
- semantischen PHP-Fehlern
- fehlerhaftem CSS
- kaputtem Responsive Design
- JavaScript-Logikfehlern
- falschen WordPress Queries
- Datenbankproblemen
- Plugin-Inkompatibilitäten
- Änderungen an WordPress-Inhalten
- defekten externen Diensten
Ein Theme kann alle Syntaxchecks bestehen und trotzdem visuell zerstört sein.
Dafür wären weitere Ebenen notwendig:
Unit Tests
Integration Tests
Browser Tests
Visual Regression Tests
Staging
Monitoring
Eine Pipeline muss nicht alles prüfen.
Sie sollte aber klar definieren, was ein grüner Status bedeutet.
In diesem Fall bedeutet er ungefähr:
Der erwartete Theme-Code ist vollständig, syntaktisch valide, wurde auf das richtige Ziel synchronisiert und die wichtigsten WordPress-Seiten antworten danach ohne offensichtlichen Laufzeitfehler.
Das ist bereits erheblich stärker als:
Der Upload ist fertig.
Der vollständige Denkprozess
Am Anfang steht häufig dieses Modell:
Ich habe ein WordPress-Theme.
Ich ändere Dateien.
Dann lade ich sie hoch.
Das führt fast automatisch zu einem dateizentrierten Prozess.
Das bessere Modell lautet:
Ich entwickle Software.
Das Repository beschreibt einen definierten Zustand.
Dieser Zustand wird validiert.
Ein validierter Zustand wird deployt.
Der vorherige Zustand wird gesichert.
Das Deployment wird anschließend verifiziert.
Daraus entsteht:
Git Repository
│
│ Source of Truth
▼
┌─────────────────┐
│ Validation │
│ │
│ PHP │
│ JavaScript │
│ Theme structure │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Deployment Guard│
│ │
│ SSH │
│ Target check │
│ rsync dry-run │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Backup │
│ │
│ Timestamp │
│ Commit │
│ Retention │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Deployment │
│ │
│ rsync │
│ --delete │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Verification │
│ │
│ HTTP │
│ HTML │
│ Fatal errors │
└────────┬────────┘
│
▼
Production
GitHub Actions ist dabei nicht der entscheidende Teil.
Auch GitLab CI/CD, Jenkins, Azure DevOps oder ein anderes System könnten denselben Prozess abbilden.
Die entscheidende Veränderung ist die Architektur des Entwicklungsprozesses.
Vom Theme-Upload zum Software-Deployment
Der Unterschied zwischen beiden Ansätzen lässt sich auf wenige Zeilen reduzieren.
Vorher:
Dateien ändern
↓
Dateien hochladen
↓
fertig
Nachher:
Änderung
↓
Git
↓
Validierung
↓
Dry Run
↓
Backup
↓
Deployment
↓
Smoke Test
↓
Production
Das wirkt für ein WordPress-Theme zunächst nach viel Infrastruktur.
Die einzelnen Bausteine sind jedoch klein.
Ein PHP-Syntaxcheck kostet fast nichts.
Ein SSH-Verbindungstest kostet fast nichts.
Ein rsync --dry-run kostet fast nichts.
Ein Backup vor dem Überschreiben kostet wenig.
Ein HTTP-Smoke-Test kostet wenige Sekunden.
Zusammen verändern diese kleinen Guardrails die Eigenschaft des gesamten Prozesses.
Das Theme liegt nicht mehr einfach auf einem Webserver.
Es besitzt einen nachvollziehbaren Lebenszyklus.
Und genau das sollte für ein WordPress-Theme genauso selbstverständlich sein wie für jede andere Software.