Compare commits

...
2 Commits
Author SHA1 Message Date
gg efef212544 feat(deploy): Image-Promotion, deploy.env, Health-Check-Fix und Anleitungen
- Deploy-Scripts: Image-Promotion statt Rebuild (docker tag + up -d),
  deploy.env fuer Server-Konfiguration, -p fuer Projekt-Namen,
  Health-Check mit Host-Header und /xhr/health-check
- docker-compose.prod.yaml: name-Feld entfernt (Projekt-Name per -p)
- deploy.env.example als Vorlage hinzugefuegt
- .vibe/skills/client-build/SKILL.md aus Git-Index entfernt (gitignore)
- Deployment-Anleitung: Zugangsdaten, deploy.env, Image-Promotion,
  prod ohne eigenes Repo, git-clone-Hinweis, Log-Befehle mit -p
- Deploy-lokal-testen.md: neue Anleitung fuer lokales Testen

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-15 17:10:18 +02:00
gg e824b545bb docs(agents): Begründung für user_id-Parameter ergänzt und Formulierungen korrigiert
Ergänzt eine kurze Begründung, warum user_id als Parameter an Modell-Funktionen
übergeben wird statt current_user direkt im Modell zu lesen. Zudem kleinere
Korrekturen an Tippfehlern, Pfadangaben und Formulierungen in den
TypeScript/Mithril-Vorgaben.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-09-14 10:06:11 +02:00
8 changed files with 447 additions and 111 deletions

No files matched your search

