refactor: enhance environment configuration and CI workflow for improved flexibility
Some checks failed
Deploy Development / deploy (push) Successful in 37s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 6s
Test Suite / playwright-tests (push) Failing after 1m29s

- Updated .env.example to provide clearer instructions and examples for production and development environments.
- Refactored docker-compose files to utilize environment variables for database and application settings, improving configurability.
- Enhanced .gitea/workflows/test.yml to streamline E2E testing setup, ensuring consistent handling of environment variables across different stages.
This commit is contained in:
Lars 2026-04-29 13:33:28 +02:00
parent f4a86f9726
commit fe86d763ad
5 changed files with 91 additions and 78 deletions

View File

@ -1,49 +1,56 @@
# === Deploy (.env auf dem Host) ============================================
# Kopiere diese Datei nach `.env` im SELBEN Verzeichnis wie `docker-compose.yml` (Pi: ~/docker/shinkan).
# Docker Compose liest `.env` beim Start — dadurch wird z. B. SMTP_HOST=${SMTP_HOST} gefüllt.
# Ist die Datei weg/leer oder steht SMTP_PASS nicht darin → im Container keine SMTP-Daten ([SMTP] nicht konfiguriert).
# === .env neben der Jeweiligen docker-compose.*.yml kopieren ====================
# Docker Compose ersetzt ${VARIABLE} beim Start.
#
# Pro Umgebung eigene Datei (z.B. ~/docker/shinkan/.env für Prod,
# ~/docker/shinkan-dev/.env für Dev) — dieselben SCHLÜSSEL, unterschiedliche Werte.
# Kein separates DEV_APP_URL vs APP_URL: immer APP_URL, ALLOWED_ORIGINS, DB_*, …
# Database
# ─── Typische Werte PROD (docker-compose.yml) ─────────────────────────────────
# DB_NAME=shinkan
# DB_USER=shinkan_user
# DB_PASSWORD=…
# APP_URL=https://shinkan.jinkendo.de
# ALLOWED_ORIGINS=https://shinkan.jinkendo.de
# ENVIRONMENT=production
# ─── Typische Werte DEV (docker-compose.dev-env.yml) ─────────────────────────
# DB_NAME=shinkan_dev
# DB_USER=shinkan_dev
# DB_PASSWORD=dev_password
# APP_URL=https://dev.shinkan.jinkendo.de
# ALLOWED_ORIGINS=https://dev.shinkan.jinkendo.de,http://192.168.2.49:3098
# ENVIRONMENT=development
# ─── Ab hier: eine ausfüllbare Vorlage (bei uns meist Prod-Defaults) ───────────
DB_HOST=postgres
DB_PORT=5432
DB_NAME=shinkan
DB_USER=shinkan_user
DB_PASSWORD=CHANGE_ME_SECURE_PASSWORD
# OpenRouter (KI - optional)
OPENROUTER_API_KEY=your_api_key_here
OPENROUTER_MODEL=anthropic/claude-sonnet-4
# SMTP (E-Mail)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=noreply@jinkendo.de
SMTP_PASS=your_smtp_password
SMTP_FROM=noreply@jinkendo.de
# Port 465 oft SSL; dann z. B. SMTP_SSL=true und SMTP_STARTTLS=false
SMTP_SSL=
SMTP_STARTTLS=
# Bootstrap: erste Registrierung (leere Nutzerliste) erhält Rolle admin; oder feste Admin-Mails:
AUTO_ADMIN_FIRST_USER=true
ADMIN_BOOTSTRAP_EMAILS=
# App
APP_URL=https://shinkan.jinkendo.de
# Kommasepariert (ohne Leerzeichen um die Kommas ist am sichersten). Für Dev mehrere Origins nötig (HTTPS + LAN).
ALLOWED_ORIGINS=https://shinkan.jinkendo.de
ENVIRONMENT=production
# Nur docker-compose.dev-env.yml (optional): DEV_APP_URL, DEV_ALLOWED_ORIGINS
# Media Storage
MEDIA_DIR=/app/media
# MediaWiki Import (Semantic MediaWiki)
MEDIAWIKI_API_URL=https://karatetrainer.net/api.php
MEDIAWIKI_USER=Jinkendo
MEDIAWIKI_PASSWORD=CHANGE_ME
# Kategorienamen im Wiki (echte Namen von karatetrainer.net)
MEDIAWIKI_CATEGORY_EXERCISES=Übungen
MEDIAWIKI_CATEGORY_SKILLS=Fähigkeitsbeschreibung
MEDIAWIKI_CATEGORY_METHODS=Methodenbeschreibung

View File

