Refactor deployment process and update documentation
EK-DOS-WEB bauen und ausrollen / backend-pruefen (push) Failing after 2s
EK-DOS-WEB bauen und ausrollen / bauen-und-ausrollen (push) Skipped

- Removed nginx configuration file as it is no longer needed.
- Updated README.md to reflect changes in deployment structure.
- Enhanced DEPLOYMENT.md with detailed directory structure and user permissions.
- Added release.sh and rollback.sh scripts for managing deployments.
- Improved N8N.md to clarify file access and document handling.
- Adjusted health check and cache flushing procedures in deployment scripts.
This commit is contained in:
Kyle Müller
2026-09-06 22:41:04 +02:00
parent 7d82e807fc
commit 940b2d4767
7 changed files with 589 additions and 259 deletions
+165 -97
View File
@@ -3,24 +3,69 @@ name: EK-DOS-WEB bauen und ausrollen
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
# Zwei gleichzeitige Ausrollvorgänge kämen sich beim Symlink-Tausch in die Quere.
# Ein neuer Lauf wartet, statt den laufenden abzubrechen.
concurrency:
group: ekdos-deploy
cancel-in-progress: false
env:
DEPLOY_HOST: 10.0.11.131
DEPLOY_USER: deploy
DEPLOY_ROOT: /var/www/ekdos
DEPLOY_PATH: /var/www/ekdos
jobs:
# ──────────────────────────────────────────────────────────────────────────
# Die Oberfläche wird hier zu statischen Dateien kompiliert. Node existiert
# ausschliesslich in diesem Container; auf dem Zielserver läuft keines.
# Backend: nichts zu kompilieren, aber alles zu prüfen. Composer läuft hier
# NICHT -- die Abhängigkeiten werden auf dem Webserver aufgelöst, wo PHP-
# Version und Erweiterungen tatsächlich stimmen. Eine Plattformprüfung im
# Runner würde nur sich selbst prüfen.
# ──────────────────────────────────────────────────────────────────────────
frontend:
backend-pruefen:
runs-on: ubuntu-latest
container: node:22-bookworm-slim
defaults:
run:
working-directory: frontend
container: php:8.5-cli
steps:
- uses: actions/checkout@v4
- name: Syntax aller PHP-Dateien prüfen
working-directory: backend
run: |
set -eu
find src public bin -type f \( -name '*.php' -o -name 'ekdos' \) -print0 \
| xargs -0 -n1 -P4 php -l
- name: composer.json und composer.lock prüfen
working-directory: backend
run: |
set -eu
curl -sS https://getcomposer.org/installer \
| php -- --install-dir=/usr/local/bin --filename=composer
composer validate --no-check-publish
# Ohne Sperrdatei löst der Webserver bei jedem Ausrollen neu auf und
# bekommt womöglich andere Fassungen als hier geprüft.
test -f composer.lock || {
echo "composer.lock fehlt. Einmal 'composer update' laufen lassen und einchecken."
exit 1
}
# ──────────────────────────────────────────────────────────────────────────
# Bauen und ausrollen in einem Auftrag.
#
# Bewusst ohne upload-/download-artifact: welche Fassung dieser Aktionen eine
# Gitea-Instanz beherrscht (v3 oder v4), hängt an deren Version und ist eine
# verlässliche Quelle für Fehlschläge beim ersten Lauf. Das Bündel bleibt
# einfach im Arbeitsverzeichnis liegen und geht von dort per rsync raus.
#
# Node existiert nur in diesem Container -- auf dem Webserver läuft keines.
# ──────────────────────────────────────────────────────────────────────────
bauen-und-ausrollen:
runs-on: ubuntu-latest
container: node:22-bookworm
needs: [backend-pruefen]
steps:
- uses: actions/checkout@v4
@@ -28,121 +73,144 @@ jobs:
run: corepack enable && corepack prepare pnpm@10.16.1 --activate
- name: Abhängigkeiten installieren
working-directory: frontend
run: pnpm install --frozen-lockfile
# Typprüfung getrennt, damit ein Typfehler nicht als Bündelfehler erscheint.
# Getrennt von `build`, damit ein Typfehler als solcher im Protokoll steht
# und nicht als Bündelfehler.
- name: Typen prüfen
working-directory: frontend
run: pnpm typecheck
- name: Bündel bauen
working-directory: frontend
run: pnpm build
- uses: actions/upload-artifact@v3
with:
name: frontend-dist
path: frontend/dist
retention-days: 7
# ──────────────────────────────────────────────────────────────────────────
# Das Backend wird nicht kompiliert, aber geprüft: Syntax aller Dateien und
# ein Abhängigkeitsbaum ohne Entwicklungspakete.
# ──────────────────────────────────────────────────────────────────────────
backend:
runs-on: ubuntu-latest
container: php:8.5-cli
defaults:
run:
working-directory: backend
steps:
- uses: actions/checkout@v4
- name: Composer und Erweiterungen bereitstellen
- name: Bündel prüfen
working-directory: frontend
run: |
apt-get update && apt-get install -y --no-install-recommends git unzip libpq-dev
docker-php-ext-install pdo_pgsql
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
set -eu
test -f dist/index.html || { echo "dist/index.html fehlt -- der Bau hat nichts erzeugt."; exit 1; }
du -sh dist
- name: Abhängigkeiten installieren
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --ignore-platform-req=ext-redis
# ── Ab hier nur noch aus main ──────────────────────────────────────────
- name: Syntax prüfen
run: find src public bin -type f \( -name '*.php' -o -name 'ekdos' \) -print0 | xargs -0 -n1 php -l
- uses: actions/upload-artifact@v3
with:
name: backend-build
path: |
backend
!backend/.env
retention-days: 7
# ──────────────────────────────────────────────────────────────────────────
# Ausrollen. Jede Veröffentlichung landet in einem eigenen Verzeichnis; erst
# der Symlink-Tausch macht sie sichtbar. Ein Fehlschlag lässt die laufende
# Fassung unberührt.
# ──────────────────────────────────────────────────────────────────────────
deploy:
runs-on: ubuntu-latest
needs: [frontend, backend]
if: gitea.ref == 'refs/heads/main'
steps:
- uses: actions/download-artifact@v3
with:
name: frontend-dist
path: dist
- uses: actions/download-artifact@v3
with:
name: backend-build
path: backend
- name: Werkzeuge zum Ausrollen installieren
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
set -eu
apt-get update
apt-get install -y --no-install-recommends rsync openssh-client
- name: SSH einrichten
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
run: |
set -eu
test -n "${DEPLOY_SSH_KEY:-}" || { echo "Secret DEPLOY_SSH_KEY fehlt."; exit 1; }
install -m 700 -d ~/.ssh
echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
# Bevorzugt der hinterlegte Fingerabdruck. Fehlt er, wird er erfragt --
# bequem, aber ohne Schutz vor einem untergeschobenen Ziel. Für den
# Dauerbetrieb setzen: ssh-keyscan -t ed25519 10.0.11.131
if [ -n "${DEPLOY_KNOWN_HOSTS:-}" ]; then
printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > ~/.ssh/known_hosts
else
echo "Hinweis: DEPLOY_KNOWN_HOSTS ist nicht gesetzt, Fingerabdruck wird ungeprüft übernommen."
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts 2>/dev/null
fi
chmod 600 ~/.ssh/known_hosts
# Lieber hier scheitern als nach dem halben Hochladen.
- name: Ziel prüfen
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
set -eu
ssh "$DEPLOY_USER@$DEPLOY_HOST" "
set -eu
test -d '$DEPLOY_PATH/releases' || { echo '$DEPLOY_PATH/releases fehlt -- siehe docs/DEPLOYMENT.md'; exit 1; }
test -f '$DEPLOY_PATH/shared/.env' || { echo '$DEPLOY_PATH/shared/.env fehlt -- einmalig anlegen'; exit 1; }
command -v composer >/dev/null || { echo 'composer fehlt auf dem Webserver'; exit 1; }
command -v php >/dev/null || { echo 'php fehlt auf dem Webserver'; exit 1; }
# Prüft die sudo-Regel, ohne etwas auszuführen.
sudo -n -l /bin/systemctl reload php8.5-fpm >/dev/null 2>&1 \
|| echo 'Hinweis: sudo-Regel für den fpm-Neustart fehlt (siehe docs/DEPLOYMENT.md) -- release.sh wird daran scheitern.'
mkdir -p '$DEPLOY_PATH/releases/${{ github.sha }}' '$DEPLOY_PATH/deploy'
"
- name: Oberfläche übertragen
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
RELEASE="$DEPLOY_ROOT/releases/${{ gitea.sha }}"
ssh "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p '$RELEASE'"
rsync -az --delete dist/ "$DEPLOY_USER@$DEPLOY_HOST:$RELEASE/"
set -eu
rsync -az --delete \
frontend/dist/ \
"$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/releases/${{ github.sha }}/public/"
- name: Backend übertragen
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
# .env bleibt auf dem Server und wird nie überschrieben.
set -eu
# vendor/ entsteht auf dem Server; .env liegt in shared/ und wird dort
# verknüpft; var/ ist der Zwischenspeicher dieser Veröffentlichung.
rsync -az --delete \
--exclude 'vendor/' \
--exclude '.env' \
--exclude 'var/cache' \
backend/ "$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_ROOT/backend/"
--exclude 'var/' \
backend/ \
"$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/releases/${{ github.sha }}/backend/"
- name: Umschalten und neu laden
- name: Ausrollskripte übertragen
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
ssh "$DEPLOY_USER@$DEPLOY_HOST" bash -euo pipefail <<'REMOTE'
cd /var/www/ekdos
set -eu
rsync -az --chmod=F755 \
deploy/release.sh deploy/rollback.sh \
"$DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/deploy/"
# Schema fortschreiben, bevor die neue Oberfläche sichtbar wird.
php backend/bin/ekdos migrate
# Atomarer Tausch: ln -sfn auf ein temporäres Ziel, dann umbenennen.
ln -sfn "releases/${GITEA_SHA:-$(ls -1t releases | head -1)}" current.new
mv -Tf current.new current
# Opcache hält den alten Quelltext, bis fpm neu lädt.
sudo systemctl reload php8.5-fpm
# Zwischengespeicherte n8n-Antworten passen womöglich nicht mehr
# zum neuen Abbildungscode.
php backend/bin/ekdos cache:flush
# Die letzten fünf Veröffentlichungen behalten.
ls -1t releases | tail -n +6 | xargs -r -I{} rm -rf "releases/{}"
REMOTE
env:
GITEA_SHA: ${{ gitea.sha }}
- name: Gesundheitsprüfung
# Hier passiert das Eigentliche: composer install, Migration, atomarer
# Symlink-Tausch, php-fpm neu laden, n8n-Cache verwerfen,
# Gesundheitsprüfung. Das Skript liegt unter deploy/release.sh und lässt
# sich genauso von Hand aufrufen.
- name: Veröffentlichung aktivieren
id: aktivieren
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
set -eu
ssh "$DEPLOY_USER@$DEPLOY_HOST" \
'curl -fsS -o /dev/null -w "%{http_code}\n" http://127.0.0.1/api/health'
"$DEPLOY_PATH/deploy/release.sh '${{ github.sha }}'"
# KEIN automatischer Rückschritt.
#
# release.sh tauscht den Symlink erst nach composer und Migration. Scheitert
# es davor, läuft die alte Fassung unverändert weiter -- ein Rückschritt
# würde dann auf die *vorletzte* schalten und aus einem folgenlosen
# Fehlschlag einen Ausfall machen. Deshalb hier nur berichten, was live ist.
- name: Bei Fehlschlag Lage berichten
if: failure() && steps.aktivieren.outcome == 'failure'
run: |
echo "Das Aktivieren ist fehlgeschlagen. Aktueller Stand auf dem Server:"
ssh "$DEPLOY_USER@$DEPLOY_HOST" "
echo -n ' aktiv: '; readlink '$DEPLOY_PATH/current' || echo '(keiner)'
echo -n ' vorher: '; cat '$DEPLOY_PATH/previous' 2>/dev/null || echo '(nichts vermerkt)'
curl -fsS --max-time 5 http://127.0.0.1/api/health && echo || echo ' /api/health antwortet NICHT'
" || true
echo
echo "Läuft die alte Fassung noch, ist nichts weiter zu tun -- Ursache beheben und erneut pushen."
echo "Sonst von Hand zurück: ssh $DEPLOY_USER@$DEPLOY_HOST $DEPLOY_PATH/deploy/rollback.sh"
exit 1
# /api/health ist im Port-80-Block von der HTTPS-Umleitung ausgenommen,
# sonst wäre die 301 für `curl -f` ein Erfolg und PHP nie erreicht.
- name: Ergebnis
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: |
set -eu
echo "Ausgerollt: ${{ github.sha }}"
ssh "$DEPLOY_USER@$DEPLOY_HOST" \
"curl -fsS --max-time 5 -H 'Accept: application/json' http://127.0.0.1/api/health"
echo