-54
View File
@@ -1,54 +0,0 @@
---
name: client-build
description: Load this skill after modifying TypeScript files under Client/ to compile and bundle the frontend. Runs tsc and rollup to produce the deployable JS bundle in NGINX_Server/html/static/.
---
# Client Build
Führt den TypeScript- und Rollup-Build für die Client-Seite aus. Wird benötigt,
nachdem `.ts`-Dateien unter `Client/` geändert wurden, damit die kompilierten
Bundles in `NGINX_Server/html/static/` aktualisiert werden.
## Build-Schritte
1. **TypeScript kompilieren** — kompiliert alle `.ts`-Dateien nach `Client/tsout/`:
```bash
cd /usr/opt/pythonapp/Client && npx tsc
```
Bei Fehlern: Ausgabe lesen, Fehler beheben, erneut ausführen.
2. **Rollup bundle** — bündelt `tsout/index.js` zu den statischen JS-Dateien:
```bash
cd /usr/opt/pythonapp/Client && npx rollup -c
```
`rollup -c` liest die Konfiguration aus `rollup.config.mjs`. Es fasst alle
kompilierten `.js`-Dateien plus Mithril zu einem selbstausführenden Bundle
(Format: iife) zusammen und schreibt zwei Dateien nach
`NGINX_Server/html/static/`:
- `kbp_crm_00.00.01.js` — unminifiziert (für Debug)
- `kbp_crm_00.00.01.min.js` — minifiziert via terser (für Produktion)
Die Dateinamen setzen sich aus `projectname` und `version` aus `config.ini`
zusammen (wird in `rollup.config.mjs` via `inireader.mjs` gelesen).
## Beide Schritte in einem Aufruf
```bash
cd /usr/opt/pythonapp/Client && npx tsc && npx rollup -c
```
## Wann ausführen
- Nach jeder Änderung an `.ts`-Dateien unter `Client/` (Modelle, Komponenten,
Widgets, index.ts)
- Vor einem Commit, der Client-Änderungen enthält, damit die deploybaren
Bundles aktuell sind
- Nicht nötig bei reinen Server-Änderungen (`App_Server/`)
## Fehlerbehandlung
- **tsc-Fehler:** TypeScript-Compiler-Fehler müssen vor dem Rollup-Schritt
behoben werden — Rollup kann nicht ohne erfolgreiche Kompilierung bündeln.
- **rollup-Fehler:** Meist fehlende Imports oder Pfadprobleme. Die
`rollup.config.mjs` erwartet `tsout/index.js` als Input — sicherstellen,
dass tsc erfolgreich war.
+7 -5
View File
@@ -5,12 +5,12 @@ In den Programmen sollen aussagekräftige Kommentare in deutsch erstellt werden.
## Vorgaben git Versionsverwaltung ## Vorgaben git Versionsverwaltung
Ein neuer Branch darf nur auf ausdrückliche Anweisung hin erstellt werden. Ohne diese Anweisung werden Änderungen immer in main gemacht. Ein neuer Branch darf nur auf ausdrückliche Anweisung hin erstellt werden. Ohne diese Anweisung werden Änderungen immer in main gemacht.
## Vorgaben für die Typescript und mithriljs Entwicklung ## Vorgaben für die Typescript und mithril.js Entwicklung
Verwende bei der GUI primar die widgets, die es in client/Components/widgets gibt. Fehler widgets, gib eines Hinweis und erstelle einen Vorschlag für ein entsprechendes neues Widget. Verwende bei der GUI primar die Widgets und Komponenten, die es in Client/Components/widgets und Client/Components gibt. Wenn Widgets fehlen, gib eines Hinweis und erstelle einen Vorschlag für ein entsprechendes neues Widget.
Es soll immer ein responsive Design implmentiert werden, da die App sowohl auf PCs als auch auf mobilen Geräten laufen soll. Es soll immer ein responsive Design implmentiert werden, da die App sowohl auf PCs als auch auf mobilen Geräten laufen soll.
Verwende für Attribute der GUI-Oberfläche nach Möglichkeit Klassen aus NGINX_Server/html/static/w3.css, insbesondere wenn es darum geht, eine Respnosive Design zu implentieren. Verwende für Attribute der GUI-Oberfläche nach Möglichkeit Klassen aus NGINX_Server/html/static/w3.css, insbesondere wenn es darum geht, ein Respnosive Design zu implentieren.
Für Farben (foreground und background) verwende priorisiert die Klassen aus NGINX_Server/html/static/app-theme.css Für Farben (foreground und background) verwende priorisiert die Klassen aus NGINX_Server/html/static/app-theme.css
View-Container werden mit `w3-container.w3-auto` gewrappt, damit sie auf kleinen Bildschirmen 100% und auf großen bis max. 1140px (bzw. 95%) zentriert nutzen. View-Container werden mit `w3-container.w3-auto` gewrappt, damit sie auf kleinen Bildschirmen 100% und auf großen bis max. 1140px (bzw. 95%) zentriert werden.
Bei Mithril `m(...)`-Aufrufen mit längeren Attributlisten (z.B. mehrere `style`-Eigenschaften oder mehrere Attribute) schreibe die Attribute untereinander, sobald sie in einer Zeile nicht mehr gut lesbar sind (Faustregel: mehr als 2–3 Attribute oder ein mehrzeiliges `style`-Objekt). Kurze Attributlisten (1–2 Attribute, einzeiliges `style`) dürfen in einer Zeile bleiben. Formatbeispiel: Bei Mithril `m(...)`-Aufrufen mit längeren Attributlisten (z.B. mehrere `style`-Eigenschaften oder mehrere Attribute) schreibe die Attribute untereinander, sobald sie in einer Zeile nicht mehr gut lesbar sind (Faustregel: mehr als 2–3 Attribute oder ein mehrzeiliges `style`-Objekt). Kurze Attributlisten (1–2 Attribute, einzeiliges `style`) dürfen in einer Zeile bleiben. Formatbeispiel:
```typescript ```typescript
@@ -25,7 +25,7 @@ m("div.w3-bar-block.w3-border", {
}) })
``` ```
Für Symbole auf Buttons und ähnlichen Steuerelementen verwende Symbole aus den Google Material Icons. Für Symbole auf Buttons und ähnlichen Steuerelementen verwende Symbole aus den Google Material Icons aus der NGINX_Server/html/static/Google_Symbols.woff2 Datei.
Erstelle die Datenmodelle für die Typescript-Entwicklung im Verzeichnis Client/Models. Erstelle für die verschiedenen Datenmodelle (Kunde, Projekt usw.) eigene .ts-Dateien. Erstelle in der jeweiligen Datei auch den Aufruf der REST-Api für die Server-Seite. Erstelle die Datenmodelle für die Typescript-Entwicklung im Verzeichnis Client/Models. Erstelle für die verschiedenen Datenmodelle (Kunde, Projekt usw.) eigene .ts-Dateien. Erstelle in der jeweiligen Datei auch den Aufruf der REST-Api für die Server-Seite.
Beim Aufruf einer Bearbeitungsmaske werden in den Route-Params nur die ID (und ggf. zum Vorbelegen notwendige Eltern-FKs wie `kunden_id`) übergeben — nicht die vollständigen Felder des Objekts. Die Maske lädt die Daten frisch vom Server via `fetchXxxById(id)` im `oninit`. So wird sichergestellt, dass die Daten aktuell sind und zwischenzeitliche Änderungen anderer Nutzer nicht mit veralteten Werten aus der Liste überschrieben werden. Beim Neuanlegen (`id === -1`) entfällt der Fetch; vorbelegte Werte (z.B. `kunden_id`) kommen aus den Route-Params. Beim Aufruf einer Bearbeitungsmaske werden in den Route-Params nur die ID (und ggf. zum Vorbelegen notwendige Eltern-FKs wie `kunden_id`) übergeben — nicht die vollständigen Felder des Objekts. Die Maske lädt die Daten frisch vom Server via `fetchXxxById(id)` im `oninit`. So wird sichergestellt, dass die Daten aktuell sind und zwischenzeitliche Änderungen anderer Nutzer nicht mit veralteten Werten aus der Liste überschrieben werden. Beim Neuanlegen (`id === -1`) entfällt der Fetch; vorbelegte Werte (z.B. `kunden_id`) kommen aus den Route-Params.
@@ -64,6 +64,8 @@ Dafür werden in `crm/routes.py` zwei Hilfsfunktionen bereitgestellt:
- `_get_visible_user_id()`: Liefert `None` für SysAdmin/AppAdmin (kein Filter), sonst `current_user.id`. Wird an die Modell-Funktionen der Lese-Endpunkte (list/get) übergeben. - `_get_visible_user_id()`: Liefert `None` für SysAdmin/AppAdmin (kein Filter), sonst `current_user.id`. Wird an die Modell-Funktionen der Lese-Endpunkte (list/get) übergeben.
- `_require_projekt_access(projekt_id)`: Prüft vor Schreiboperationen (create/update/delete), ob der Nutzer dem Projekt zugeordnet ist. Bei fehlender Zuordnung wird 404 (nicht 403) geworfen, um keine Rückschlüsse auf die Existenz des Projekts zu ermöglichen. - `_require_projekt_access(projekt_id)`: Prüft vor Schreiboperationen (create/update/delete), ob der Nutzer dem Projekt zugeordnet ist. Bei fehlender Zuordnung wird 404 (nicht 403) geworfen, um keine Rückschlüsse auf die Existenz des Projekts zu ermöglichen.
Die `user_id` wird als Parameter übergeben statt `current_user` direkt im Modell zu lesen, weil `None` als Signalwert für Admins (kein Filter) nur so sauber transportiert werden kann und die Modell-Funktionen frei von Flask-Request-Kontext bleiben — das wahrt die Schichtentrennung und Testbarkeit.
Bei neuen projektbezogenen Entitäten muss: Bei neuen projektbezogenen Entitäten muss:
1. Die Modell-Funktion `get_xxx(...)` einen optionalen `user_id`-Parameter erhalten. Ist dieser gesetzt, wird per Subquery auf `projekt_users` gefiltert. 1. Die Modell-Funktion `get_xxx(...)` einen optionalen `user_id`-Parameter erhalten. Ist dieser gesetzt, wird per Subquery auf `projekt_users` gefiltert.
2. Die Modell-Funktion `get_xxx_by_id(id, user_id)` bei gesetztem `user_id` die Projekt-Zuordnung prüfen und bei Fehlen 404 werfen. 2. Die Modell-Funktion `get_xxx_by_id(id, user_id)` bei gesetztem `user_id` die Projekt-Zuordnung prüfen und bei Fehlen 404 werfen.
+229
View File
@@ -0,0 +1,229 @@
# Anleitung: Deploy-Prozess lokal auf dem Entwicklungsrechner testen
Diese Anleitung beschreibt, wie der komplette Deploy-Prozess (test-prod
und Prod-Promotion) lokal auf dem Entwicklungsrechner getestet werden kann —
ohne Git-Hook, ohne Remote-Server. Voraussetzung: Docker und Docker Compose
sind auf dem Host installiert (nicht im Dev-Container, sondern auf dem
Betriebssystem des Rechners).
---
## 1. Voraussetzungen
- Docker und Docker Compose auf dem Host installiert
- Git-Repo lokal vorhanden (oder Remote-URL fuer Clone)
- Freie Ports: 8080 (test-prod) und 8081 (prod)
---
## 2. Verzeichnisstruktur anlegen
```bash
mkdir -p /tmp/deploy-test/{test-prod/repo,scripts,test-prod/data,prod/data,test-prod/certs,prod/certs}
```
---
## 3. Repository klonen
```bash
# Entwender vom Remote-Server:
git clone https://git.everdc.eu/chmln-app-dev/kbcrm /tmp/deploy-test/test-prod/repo
# Oder vom lokalen Repo (schneller, kein Netzwerk noetig):
git clone /pfad/zum/kbcrm /tmp/deploy-test/test-prod/repo
```
---
## 4. `.env` fuer Test-Prod anlegen
```bash
cp /tmp/deploy-test/test-prod/repo/env-sample /tmp/deploy-test/test-prod/.env
vi /tmp/deploy-test/test-prod/.env
```
Minimal-Konfiguration fuer lokales Testen (SSL aus, keine Zertifikate noetig):
```bash
# NGINX-Server
NGINX_USE_SSL=false
NGINX_HOST_PORT=8080
NGINX_SERVER_NAME=localhost
# Auch bei SSL=false muessen die Variablen gesetzt sein (Compose bindet den Pfad ein)
NGINX_CERTS=/tmp/deploy-test/test-prod/certs
NGINX_CERT=placeholder
NGINX_CERT_KEY=placeholder
# Python App-Server
PYTHON_DB_DIR=/tmp/deploy-test/test-prod/data
# OIDC-Settings (ggf. anpassen oder Platzhalter fuer lokales Testen)
ZITADEL_ISSUER=https://id.vmlx.de
ZITADEL_CLIENT_ID=local-test
ZITADEL_CLIENT_SECRET=local-test
ZITADEL_REDIRECT_URI=http://localhost:8080/auth/callback
ZITADEL_SCOPES=openid,profile,email
```
---
## 5. `.env` fuer Prod anlegen
```bash
cp /tmp/deploy-test/test-prod/repo/env-sample /tmp/deploy-test/prod/.env
vi /tmp/deploy-test/prod/.env
```
```bash
NGINX_USE_SSL=false
NGINX_HOST_PORT=8081
NGINX_SERVER_NAME=localhost
NGINX_CERTS=/tmp/deploy-test/prod/certs
NGINX_CERT=placeholder
NGINX_CERT_KEY=placeholder
PYTHON_DB_DIR=/tmp/deploy-test/prod/data
ZITADEL_ISSUER=https://id.vmlx.de
ZITADEL_CLIENT_ID=local-test
ZITADEL_CLIENT_SECRET=local-test
ZITADEL_REDIRECT_URI=http://localhost:8081/auth/callback
ZITADEL_SCOPES=openid,profile,email
```
---
## 6. Deploy-Scripts und deploy.env kopieren
```bash
cp /tmp/deploy-test/test-prod/repo/scripts/deploy-kbcrm-test.sh /tmp/deploy-test/scripts/
cp /tmp/deploy-test/test-prod/repo/scripts/deploy-kbcrm-prod.sh /tmp/deploy-test/scripts/
cp /tmp/deploy-test/test-prod/repo/scripts/deploy.env.example /tmp/deploy-test/scripts/deploy.env
chmod +x /tmp/deploy-test/scripts/*.sh
```
`deploy.env` anpassen:
```bash
vi /tmp/deploy-test/scripts/deploy.env
```
```bash
# --- Test-Prod Umgebung ---
DEPLOY_REPO_DIR=/tmp/deploy-test/test-prod/repo
DEPLOY_ENV_FILE=/tmp/deploy-test/test-prod/.env
DEPLOY_PROJECT_NAME=kbptest
DEPLOY_HEALTH_PORT=8080
# --- Prod Umgebung ---
DEPLOY_PROD_COMPOSE_FILE=/tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml
DEPLOY_PROD_ENV_FILE=/tmp/deploy-test/prod/.env
DEPLOY_PROD_PROJECT_NAME=kbpprod
DEPLOY_PROD_TEST_PROJECT_NAME=kbptest
DEPLOY_PROD_HEALTH_PORT=8081
```
---
## 7. Test-Deploy ausfuehren
```bash
/tmp/deploy-test/scripts/deploy-kbcrm-test.sh
```
Das Script:
1. Pullt den aktuellen Code (falls vom Remote geklont) oder nutzt den lokalen Stand
2. Baut beide Images (NGINX mit TypeScript-Build, Python mit Anwendungscode)
3. Startet die Container
4. Fuehrt einen Health-Check durch
Erster Build dauert einige Minuten (Node.js-Stage, npm install, tsc+rollup,
pip install). Folge-Builds sind durch Docker-Layer-Caching schneller.
Health-Check manuell:
```bash
curl http://localhost:8080/xhr/health-check
```
---
## 8. Prod-Promotion testen
Voraussetzung: der Test-Deploy wurde erfolgreich durchgefuehrt — die
Images `kbptest-pythonapp` und `kbptest-nginx-proxy` existieren.
Die Promotion braucht kein eigenes Prod-Repo — die Compose-Datei
wird aus dem test-prod-Repo gelesen.
Dann die Promotion ausfuehren:
```bash
/tmp/deploy-test/scripts/deploy-kbcrm-prod.sh
```
Das Script:
1. Fragt "yes/no" (mit "yes" bestaetigen)
2. Taggt die Images von `kbptest-*` nach `kbpprod-*`
3. Startet die Prod-Container (ohne Rebuild)
4. Health-Check
Health-Check manuell:
```bash
curl http://localhost:8081/xhr/health-check
```
---
## 9. Container pruefen
```bash
# Laufende Container anzeigen
docker compose -p kbptest --env-file /tmp/deploy-test/test-prod/.env \
-f /tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml ps
docker compose -p kbpprod --env-file /tmp/deploy-test/prod/.env \
-f /tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml ps
# Logs anzeigen
docker compose -p kbptest --env-file /tmp/deploy-test/test-prod/.env \
-f /tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml logs
docker compose -p kbpprod --env-file /tmp/deploy-test/prod/.env \
-f /tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml logs
```
---
## 10. Aufraeumen
Nach dem Testen alle Container und Images entfernen:
```bash
# Test-Container stoppen und entfernen
docker compose -p kbptest --env-file /tmp/deploy-test/test-prod/.env \
-f /tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml down
# Prod-Container stoppen und entfernen
docker compose -p kbpprod --env-file /tmp/deploy-test/prod/.env \
-f /tmp/deploy-test/test-prod/repo/docker-compose.prod.yaml down
# Images entfernen
docker rmi kbptest-pythonapp kbptest-nginx-proxy kbpprod-pythonapp kbpprod-nginx-proxy 2>/dev/null
# Verzeichnis loeschen
rm -rf /tmp/deploy-test
```
---
## 11. Typische Probleme
| Problem | Ursache | Loesung |
|---|---|---|
| `docker compose` not found | Docker Compose Plugin fehlt | `docker compose` muss als Plugin installiert sein (nicht `docker-compose`) |
| Port bereits belegt | Anderer Dienst auf 8080/8081 | Anderen Port in `.env` und `deploy.env` setzen |
| `COPY failed` im Build | `.dockerignore` schliesst benoetigte Dateien aus | `.dockerignore` im Repo-Root pruefen |
| Health-Check schlaegt fehl | Container braucht laenger als 10 Sek | `sleep 10` im Script erhoehen oder Logs pruefen |
| OIDC-Login schlaegt fehl | Redirect-URI stimmt nicht | `ZITADEL_REDIRECT_URI` in `.env` an Port anpassen |
| `git fetch` schlaegt fehl | Keine Zugangsdaten konfiguriert | Lokales Repo verwenden oder Credentials einrichten |
+97 -28
View File
@@ -58,6 +58,23 @@ Folgende Software muss installiert sein:
Docker Compose muss als Plugin installiert sein (Befehl: `docker compose`, Docker Compose muss als Plugin installiert sein (Befehl: `docker compose`,
nicht `docker-compose`). nicht `docker-compose`).
### Git-Zugangsdaten konfigurieren
Das `git clone` und `git fetch` in den Deploy-Scripts nutzen die
Git-Credentials, die auf dem Deploy-Server konfiguriert sind. Die
Zugangsdaten stehen nicht in den Scripts oder Konfigurationsdateien.
Drei Moeglichkeiten:
| Methode | Einrichtung | Wo hinterlegt |
|---|---|---|
| SSH-Key | `git clone git@git.everdc.eu:.../kbcrm` (SSH-URL verwenden) | `~/.ssh/id_rsa` auf dem Deploy-Server |
| Git-Credential-Store | `git config --global credential.helper store` | `~/.git-credentials` auf dem Deploy-Server |
| Deploy-Token (Gitea) | Token in Remote-URL: `https://token:secret@git.everdc.eu/...` | In der `origin`-URL des geklonten Repos |
Empfohlen: SSH-Key oder Deploy-Token. Der Credential-Store speichert
Passwoerter im Klartext und sollte nur fuer Test-Umgebungen verwendet werden.
--- ---
## 3. Verzeichnisstruktur auf dem Deploy-Server ## 3. Verzeichnisstruktur auf dem Deploy-Server
@@ -67,14 +84,14 @@ Es werden zwei Umgebungen (test-prod und prod) parallel betrieben:
``` ```
/opt/kbcrm/ /opt/kbcrm/
├── test-prod/ ├── test-prod/
│ ├── repo/ ← git clone des Repos │ ├── repo/ ← git clone des Repos (einziges Repo auf dem Server)
│ └── .env ← test-prod Umgebung (eigene Ports, eigene DB) │ └── .env ← test-prod Umgebung (eigene Ports, eigene DB)
├── prod/ ├── prod/
│ ├── repo/ │ └── .env ← prod Umgebung (kein eigenes Repo — Images per docker tag)
│ └── .env ← prod Umgebung
└── scripts/ └── scripts/
├── deploy-kbcrm-test.sh ← aus dem Repo (scripts/) ├── deploy-kbcrm-test.sh ← aus dem Repo (scripts/)
└── deploy-kbcrm-prod.sh ← aus dem Repo (scripts/) ├── deploy-kbcrm-prod.sh ← aus dem Repo (scripts/)
└── deploy.env ← Server-spezifische Konfiguration (nicht im Repo)
``` ```
Die SQLite-Datenbanken liegen ausserhalb des Repos in einem bind-mount Die SQLite-Datenbanken liegen ausserhalb des Repos in einem bind-mount
@@ -89,11 +106,16 @@ Die SQLite-Datenbanken liegen ausserhalb des Repos in einem bind-mount
```bash ```bash
sudo mkdir -p /opt/kbcrm/test-prod /opt/kbcrm/prod sudo mkdir -p /opt/kbcrm/test-prod /opt/kbcrm/prod
# Test-Prod Repository # Repository klonen (nur einmal — prod nutzt die Images aus test-prod)
# WICHTIG: git clone wird nur beim ersten Mal ausgefuehrt. Wenn das Verzeichnis
# bereits existiert (z.B. .env wurde bereits abgelegt), schlaegt clone fehl.
# Weitere Updates werden durch das deploy-kbcrm-test.sh Script via
# git fetch + git reset --hard durchgefuehrt — kein manueller clone mehr.
sudo git clone https://git.everdc.eu/chmln-app-dev/kbcrm /opt/kbcrm/test-prod/repo sudo git clone https://git.everdc.eu/chmln-app-dev/kbcrm /opt/kbcrm/test-prod/repo
# Prod Repository # Falls das Verzeichnis bereits existiert und aktualisiert werden soll:
sudo git clone https://git.everdc.eu/chmln-app-dev/kbcrm /opt/kbcrm/prod/repo # cd /opt/kbcrm/test-prod/repo && git fetch origin main && git reset --hard origin/main
# Oder einfach: /opt/kbcrm/scripts/deploy-kbcrm-test.sh
``` ```
### 4.2. `.env`-Dateien anlegen ### 4.2. `.env`-Dateien anlegen
@@ -104,8 +126,8 @@ Kopieren Sie die Vorlage aus dem Repo und passen Sie die Werte an:
# Test-Prod # Test-Prod
cp /opt/kbcrm/test-prod/repo/env-sample /opt/kbcrm/test-prod/.env cp /opt/kbcrm/test-prod/repo/env-sample /opt/kbcrm/test-prod/.env
# Prod # Prod (Vorlage aus test-prod-Repo)
cp /opt/kbcrm/prod/repo/env-sample /opt/kbcrm/prod/.env cp /opt/kbcrm/test-prod/repo/env-sample /opt/kbcrm/prod/.env
``` ```
### 4.3. `.env` fuer Test-Prod anpassen ### 4.3. `.env` fuer Test-Prod anpassen
@@ -194,12 +216,44 @@ zentralen Ort:
```bash ```bash
sudo mkdir -p /opt/kbcrm/scripts sudo mkdir -p /opt/kbcrm/scripts
sudo cp /opt/kbcrm/test-prod/repo/scripts/deploy-kbcrm-test.sh /opt/kbcrm/scripts/ sudo cp /opt/kbcrm/test-prod/repo/scripts/deploy-kbcrm-test.sh /opt/kbcrm/scripts/
sudo cp /opt/kbcrm/prod/repo/scripts/deploy-kbcrm-prod.sh /opt/kbcrm/scripts/ sudo cp /opt/kbcrm/test-prod/repo/scripts/deploy-kbcrm-prod.sh /opt/kbcrm/scripts/
sudo chmod +x /opt/kbcrm/scripts/*.sh sudo chmod +x /opt/kbcrm/scripts/*.sh
``` ```
Passen Sie die Pfad-Variablen am Anfang der Scripts an, falls Ihre ### 4.8. `deploy.env` anlegen (Server-spezifische Konfiguration)
Verzeichnisstruktur abweicht.
Die Deploy-Scripts laden ihre Server-spezifische Konfiguration (Pfade, Ports,
Projekt-Namen) aus einer Datei `deploy.env`, die im gleichen Verzeichnis wie
die Scripts liegen muss. Diese Datei ist **nicht Teil des Repos** und wird
nicht per `git pull` ueberschrieben.
Kopieren Sie die Vorlage und passen Sie die Werte an:
```bash
cp /opt/kbcrm/test-prod/repo/scripts/deploy.env.example /opt/kbcrm/scripts/deploy.env
vi /opt/kbcrm/scripts/deploy.env
```
Inhalt der `deploy.env`:
```bash
# --- Test-Prod Umgebung (fuer deploy-kbcrm-test.sh) ---
DEPLOY_REPO_DIR=/opt/kbcrm/test-prod/repo
DEPLOY_ENV_FILE=/opt/kbcrm/test-prod/.env
DEPLOY_PROJECT_NAME=kbptest
DEPLOY_HEALTH_PORT=8443
# --- Prod Umgebung (fuer deploy-kbcrm-prod.sh) ---
DEPLOY_PROD_COMPOSE_FILE=/opt/kbcrm/test-prod/repo/docker-compose.prod.yaml
DEPLOY_PROD_ENV_FILE=/opt/kbcrm/prod/.env
DEPLOY_PROD_PROJECT_NAME=kbpprod
DEPLOY_PROD_TEST_PROJECT_NAME=kbptest
DEPLOY_PROD_HEALTH_PORT=443
```
Wenn `deploy.env` fehlt, verwenden die Scripts ihre eingebauten Defaults
(die den obigen Werten entsprechen). Die `deploy.env` ist nur noetig, wenn
Ihre Verzeichnisstruktur oder Ports abweichen.
--- ---
@@ -291,7 +345,7 @@ Das Script fuehrt folgende Schritte aus:
Build und Python mit Anwendungscode) Build und Python mit Anwendungscode)
3. Health-Check 3. Health-Check
### 6.2. Manuelle Promotion nach Prod ### 6.2. Manuelle Promotion nach Prod (Image-Promotion)
Nach Verifikation auf dem Test-Server: Nach Verifikation auf dem Test-Server:
@@ -299,8 +353,22 @@ Nach Verifikation auf dem Test-Server:
/opt/kbcrm/scripts/deploy-kbcrm-prod.sh /opt/kbcrm/scripts/deploy-kbcrm-prod.sh
``` ```
Das Script fragt eine Bestaetigung ("yes/no") und fuehrt dann den gleichen Das Script fragt eine Bestaetigung ("yes/no") und uebernimmt dann die
Ablauf aus wie das Test-Script, jedoch mit der Prod-`.env`. getesteten Images aus test-prod per `docker tag` — kein Rebuild, kein git pull.
Prod startet mit exakt dem Stand, der in test-prod verifiziert wurde. Nur
die `.env` unterscheidet sich (Ports, Zertifikate, DB-Pfad, OIDC).
Ablauf:
1. Sicherheitsabfrage ("yes/no")
2. `docker tag` — Images von `kbptest-*` nach `kbpprod-*` kopieren
3. `docker compose up -d` — Container starten (ohne `--build`)
4. Health-Check
**Wichtig:** Das Prod-Script fuehrt kein `git pull` aus. Eine Aenderung an
der `docker-compose.prod.yaml` zwischen test-prod und Promotion wuerde
bedeuten, dass prod mit einer anderen Konfiguration startet als verifiziert.
Bei Aenderungen an der Compose-Datei muss erst ein neues test-prod-Deploy
durchgefuehrt werden.
### 6.3. Alternative: Git-Tag-basierte Promotion ### 6.3. Alternative: Git-Tag-basierte Promotion
@@ -334,34 +402,34 @@ done
```bash ```bash
# Test-Prod # Test-Prod
docker compose --env-file /opt/kbcrm/test-prod/.env \ docker compose -p kbptest --env-file /opt/kbcrm/test-prod/.env \
-f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs
# Prod # Prod
docker compose --env-file /opt/kbcrm/prod/.env \ docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
-f /opt/kbcrm/prod/repo/docker-compose.prod.yaml logs -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs
# Nur Python-Container # Nur Python-Container
docker compose --env-file /opt/kbcrm/prod/.env \ docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
-f /opt/kbcrm/prod/repo/docker-compose.prod.yaml logs pythonapp -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs pythonapp
# Nur NGINX-Container # Nur NGINX-Container
docker compose --env-file /opt/kbcrm/prod/.env \ docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
-f /opt/kbcrm/prod/repo/docker-compose.prod.yaml logs nginx-proxy -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml logs nginx-proxy
``` ```
### Container-Status pruefen ### Container-Status pruefen
```bash ```bash
docker compose --env-file /opt/kbcrm/prod/.env \ docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
-f /opt/kbcrm/prod/repo/docker-compose.prod.yaml ps -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml ps
``` ```
### Manuell in den Container wechseln ### Manuell in den Container wechseln
```bash ```bash
docker compose --env-file /opt/kbcrm/prod/.env \ docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
-f /opt/kbcrm/prod/repo/docker-compose.prod.yaml exec pythonapp bash -f /opt/kbcrm/test-prod/repo/docker-compose.prod.yaml exec pythonapp bash
``` ```
### Rollback ### Rollback
@@ -370,10 +438,10 @@ Bei Problemen kann auf einen frueheren Stand zurueckgekehrt werden:
```bash ```bash
# Auf dem Deploy-Server: # Auf dem Deploy-Server:
cd /opt/kbcrm/prod/repo cd /opt/kbcrm/test-prod/repo
git log --oneline -10 # frueheren Commit finden git log --oneline -10 # frueheren Commit finden
git checkout <commit-hash> git checkout <commit-hash>
docker compose --env-file /opt/kbcrm/prod/.env \ docker compose -p kbpprod --env-file /opt/kbcrm/prod/.env \
-f docker-compose.prod.yaml up -d --build -f docker-compose.prod.yaml up -d --build
``` ```
@@ -425,6 +493,7 @@ docker image prune -f
- [ ] Datenbank-Verzeichnisse erstellt (`PYTHON_DB_DIR`) - [ ] Datenbank-Verzeichnisse erstellt (`PYTHON_DB_DIR`)
- [ ] SSL-Zertifikate abgelegt (`NGINX_CERTS`) - [ ] SSL-Zertifikate abgelegt (`NGINX_CERTS`)
- [ ] Deploy-Scripts kopiert und ausfuehrbar gemacht - [ ] Deploy-Scripts kopiert und ausfuehrbar gemacht
- [ ] `deploy.env` angelegt und angepasst (Pfade, Ports, Projekt-Namen)
- [ ] Git-Hook oder Webhook eingerichtet (fuer automatisches Test-Deploy) - [ ] Git-Hook oder Webhook eingerichtet (fuer automatisches Test-Deploy)
- [ ] Erster manueller Deploy-Test: `deploy-kbcrm-test.sh` - [ ] Erster manueller Deploy-Test: `deploy-kbcrm-test.sh`
- [ ] Health-Check erfolgreich: `curl -k https://localhost:8443/health-check` - [ ] Health-Check erfolgreich: `curl -k https://localhost:8443/health-check`
+3 -1
View File
@@ -1,4 +1,6 @@
name: kbpprod # Kein name:-Feld — Projekt-Name wird per -p in den Deploy-Scripts gesetzt
# (kbptest fuer test-prod, kbpprod fuer prod), damit beide Umgebungen
# parallel auf dem gleichen Server laufen koennen.
services: services:
pythonapp: pythonapp:
extends: extends:
Regular → Executable
+51 -16
View File
@@ -1,14 +1,45 @@
#!/bin/bash #!/bin/bash
# Deploy-Script: Promotion nach PROD (manuelles OK erforderlich) # Deploy-Script: Promotion nach PROD (manuelles OK erforderlich)
# Wird manuell aufgerufen, nachdem test-prod verifiziert wurde. # Wird manuell aufgerufen, nachdem test-prod verifiziert wurde.
# Uebernimmt die getesteten Images aus test-prod per docker tag (kein Rebuild,
# kein git pull). Die Compose-Datei und .env werden nicht aktualisiert —
# prod startet mit exakt dem Stand, der in test-prod verifiziert wurde.
# Voraussetzungen auf dem Server: git, docker, docker compose. # Voraussetzungen auf dem Server: git, docker, docker compose.
#
# Server-spezifische Konfiguration (Pfade, Ports, Projekt-Namen) wird aus
# einer Datei deploy.env geladen, die im gleichen Verzeichnis wie dieses
# Script liegen muss. Diese Datei ist NICHT Teil des Repos und wird nicht
# per git pull ueberschrieben. Die Defaults unten greifen, falls deploy.env
# fehlt oder einzelne Werte nicht setzt.
set -euo pipefail set -euo pipefail
# Konfiguration — an die jeweilige Server-Umgebung anpassen # === Script-Verzeichnis ermitteln (fuer deploy.env) ===
REPO_DIR=/opt/kbcrm-prod/repo SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE=docker-compose.prod.yaml
ENV_FILE=/opt/kbcrm-prod/.env # === Server-Konfiguration laden (ausserhalb des Repos, nicht per git pull ueberschreibbar) ===
PROD_PORT=443 DEPLOY_CONFIG="$SCRIPT_DIR/deploy.env"
if [ -f "$DEPLOY_CONFIG" ]; then
source "$DEPLOY_CONFIG"
fi
# === Defaults — werden verwendet, falls deploy.env fehlt oder Werte nicht setzt ===
# COMPOSE_FILE verweist auf das test-prod-Repo — die Compose-Datei wurde
# dort verifiziert und soll in prod identisch sein. Prod hat kein eigenes Repo.
COMPOSE_FILE="${DEPLOY_PROD_COMPOSE_FILE:-/opt/kbcrm/test-prod/repo/docker-compose.prod.yaml}"
ENV_FILE="${DEPLOY_PROD_ENV_FILE:-/opt/kbcrm/prod/.env}"
PROJECT_NAME="${DEPLOY_PROD_PROJECT_NAME:-kbpprod}"
TEST_PROJECT_NAME="${DEPLOY_PROD_TEST_PROJECT_NAME:-kbptest}"
HEALTH_PORT="${DEPLOY_PROD_HEALTH_PORT:-443}"
# === NGINX_SERVER_NAME aus .env laden (fuer Health-Check Host-Header) ===
# Die .env wird per --env-file an Docker Compose uebergeben, aber die Variablen
# stehen nicht als Shell-Variablen im Script zur Verfuegung. Daher laden wir
# NGINX_SERVER_NAME manuell, damit der Health-Check den richtigen Host-Header
# sendet (sonst liefert NGINX 404 wegen server_name-Mismatch).
NGINX_SERVER_NAME=""
if [ -f "$ENV_FILE" ]; then
NGINX_SERVER_NAME=$(grep -E "^NGINX_SERVER_NAME=" "$ENV_FILE" | cut -d= -f2- | tr -d '\r\n')
fi
echo "=== Deploy KB-CRM PROD ===" echo "=== Deploy KB-CRM PROD ==="
@@ -20,25 +51,29 @@ if [ "$confirmation" != "yes" ]; then
exit 1 exit 1
fi fi
# 1. Code aktualisieren # 1. Getestete Images aus test-prod uebernehmen (kein Rebuild, kein git pull)
cd "$REPO_DIR" # docker tag erzeugt eine zusaetzliche Referenz auf das gleiche Image —
echo "=== Pulling latest code ===" # die Original-Tags (kbptest-*) bleiben unangetastet.
git fetch origin main echo "=== Promoting tested images from test-prod to prod ==="
git checkout main docker tag "${TEST_PROJECT_NAME}-pythonapp" "${PROJECT_NAME}-pythonapp"
git reset --hard origin/main docker tag "${TEST_PROJECT_NAME}-nginx-proxy" "${PROJECT_NAME}-nginx-proxy"
# 2. Images neu bauen und Container starten # 2. Container starten (ohne --build — Images wurden per tag uebernommen)
echo "=== Building and starting containers ===" # -p setzt den Projekt-Namen (fuer Container-Namen)
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d --build # --env-file laedt die Container-Variablen (Ports, Zertifikate, DB, OIDC)
# -f gibt die Compose-Datei explizit an (ignoriert COMPOSE_FILE aus .env)
echo "=== Starting prod containers ==="
docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" \
-f "$COMPOSE_FILE" up -d
# 3. Health-Check # 3. Health-Check
echo "=== Waiting for container startup ===" echo "=== Waiting for container startup ==="
sleep 10 sleep 10
if curl -sf "http://localhost:${PROD_PORT}/health-check" >/dev/null 2>&1; then if curl -sf -H "Host: ${NGINX_SERVER_NAME}" "http://localhost:${HEALTH_PORT}/xhr/health-check" >/dev/null 2>&1; then
echo "Health check: OK" echo "Health check: OK"
else else
echo "WARNING: Health check failed — check container logs with:" echo "WARNING: Health check failed — check container logs with:"
echo " docker compose --env-file $ENV_FILE -f $COMPOSE_FILE logs" echo " docker compose -p $PROJECT_NAME --env-file $ENV_FILE -f $COMPOSE_FILE logs"
fi fi
echo "=== Deploy PROD complete ===" echo "=== Deploy PROD complete ==="
Regular → Executable
+37 -7
View File
@@ -2,13 +2,39 @@
# Deploy-Script: Automatisches Deployment nach TEST (test-prod) # Deploy-Script: Automatisches Deployment nach TEST (test-prod)
# Wird durch Git-Hook/Webhook oder manuell aufgerufen. # Wird durch Git-Hook/Webhook oder manuell aufgerufen.
# Voraussetzungen auf dem Server: git, docker, docker compose. # Voraussetzungen auf dem Server: git, docker, docker compose.
#
# Server-spezifische Konfiguration (Pfade, Ports, Projekt-Namen) wird aus
# einer Datei deploy.env geladen, die im gleichen Verzeichnis wie dieses
# Script liegen muss. Diese Datei ist NICHT Teil des Repos und wird nicht
# per git pull ueberschrieben. Die Defaults unten greifen, falls deploy.env
# fehlt oder einzelne Werte nicht setzt.
set -euo pipefail set -euo pipefail
# Konfiguration — an die jeweilige Server-Umgebung anpassen # === Script-Verzeichnis ermitteln (fuer deploy.env) ===
REPO_DIR=/opt/kbcrm-test/repo SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# === Server-Konfiguration laden (ausserhalb des Repos, nicht per git pull ueberschreibbar) ===
DEPLOY_CONFIG="$SCRIPT_DIR/deploy.env"
if [ -f "$DEPLOY_CONFIG" ]; then
source "$DEPLOY_CONFIG"
fi
# === Defaults — werden verwendet, falls deploy.env fehlt oder Werte nicht setzt ===
REPO_DIR="${DEPLOY_REPO_DIR:-/opt/kbcrm/test-prod/repo}"
COMPOSE_FILE=docker-compose.prod.yaml COMPOSE_FILE=docker-compose.prod.yaml
ENV_FILE=/opt/kbcrm-test/.env ENV_FILE="${DEPLOY_ENV_FILE:-/opt/kbcrm/test-prod/.env}"
TEST_PORT=8443 PROJECT_NAME="${DEPLOY_PROJECT_NAME:-kbptest}"
HEALTH_PORT="${DEPLOY_HEALTH_PORT:-8443}"
# === NGINX_SERVER_NAME aus .env laden (fuer Health-Check Host-Header) ===
# Die .env wird per --env-file an Docker Compose uebergeben, aber die Variablen
# stehen nicht als Shell-Variablen im Script zur Verfuegung. Daher laden wir
# NGINX_SERVER_NAME manuell, damit der Health-Check den richtigen Host-Header
# sendet (sonst liefert NGINX 404 wegen server_name-Mismatch).
NGINX_SERVER_NAME=""
if [ -f "$ENV_FILE" ]; then
NGINX_SERVER_NAME=$(grep -E "^NGINX_SERVER_NAME=" "$ENV_FILE" | cut -d= -f2- | tr -d '\r\n')
fi
echo "=== Deploy KB-CRM TEST ===" echo "=== Deploy KB-CRM TEST ==="
@@ -20,17 +46,21 @@ git checkout main
git reset --hard origin/main git reset --hard origin/main
# 2. Images neu bauen und Container starten # 2. Images neu bauen und Container starten
# -p setzt den Projekt-Namen (fuer Image-Namen und Container-Namen)
# --env-file laedt die Container-Variablen (Ports, Zertifikate, DB, OIDC)
# -f gibt die Compose-Datei explizit an (ignoriert COMPOSE_FILE aus .env)
echo "=== Building and starting containers ===" echo "=== Building and starting containers ==="
docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" up -d --build docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" \
-f "$COMPOSE_FILE" up -d --build
# 3. Health-Check # 3. Health-Check
echo "=== Waiting for container startup ===" echo "=== Waiting for container startup ==="
sleep 10 sleep 10
if curl -sf "http://localhost:${TEST_PORT}/health-check" >/dev/null 2>&1; then if curl -sf -H "Host: ${NGINX_SERVER_NAME}" "http://localhost:${HEALTH_PORT}/xhr/health-check" >/dev/null 2>&1; then
echo "Health check: OK" echo "Health check: OK"
else else
echo "WARNING: Health check failed — check container logs with:" echo "WARNING: Health check failed — check container logs with:"
echo " docker compose --env-file $ENV_FILE -f $COMPOSE_FILE logs" echo " docker compose -p $PROJECT_NAME --env-file $ENV_FILE -f $COMPOSE_FILE logs"
fi fi
echo "=== Deploy TEST complete ===" echo "=== Deploy TEST complete ==="
+23
View File
@@ -0,0 +1,23 @@
# =============================================================================
# deploy.env — Server-spezifische Konfiguration fuer die Deploy-Scripts
# =============================================================================
# Diese Datei liegt NICHT im Git-Repo und wird NICHT per git pull ueberschrieben.
# Kopieren Sie diese Vorlage nach deploy.env (im gleichen Verzeichnis wie die
# Scripts) und passen Sie die Werte an Ihre Server-Umgebung an.
#
# Wenn deploy.env fehlt, verwenden die Scripts ihre eingebauten Defaults.
# =============================================================================
# --- Test-Prod Umgebung (fuer deploy-kbcrm-test.sh) ---
DEPLOY_REPO_DIR=/opt/kbcrm/test-prod/repo
DEPLOY_ENV_FILE=/opt/kbcrm/test-prod/.env
DEPLOY_PROJECT_NAME=kbptest
DEPLOY_HEALTH_PORT=8443
# --- Prod Umgebung (fuer deploy-kbcrm-prod.sh) ---
# COMPOSE_FILE muss auf das test-prod-Repo verweisen (prod hat kein eigenes Repo)
DEPLOY_PROD_COMPOSE_FILE=/opt/kbcrm/test-prod/repo/docker-compose.prod.yaml
DEPLOY_PROD_ENV_FILE=/opt/kbcrm/prod/.env
DEPLOY_PROD_PROJECT_NAME=kbpprod
DEPLOY_PROD_TEST_PROJECT_NAME=kbptest
DEPLOY_PROD_HEALTH_PORT=443