@ -57,9 +57,11 @@ jobs:
playwright-tests:
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
# Lokal: docker-compose.dev-env + COMPOSE_PROJECT_NAME. Nach „Deploy Production“: HTTPS-Prod (kein Docker).
# Kein zusätzlicher docker-compose im Job: keine zweiten Container/Host-Ports.
# Dev: Tests gegen bereits deployte URL (HTTPS, Reverse-Proxy). Prod: gleicher Ablauf.
env:
COMPOSE_PROJECT_NAME: shinkan-e2e-${{ github.run_id }}
# Öffentliche Dev-Basis — muss ALLOWED_ORIGINS / Nginx entsprechen; bei anderer Domain Workflow anpassen.
E2E_TARGET_URL: https://dev.shinkan.jinkendo.de
steps:
- name: Checkout repository
uses: actions/checkout@v4
@ -69,41 +71,39 @@ jobs:
with:
node-version: '20'
- name: E2E-Ziel wählen (lokal vs. Production)
- name: E2E-Ziel wählen (Dev über Proxy vs. Production)
id: e2e
run: |
EVENT="${{ github.event_name }}"
WF_NAME="${{ github.event.workflow_run.name }}"
DEV_BASE="${{ env.E2E_TARGET_URL }}"
if [ "$EVENT" = "workflow_run" ] && [ "$WF_NAME" = "Deploy Production" ]; then
echo "mode=prod" >> $GITHUB_OUTPUT
echo "base_url=https://shinkan.jinkendo.de" >> $GITHUB_OUTPUT
echo "→ Playwright gegen Production. Login: Secrets E2E_PROD_TEST_EMAIL / E2E_PROD_TEST_PASSWORD."
echo "→ Prod. Secrets: E2E_PROD_TEST_EMAIL / E2E_PROD_TEST_PASSWORD."
else
echo "mode=local" >> $GITHUB_OUTPUT
echo "base_url=http://127.0.0.1:3098" >> $GITHUB_OUTPUT
echo "→ Playwright gegen lokalen Stack (docker-compose.dev-env.yml)."
echo "mode=dev" >> $GITHUB_OUTPUT
echo "base_url=${DEV_BASE}" >> $GITHUB_OUTPUT
echo "→ Deployte Dev-Umgebung (${DEV_BASE}). Secrets: E2E_DEV_TEST_EMAIL / E2E_DEV_TEST_PASSWORD."
fi
- name: Start dev stack for E2E (nur lokal)
if: ${{ steps.e2e.outputs.mode == 'local' }}
env:
DEV_ALLOWED_ORIGINS: http://127.0.0.1:3098,http://localhost:3098,http://host.docker.internal:3098,https://dev.shinkan.jinkendo.de
- name: Dev /health abwarten
if: ${{ steps.e2e.outputs.mode == 'dev' }}
run: |
docker compose -f docker-compose.dev-env.yml up -d --build
echo "Warte auf Frontend + API (/health) …"
BASE="${{ steps.e2e.outputs.base_url }}"
echo "Warte auf $BASE/health …"
for i in $(seq 1 90); do
if curl -sf http://127.0.0.1:3098/health >/dev/null 2>&1; then
if curl -sf "$BASE/health" >/dev/null 2>&1; then
echo "Health OK (Versuch $i)"
exit 0
fi
sleep 2
done
echo "Timeout: /health nicht erreichbar"
curl -v http://127.0.0.1:3098/health || true
docker compose -f docker-compose.dev-env.yml logs --tail=120
echo "Timeout: Dev /health nicht erreichbar — Deploy / DNS / Firewall prüfen."
curl -v "$BASE/health" || true
exit 1
- name: Prod /health abwarten (nach Deploy)
- name: Prod /health abwarten
if: ${{ steps.e2e.outputs.mode == 'prod' }}
run: |
BASE="${{ steps.e2e.outputs.base_url }}"
@ -119,16 +119,21 @@ jobs:
curl -v "$BASE/health" || true
exit 1
- name: Seed E2E-User (nur lokaler Stack, frische DB)
if: ${{ steps.e2e.outputs.mode == 'local' }}
- name: Testnutzer registrieren (Dev, nur wenn möglich)
if: ${{ steps.e2e.outputs.mode == 'dev' }}
env:
TEST_EMAIL: lars@stommer.com
TEST_PASSWORD: 12345678
E2E_DEV_TEST_EMAIL: ${{ secrets.E2E_DEV_TEST_EMAIL }}
E2E_DEV_TEST_PASSWORD: ${{ secrets.E2E_DEV_TEST_PASSWORD }}
run: |
curl -sf -X POST "http://127.0.0.1:3098/api/auth/register" \
BASE="${{ steps.e2e.outputs.base_url }}"
if [ -z "$E2E_DEV_TEST_EMAIL" ] || [ -z "$E2E_DEV_TEST_PASSWORD" ]; then
echo "(Registrierung übersprungen — Secrets E2E_DEV_* nicht gesetzt.)"
exit 0
fi
curl -sf -X POST "$BASE/api/auth/register" \
-H "Content-Type: application/json" \
-d "{\"email\":\"${TEST_EMAIL}\",\"password\":\"${TEST_PASSWORD}\",\"name\":\"Playwright CI\"}" \
|| echo "(Registrierung übersprungen — Nutzer existiert evtl. schon)"
-d "{\"email\":\"${E2E_DEV_TEST_EMAIL}\",\"password\":\"${E2E_DEV_TEST_PASSWORD}\",\"name\":\"Playwright CI\"}" \
|| echo "(Register evtl. schon erfolgt oder Limits — Login-Test gilt trotzdem.)"
- name: Install Playwright
run: |
@ -136,33 +141,35 @@ jobs:
npx playwright install --with-deps chromium
- name: Run Playwright tests
env:
E2E_DEV_TEST_EMAIL: ${{ secrets.E2E_DEV_TEST_EMAIL }}
E2E_DEV_TEST_PASSWORD: ${{ secrets.E2E_DEV_TEST_PASSWORD }}
run: |
set -e
MODE="${{ steps.e2e.outputs.mode }}"
BASE_URL="${{ steps.e2e.outputs.base_url }}"
export PLAYWRIGHT_BASE_URL="$BASE_URL"
if [ "$MODE" = "prod" ]; then
export PLAYWRIGHT_BASE_URL="$BASE_URL"
export TEST_EMAIL="${{ secrets.E2E_PROD_TEST_EMAIL }}"
export TEST_PASSWORD="${{ secrets.E2E_PROD_TEST_PASSWORD }}"
if [ -z "$TEST_EMAIL" ] || [ -z "$TEST_PASSWORD" ]; then
echo "Fehler: Für Prod-E2E Repository-Secrets E2E_PROD_TEST_EMAIL und E2E_PROD_TEST_PASSWORD setzen (Testnutzer mit Login auf Prod)."
echo "Fehler: E2E_PROD_TEST_EMAIL und E2E_PROD_TEST_PASSWORD setzen."
exit 1
fi
else
export PLAYWRIGHT_BASE_URL="$BASE_URL"
export TEST_EMAIL="lars@stommer.com"
export TEST_PASSWORD="12345678"
export TEST_EMAIL="$E2E_DEV_TEST_EMAIL"
export TEST_PASSWORD="$E2E_DEV_TEST_PASSWORD"
if [ -z "$TEST_EMAIL" ] || [ -z "$TEST_PASSWORD" ]; then
echo "Fehler: E2E_DEV_TEST_EMAIL und E2E_DEV_TEST_PASSWORD setzen (Playwright soll gegen Dev einloggen)."
exit 1
fi
fi
mkdir -p screenshots
npx playwright test
echo "✓ Playwright tests passed"
- name: Stop dev stack
if: ${{ always() && steps.e2e.outputs.mode == 'local' }}
run: |
docker compose -f docker-compose.dev-env.yml down --remove-orphans
echo "Gestoppt unter Projekt: ${COMPOSE_PROJECT_NAME}"
- name: Upload test screenshots
if: failure()
uses: actions/upload-artifact@v4

