Skip to content

Deployment nach Mittwald ​

Der Workflow .github/workflows/deploy.yml baut, prüft und überträgt per rsync über SSH. Die Begründung des Verfahrens steht in ADR 0014.

Einrichten ​

1. Zielverzeichnis ​

Bei Mittwald liegt der Dokumentenstamm unterhalb des Projektverzeichnisses, der SSH-Benutzer ist die Projektkennung:

text
/home/www/p123456/html/kunde.de/
├── index.html …            aus dist/
└── api/forms/              nur bei provider: 'standalone-php'

Welcher Pfad der Domain zugeordnet ist, steht in mStudio unter der Domain-Konfiguration. Der Wert gehört unverändert in DEPLOY_PATH.

2. SSH-Schlüssel ​

Ein eigenes Schlüsselpaar für das Deployment — nicht der persönliche Schlüssel:

sh
ssh-keygen -t ed25519 -C "deploy kunde.de" -f ./deploy_key -N ""

Den öffentlichen Teil (deploy_key.pub) in mStudio beim SSH-Benutzer hinterlegen, den privaten (deploy_key) als Secret DEPLOY_SSH_KEY in die GitHub-Umgebung. Danach die lokale Kopie löschen.

3. Hostschlüssel ​

sh
ssh-keyscan -t rsa,ecdsa,ed25519 ssh.kunde.mittwald.de

Die Ausgabe gehört vollständig in die Variable DEPLOY_HOST_KEY. Fehlt sie, bricht der Workflow ab — beabsichtigt.

Nicht abkürzen

StrictHostKeyChecking=no steht in jeder Anleitung und macht die Verschlüsselung der Verbindung wertlos: Sie schützt dann gegen niemanden, der sich dazwischenstellen kann. Den Fingerprint einmal gegen die Angabe des Hosters prüfen kostet zwei Minuten und gilt für die Lebensdauer des Servers.

4. GitHub-Umgebung ​

Unter Settings → Environments je Umgebung (production, bei Bedarf staging):

ArtNameBeispiel
SecretDEPLOY_SSH_KEYInhalt von deploy_key
VariableDEPLOY_HOSTssh.kunde.mittwald.de
VariableDEPLOY_USERp123456
VariableDEPLOY_PATH/home/www/p123456/html/kunde.de
VariableDEPLOY_HOST_KEYAusgabe von ssh-keyscan
VariableDEPLOY_URLhttps://www.kunde.de
VariableDEPLOY_PORTnur bei abweichendem Port
VariablePUBLIC_TURNSTILE_SITE_KEYnur bei captcha: 'turnstile'
SecretKT_BASIC_AUTH_USERnur bei deployment.apache.basicAuth
SecretKT_BASIC_AUTH_PASSWORDnur bei deployment.apache.basicAuth

