From 13efe9406ca104f7cae9e8775389d288f7ad60d3 Mon Sep 17 00:00:00 2001 From: madgerm Date: Tue, 3 Feb 2026 09:13:03 +0100 Subject: [PATCH] Add worktree sandbox and test env workflow --- .env.example | 4 +- AGENTS.md | 8 +++ README.md | 6 +++ docker-compose.yml | 6 +-- docs/README.md | 31 +++++++++++ scripts/test-env-up.sh | 101 ++++++++++++++++++++++++++++++++++++ scripts/worktree-destroy.sh | 33 ++++++++++++ scripts/worktree-down.sh | 30 +++++++++++ scripts/worktree-init.sh | 31 +++++++++++ scripts/worktree-sync.sh | 50 ++++++++++++++++++ scripts/worktree-up.sh | 30 +++++++++++ 11 files changed, 326 insertions(+), 4 deletions(-) create mode 100755 scripts/test-env-up.sh create mode 100755 scripts/worktree-destroy.sh create mode 100755 scripts/worktree-down.sh create mode 100755 scripts/worktree-init.sh create mode 100755 scripts/worktree-sync.sh create mode 100755 scripts/worktree-up.sh diff --git a/.env.example b/.env.example index 0fd6f54..59061b4 100644 --- a/.env.example +++ b/.env.example @@ -7,4 +7,6 @@ DB_PORT=change-me DB_NAME=change-me DB_TEST_NAME=change-me DB_USER=change-me -DB_PASS=change-me +DB_PASS=your-strong-password + +# Nur Platzhalter. Vor Prod aendern. diff --git a/AGENTS.md b/AGENTS.md index 392bf9c..2e63166 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,3 +15,11 @@ ## Sprache - Alle Agenten-Ausgaben und Kommentare sind **Deutsch**, außer der User verlangt explizit eine andere Sprache. + +## Lokaler Test-Workflow (empfohlen) +- Sandbox-Worktree verwenden: `scripts/worktree-init.sh`, `scripts/worktree-up.sh`, `scripts/worktree-down.sh`, `scripts/worktree-destroy.sh`. +- Docker laeuft nur in der Sandbox; das Haupt-Repo bleibt frei von Logs/Uploads/DB-Daten. +- Optional: `scripts/worktree-sync.sh` uebernimmt uncommitted Aenderungen per Patch. + +## Secrets +- Keine Secrets ins Repo. Nur Platzhalter in `.env.example` (z. B. `change-me` / `your-strong-password`) und Hinweis, dass vor Prod zu aendern ist. diff --git a/README.md b/README.md index 39bda79..e0550f4 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ Alles, was später auf den Webserver gehört, lebt unter `web/`. Die anderen Ord - `config/`: JSON-Konfigurationen (Planetklassen, Rassen, Gebäude-Blueprints). ## Entwicklung +Schnellstart (Testumgebung, Worktree-Sandbox): +1. `bash scripts/test-env-up.sh` + +Manueller Ablauf: 1. `cp .env.example .env` 2. `docker compose up -d` 3. `psql -h 127.0.0.1 -U -c \"CREATE DATABASE \"` (nur einmalig) @@ -22,9 +26,11 @@ Alles, was später auf den Webserver gehört, lebt unter `web/`. Die anderen Ord 7. `vendor/bin/phpunit` 8. `php -S localhost:8000 -t web/desktop/public` 9. Öffne `http://localhost:8000/index.php?s=overview&p=dashboard` (für mobile Tests `-t web/mobile/public`). +10. Beim Start wird `/api/state` geladen; die Ressourcen in der Leiste aktualisieren sich automatisch. ## Konfiguration (.env) Die Datei `.env.example` enthält **Platzhalter** (`change-me`). Diese Werte **müssen** vor einem Produktivbetrieb ersetzt werden. +Auch Default-Credentials im Seed (z.B. User `dev` mit Passwort `change-me`) sind **nur Platzhalter** und müssen vor Prod geändert werden. Benötigte Variablen: - `APP_ENV` diff --git a/docker-compose.yml b/docker-compose.yml index b5c7158..624d4bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,9 +5,9 @@ services: ports: - "5432:5432" environment: - POSTGRES_DB: galaxyforge - POSTGRES_USER: galaxyforge - POSTGRES_PASSWORD: galaxyforge + POSTGRES_DB: ${DB_NAME:-change-me} + POSTGRES_USER: ${DB_USER:-change-me} + POSTGRES_PASSWORD: ${DB_PASS:-your-strong-password} volumes: - pgdata:/var/lib/postgresql/data volumes: diff --git a/docs/README.md b/docs/README.md index f6f049a..7d1c015 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,37 @@ Diese Demo zeigt eine HUD-artige Navigation mit statischem Hauptmenü, kontextab 2. `php -S localhost:8000 -t web/desktop/public` (setzt die Dokumentenwurzel auf den Desktop-Build). 3. Öffne `http://localhost:8000/index.php` und wechsle z. B. über `?s=build&p=demolish` die Sections oder wechsle mit `-t web/mobile/public` zur mobilen Variante. +## Worktree-Sandbox (Docker) +Die Sandbox ist eine getrennte Working-Copy via `git worktree`. Docker laeuft nur dort, damit Logs/Uploads/DB-Volumes das Hauptverzeichnis nicht verschmutzen. + +### Init (einmalig) +- `bash scripts/worktree-init.sh` +- Optionaler Pfad: `bash scripts/worktree-init.sh ../mein-sandbox-pfad` + +### Start (Up) +- `bash scripts/worktree-up.sh` +- Optionaler Pfad: `bash scripts/worktree-up.sh ../mein-sandbox-pfad` + +### Sync (nur Code, ohne Commit) +- `bash scripts/worktree-sync.sh` +- Wendet auf Wunsch uncommitted Aenderungen aus dem Haupt-Worktree per Patch an. + +### Stop (Down) +- `bash scripts/worktree-down.sh` + +### Entfernen (Destroy) +- `bash scripts/worktree-destroy.sh` + +Hinweise: +- Standardpfad ist `../galaxyforge-sandbox` (relativ zum Repo-Root). +- Lege eine `.env` nur in der Sandbox an. Sie wird nicht committet. +- Verwende `.env.example` als Vorlage und aendere die Platzhalter vor Prod. + +### Testumgebung starten (inkl. DB, Migrations, Seed) +- `bash scripts/test-env-up.sh` +- Erstellt die Sandbox bei Bedarf, startet Docker, legt eine lokale `.env` in der Sandbox an und startet den PHP-Server. +- Stoppen: `CTRL+C` im Terminal, danach optional `bash scripts/worktree-down.sh`. + ## Hinweise - Die PHP-Session (`session_start()`) im Entry-Point dient Flash-Toasts und Alert-Messages. - Die Partial-Templates erwarten die Variablen `$section`, `$sub`, `$planet` und greifen über `$partialsPath` aus `web/desktop/public/index.php` aufeinander zu. diff --git a/scripts/test-env-up.sh b/scripts/test-env-up.sh new file mode 100755 index 0000000..e5af1f4 --- /dev/null +++ b/scripts/test-env-up.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) +DEFAULT_SANDBOX="${REPO_ROOT}/../galaxyforge-sandbox" +SANDBOX_PATH="${1:-$DEFAULT_SANDBOX}" + +if [[ "$SANDBOX_PATH" != /* ]]; then + SANDBOX_PATH="${REPO_ROOT}/${SANDBOX_PATH}" +fi + +SANDBOX_PATH=$(realpath -m "$SANDBOX_PATH") + +if [[ ! -d "$SANDBOX_PATH" ]]; then + echo "Sandbox nicht gefunden. Erstelle Worktree..." + "${SCRIPT_DIR}/worktree-init.sh" "$SANDBOX_PATH" +fi + +"${SCRIPT_DIR}/worktree-sync.sh" "$SANDBOX_PATH" + +if [[ ! -f "$SANDBOX_PATH/.env" ]]; then + cat <<'ENV' > "$SANDBOX_PATH/.env" +APP_ENV=test +DEV_MODE=1 +DEV_USER_ID=change-me + +DB_HOST=127.0.0.1 +DB_PORT=5432 +DB_NAME=galaxyforge +DB_TEST_NAME=galaxyforge_test +DB_USER=change-me +DB_PASS=your-strong-password + +# Nur Platzhalter. Vor Prod aendern. +ENV + echo "Lokale .env in der Sandbox erzeugt: $SANDBOX_PATH/.env" +fi + +if [[ ! -f "$SANDBOX_PATH/docker-compose.yml" ]]; then + echo "Fehler: docker-compose.yml fehlt in der Sandbox: $SANDBOX_PATH" >&2 + exit 1 +fi + +( + cd "$SANDBOX_PATH" + docker compose up -d --build +) + +set -a +source "$SANDBOX_PATH/.env" +set +a + +DB_USER="${DB_USER:-change-me}" +DB_NAME="${DB_NAME:-galaxyforge}" +DB_TEST_NAME="${DB_TEST_NAME:-galaxyforge_test}" +DB_PASS="${DB_PASS:-your-strong-password}" + +READY=0 +for _ in {1..30}; do + if (cd "$SANDBOX_PATH" && docker compose exec -T db pg_isready -U "$DB_USER" >/dev/null 2>&1); then + READY=1 + break + fi + sleep 1 +done + +if [[ "$READY" != "1" ]]; then + echo "Fehler: Postgres ist nicht erreichbar." >&2 + exit 1 +fi + +if [[ "$DB_TEST_NAME" != "$DB_NAME" ]]; then + if ! (cd "$SANDBOX_PATH" && docker compose exec -T db env PGPASSWORD="$DB_PASS" psql -U "$DB_USER" -d postgres -tc "SELECT 1 FROM pg_database WHERE datname = '${DB_TEST_NAME}'" | grep -q 1); then + (cd "$SANDBOX_PATH" && docker compose exec -T db env PGPASSWORD="$DB_PASS" psql -U "$DB_USER" -d postgres -c "CREATE DATABASE \"${DB_TEST_NAME}\"") + fi +fi + +if [[ ! -f "$SANDBOX_PATH/server/vendor/autoload.php" ]]; then + if ! command -v composer >/dev/null 2>&1; then + echo "Fehler: composer nicht gefunden. Bitte installieren und erneut versuchen." >&2 + exit 1 + fi + (cd "$SANDBOX_PATH/server" && composer install) +fi + +( + cd "$SANDBOX_PATH/server" + php db/migrate.php + php db/seed.php +) + +echo "Testumgebung laeuft. Starte PHP-Server (Stop mit CTRL+C)." +echo "- http://localhost:8000/" +echo "- http://localhost:8000/m" +echo "- http://localhost:8000/api/health" + +( + cd "$SANDBOX_PATH" + php -S localhost:8000 -t web/desktop/public +) diff --git a/scripts/worktree-destroy.sh b/scripts/worktree-destroy.sh new file mode 100755 index 0000000..127867d --- /dev/null +++ b/scripts/worktree-destroy.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) +DEFAULT_SANDBOX="${REPO_ROOT}/../galaxyforge-sandbox" +SANDBOX_PATH="${1:-$DEFAULT_SANDBOX}" + +if [[ "$SANDBOX_PATH" != /* ]]; then + SANDBOX_PATH="${REPO_ROOT}/${SANDBOX_PATH}" +fi + +SANDBOX_PATH=$(realpath -m "$SANDBOX_PATH") + +if [[ ! -d "$SANDBOX_PATH" ]]; then + echo "Fehler: Sandbox nicht gefunden: $SANDBOX_PATH" >&2 + exit 1 +fi + +if git -C "$REPO_ROOT" worktree list --porcelain | grep -Fq "worktree $SANDBOX_PATH"; then + if [[ -f "$SANDBOX_PATH/docker-compose.yml" ]]; then + ( + cd "$SANDBOX_PATH" + docker compose down + ) + fi + + git -C "$REPO_ROOT" worktree remove "$SANDBOX_PATH" --force + echo "Sandbox entfernt: $SANDBOX_PATH" +else + echo "Fehler: Kein Git-Worktree am Pfad: $SANDBOX_PATH" >&2 + exit 1 +fi diff --git a/scripts/worktree-down.sh b/scripts/worktree-down.sh new file mode 100755 index 0000000..7cae3b9 --- /dev/null +++ b/scripts/worktree-down.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) +DEFAULT_SANDBOX="${REPO_ROOT}/../galaxyforge-sandbox" +SANDBOX_PATH="${1:-$DEFAULT_SANDBOX}" + +if [[ "$SANDBOX_PATH" != /* ]]; then + SANDBOX_PATH="${REPO_ROOT}/${SANDBOX_PATH}" +fi + +SANDBOX_PATH=$(realpath -m "$SANDBOX_PATH") + +if [[ ! -d "$SANDBOX_PATH" ]]; then + echo "Fehler: Sandbox nicht gefunden: $SANDBOX_PATH" >&2 + exit 1 +fi + +if [[ ! -f "$SANDBOX_PATH/docker-compose.yml" ]]; then + echo "Fehler: docker-compose.yml fehlt in der Sandbox: $SANDBOX_PATH" >&2 + exit 1 +fi + +( + cd "$SANDBOX_PATH" + docker compose down +) + +echo "Docker in der Sandbox gestoppt." diff --git a/scripts/worktree-init.sh b/scripts/worktree-init.sh new file mode 100755 index 0000000..e71e022 --- /dev/null +++ b/scripts/worktree-init.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) +DEFAULT_SANDBOX="${REPO_ROOT}/../galaxyforge-sandbox" +SANDBOX_PATH="${1:-$DEFAULT_SANDBOX}" + +if [[ "$SANDBOX_PATH" != /* ]]; then + SANDBOX_PATH="${REPO_ROOT}/${SANDBOX_PATH}" +fi + +SANDBOX_PATH=$(realpath -m "$SANDBOX_PATH") + +if ! git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Fehler: Repo-Root nicht gefunden: $REPO_ROOT" >&2 + exit 1 +fi + +if [[ -e "$SANDBOX_PATH" ]]; then + echo "Fehler: Zielpfad existiert bereits: $SANDBOX_PATH" >&2 + echo "Tipp: Anderen Pfad angeben oder scripts/worktree-destroy.sh verwenden." >&2 + exit 1 +fi + +COMMIT=$(git -C "$REPO_ROOT" rev-parse HEAD) + +git -C "$REPO_ROOT" worktree add --detach "$SANDBOX_PATH" "$COMMIT" >/dev/null + +echo "Sandbox erstellt (detached HEAD) unter: $SANDBOX_PATH" +echo "Starten: bash \"$REPO_ROOT/scripts/worktree-up.sh\" \"$SANDBOX_PATH\"" diff --git a/scripts/worktree-sync.sh b/scripts/worktree-sync.sh new file mode 100755 index 0000000..a642e8a --- /dev/null +++ b/scripts/worktree-sync.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) +DEFAULT_SANDBOX="${REPO_ROOT}/../galaxyforge-sandbox" +SANDBOX_PATH="${1:-$DEFAULT_SANDBOX}" + +if [[ "$SANDBOX_PATH" != /* ]]; then + SANDBOX_PATH="${REPO_ROOT}/${SANDBOX_PATH}" +fi + +SANDBOX_PATH=$(realpath -m "$SANDBOX_PATH") + +if ! git -C "$REPO_ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Fehler: Repo-Root nicht gefunden: $REPO_ROOT" >&2 + exit 1 +fi + +if [[ ! -d "$SANDBOX_PATH" ]]; then + echo "Fehler: Sandbox nicht gefunden: $SANDBOX_PATH" >&2 + echo "Bitte scripts/worktree-init.sh ausfuehren." >&2 + exit 1 +fi + +if ! git -C "$SANDBOX_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "Fehler: Pfad ist kein Git-Worktree: $SANDBOX_PATH" >&2 + exit 1 +fi + +COMMIT=$(git -C "$REPO_ROOT" rev-parse HEAD) + +git -C "$SANDBOX_PATH" checkout --detach -f "$COMMIT" >/dev/null + +echo "Sandbox auf Commit $COMMIT synchronisiert." + +if ! git -C "$REPO_ROOT" diff --quiet HEAD; then + if ! git -C "$REPO_ROOT" diff --binary HEAD | git -C "$SANDBOX_PATH" apply --whitespace=nowarn; then + echo "Fehler: Patch konnte nicht angewendet werden. Bitte Sandbox bereinigen oder Konflikte pruefen." >&2 + exit 1 + fi + echo "Uncommitted Aenderungen aus dem Haupt-Worktree wurden angewendet." +else + echo "Keine uncommitted Aenderungen im Haupt-Worktree gefunden." +fi + +UNTRACKED=$(git -C "$REPO_ROOT" ls-files --others --exclude-standard) +if [[ -n "$UNTRACKED" ]]; then + echo "Hinweis: Untracked Dateien wurden nicht uebernommen." +fi diff --git a/scripts/worktree-up.sh b/scripts/worktree-up.sh new file mode 100755 index 0000000..a04056e --- /dev/null +++ b/scripts/worktree-up.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) +DEFAULT_SANDBOX="${REPO_ROOT}/../galaxyforge-sandbox" +SANDBOX_PATH="${1:-$DEFAULT_SANDBOX}" + +if [[ "$SANDBOX_PATH" != /* ]]; then + SANDBOX_PATH="${REPO_ROOT}/${SANDBOX_PATH}" +fi + +SANDBOX_PATH=$(realpath -m "$SANDBOX_PATH") + +"${SCRIPT_DIR}/worktree-sync.sh" "$SANDBOX_PATH" + +if [[ ! -f "$SANDBOX_PATH/docker-compose.yml" ]]; then + echo "Fehler: docker-compose.yml fehlt in der Sandbox: $SANDBOX_PATH" >&2 + exit 1 +fi + +( + cd "$SANDBOX_PATH" + docker compose up -d --build +) + +echo "Sandbox laeuft. URLs:" +echo "- http://localhost/" +echo "- http://localhost/m" +echo "- http://localhost/api/health"