View File

@ -1,15 +1,15 @@
version: '3.8'
# Keine festen container_name: Namen sind hostweit eindeutig und kollidieren bei
# erneuten Deploys / anderem Compose-Projektprefix. Compose vergibt z. B. <projekt>-postgres-1.
# Keine festen container_name — Compose-Namen haben Projektprefix (<projekt>-postgres-1).
# Gleiche Variablennamen wie docker-compose.yml; andere Werte in einer eigenen .env neben dieser Datei.
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: shinkan_dev
POSTGRES_USER: shinkan_dev
POSTGRES_PASSWORD: dev_password
POSTGRES_DB: "${DB_NAME:-shinkan_dev}"
POSTGRES_USER: "${DB_USER:-shinkan_dev}"
POSTGRES_PASSWORD: "${DB_PASSWORD:-dev_password}"
volumes:
- dev-shinkan-db-data:/var/lib/postgresql/data
ports:
@ -25,9 +25,9 @@ services:
environment:
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: shinkan_dev
DB_USER: shinkan_dev
DB_PASSWORD: dev_password
DB_NAME: "${DB_NAME:-shinkan_dev}"
DB_USER: "${DB_USER:-shinkan_dev}"
DB_PASSWORD: "${DB_PASSWORD:-dev_password}"
OPENROUTER_API_KEY: ${OPENROUTER_API_KEY}
OPENROUTER_MODEL: ${OPENROUTER_MODEL}
SMTP_HOST: ${SMTP_HOST}
@ -35,18 +35,16 @@ services:
SMTP_USER: ${SMTP_USER}
SMTP_PASS: ${SMTP_PASS}
SMTP_FROM: ${SMTP_FROM}
# Öffentliche Dev-URL (E-Mail-Links); lokaler Zugriff per IP bleibt über ALLOWED_ORIGINS möglich
APP_URL: "${DEV_APP_URL:-https://dev.shinkan.jinkendo.de}"
# Login/Register vom Browser: HTTPS-Subdomain und optional LAN-IP (Compose überschreibbar per .env)
ALLOWED_ORIGINS: "${DEV_ALLOWED_ORIGINS:-https://dev.shinkan.jinkendo.de,http://192.168.2.49:3098}"
ENVIRONMENT: development
MEDIAWIKI_API_URL: https://karatetrainer.net/api.php
MEDIAWIKI_USER: Jinkendo
MEDIAWIKI_PASSWORD: Jinkendo6970
MEDIAWIKI_CATEGORY_EXERCISES: Übungen
MEDIAWIKI_CATEGORY_SKILLS: Fähigkeitsbeschreibung
MEDIAWIKI_CATEGORY_METHODS: Methodenbeschreibung
MEDIAWIKI_CATEGORY_MODELS: Reifegradmodelle
APP_URL: "${APP_URL:-https://dev.shinkan.jinkendo.de}"
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://dev.shinkan.jinkendo.de,http://192.168.2.49:3098}"
ENVIRONMENT: "${ENVIRONMENT:-development}"
MEDIAWIKI_API_URL: "${MEDIAWIKI_API_URL:-https://karatetrainer.net/api.php}"
MEDIAWIKI_USER: "${MEDIAWIKI_USER:-Jinkendo}"
MEDIAWIKI_PASSWORD: "${MEDIAWIKI_PASSWORD:-CHANGE_ME}"
MEDIAWIKI_CATEGORY_EXERCISES: "${MEDIAWIKI_CATEGORY_EXERCISES:-Übungen}"
MEDIAWIKI_CATEGORY_SKILLS: "${MEDIAWIKI_CATEGORY_SKILLS:-Fähigkeitsbeschreibung}"
MEDIAWIKI_CATEGORY_METHODS: "${MEDIAWIKI_CATEGORY_METHODS:-Methodenbeschreibung}"
MEDIAWIKI_CATEGORY_MODELS: "${MEDIAWIKI_CATEGORY_MODELS:-Reifegradmodelle}"
volumes:
- dev-shinkan-media:/app/media
ports:

View File

@ -5,8 +5,8 @@ services:
image: postgres:16-alpine
container_name: shinkan-db-prod
environment:
POSTGRES_DB: shinkan
POSTGRES_USER: shinkan_user
POSTGRES_DB: "${DB_NAME:-shinkan}"
POSTGRES_USER: "${DB_USER:-shinkan_user}"
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- shinkan-db-data:/var/lib/postgresql/data
@ -40,9 +40,10 @@ services:
# Erste Self-Registration → Admin; oder ADMIN_BOOTSTRAP_EMAILS=mail@…,weitere@…
AUTO_ADMIN_FIRST_USER: "${AUTO_ADMIN_FIRST_USER:-true}"
ADMIN_BOOTSTRAP_EMAILS: "${ADMIN_BOOTSTRAP_EMAILS:-}"
APP_URL: https://shinkan.jinkendo.de
ALLOWED_ORIGINS: https://shinkan.jinkendo.de
ENVIRONMENT: production
# Werte wie in .env (APP_URL, ALLOWED_ORIGINS, ENVIRONMENT) — keine zweite „Wahrheit“ in YAML
APP_URL: "${APP_URL:-https://shinkan.jinkendo.de}"
ALLOWED_ORIGINS: "${ALLOWED_ORIGINS:-https://shinkan.jinkendo.de}"
ENVIRONMENT: "${ENVIRONMENT:-production}"
volumes:
- shinkan-media:/app/media
ports:

View File

@ -1,4 +1,4 @@
// CI: PLAYWRIGHT_BASE_URL — lokal docker dev-env (:3098); nach „Deploy Production“ https://shinkan.jinkendo.de
// CI: PLAYWRIGHT_BASE_URL = öffentliche Dev-/Prod-URL (HTTPS über Reverse Proxy), nicht localhost
// Lokal: export PLAYWRIGHT_BASE_URL=http://192.168.x.x:3098
const rawBase =