DEPLOY_USER ist bei manchen Mittwald-Projekten selbst eine Zeichenkette mit @ (bei projektübergreifendem SSH-Zugriff über die neuere „Mittwald Stack"-Plattform, Format ssh-nutzer@projekt-id). Der Workflow übergibt ihn deshalb per ssh -l, nicht durch Zusammensetzen von user@host — sonst würde aus einem eingebetteten @ ein zweites, und die Verbindung schlägt fehl oder verbindet zum falschen Ziel. Der Wert steht in mStudio beim SSH-Zugang des Projekts, wortwörtlich übernehmen.

KT_BASIC_AUTH_USER/KT_BASIC_AUTH_PASSWORD sind nur nötig, wenn deployment.apache.basicAuth gesetzt ist (Apache) — ein Vorschau-Schutz vor dem Launch. Fehlen sie bei einem production-Build, bricht er ab.

DEPLOY_URL erscheint als Verweis am Deployment in GitHub. Der Turnstile Site Key ist öffentlich per Design und deshalb eine Variable; der Secret Key liegt ausschließlich beim Handler.

Für production empfiehlt sich zusätzlich ein Required reviewer: Damit ist das Deployment eine Freigabe und nicht nur ein Tag.

5. Erster Lauf als Probelauf ​

sh
gh workflow run deploy.yml --ref v1.0.0 -f dryRun=true

Der Lauf durchläuft alles bis zur Übertragung und zeigt dann nur, was er ändern würde. Das ist der Moment, in dem ein falscher DEPLOY_PATH auffällt — und nicht der, in dem --delete im falschen Verzeichnis arbeitet.

Was der Workflow tut ​

  1. Abhängigkeiten installieren, PHP 8.3 bereitstellen
  2. pnpm verify — nur, wenn die CI die Kette für diesen Commit nicht nachweisen kann; dann samt Chromium (ADR 0019)
  3. pnpm run build mit dem PUBLIC_ENVIRONMENT dieser Umgebung
  4. .deploy-plan.json lesen und gegen die Umgebung prüfen
  5. Bei standalone-php: composer install --no-dev --optimize-autoloader
  6. SSH einrichten, Hostschlüssel pinnen
  7. dist/ → Dokumentenstamm, mit --delete, ohne das Handler-Verzeichnis
  8. server/forms/ → Handler-Verzeichnis: nur submit.php, src/, vendor/ und config/ — ohne config.local.php, var/, tests/ und die Composer-Dateien

Schritt 7 und 8 sind getrennt, weil für beide Teile verschiedene Regeln gelten. Ein einzelner Aufruf mit --delete würde entweder den Handler löschen oder verwaiste Dateien einer früheren Version stehen lassen.

Nach dem ersten Deployment ​

Auf dem Server bleibt eine Sache von Hand zu tun: config/config.local.php anlegen. Die Vorlage config.local.php.example wird mit übertragen, die Datei selbst nie — sie enthält das SMTP-Passwort. Ohne sie antwortet der Handler mit einem Konfigurationsfehler.

Danach eine echte Übermittlung prüfen: Kommt die E-Mail an, und steht im Log ein Eintrag ohne Feldinhalte?

Rollback ​

sh
gh workflow run deploy.yml --ref v1.1.0

Das ist derselbe Weg mit einem früheren Stand, kein zweiter Mechanismus. Es dauert so lange wie ein Deployment — weil es eines ist.

Fehlerbilder ​

SymptomUrsache
Tag gepusht, aber kein Lauf erscheintDer Tag kam von einem Workflow mit GITHUB_TOKEN — das löst nichts aus, siehe Deployment
Host key verification failedDEPLOY_HOST_KEY fehlt, ist veraltet oder gehört zu einem anderen Host
Permission denied (publickey)Öffentlicher Schlüssel nicht beim SSH-Benutzer hinterlegt, oder DEPLOY_USER falsch
Deployment grün, Website unverändertDEPLOY_PATH zeigt nicht auf den Dokumentenstamm der Domain — in mStudio gegenprüfen
Formular antwortet mit „Es ist ein Fehler aufgetreten"config/config.local.php fehlt auf dem Server oder KT_FORMS_ORIGINS passt nicht zur Domain
Website lädt, aber ohne Gestaltung.htaccess greift nicht — AllowOverride prüfen, siehe Apache
deployment.target ist "github-pages"Falscher Workflow für dieses Projekt; deploy-pages.yml ist zuständig
WeiterleitungsschleifeEin zweiter HTTPS-Redirect in der Serverkonfiguration. deployment.apache.forceHttps: false setzen

Ohne GitHub Actions ​

Wer von Hand überträgt, braucht dieselben Schritte:

sh
PUBLIC_ENVIRONMENT=production pnpm run build
composer install --working-dir=server/forms --no-dev --optimize-autoloader

rsync -az --delete --exclude=/.deploy-plan.json --exclude=/api/forms/ \
  dist/ p123456@ssh.kunde.mittwald.de:/home/www/p123456/html/kunde.de/

rsync -az --delete --mkpath \
  --exclude=config/config.local.php --exclude=var/ \
  --exclude=tests/ --exclude=phpunit.xml.dist \
  --exclude=composer.json --exclude=composer.lock \
  server/forms/ p123456@ssh.kunde.mittwald.de:/home/www/p123456/html/kunde.de/api/forms/

--mkpath bei der zweiten Übertragung: Das Handler-Verzeichnis liegt unterhalb des Dokumentenstamms und existiert beim allerersten Deployment noch nicht — ohne die Option legt rsync nur die letzte fehlende Verzeichnisebene an, nicht mehrere verschachtelte auf einmal.

Die Ausschlusslisten sind nicht optional. Ohne die erste löscht --delete das Handler-Verzeichnis, ohne die zweite das SMTP-Passwort und das Log.

Auf dem Server bleiben damit submit.php, src/, vendor/ und config/ — genau das, was der Handler zur Laufzeit braucht. Die Tests und die Composer-Dateien gehören nicht dazu: Das ist derselbe Grund, aus dem composer install mit --no-dev läuft. Die .htaccess des Verzeichnisses würde sie zwar sperren, aber auf sie allein verlässt sich diese Auslieferung an keiner Stelle.