From 9866b77c7dce6a17cc30ed9c4faa24be8d7722c2 Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Fri, 21 Aug 2026 11:47:56 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20#196:=20scripts/=20nach=20Ausf=C3=BChrung?= =?UTF-8?q?sort=20aufger=C3=A4umt=20(Host=20/=20Container=20/=20intern)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 16 ++--- Changes.md | 10 +++ README.md | 18 ++--- docker-entrypoint.sh | 10 +-- docs/datenbankstruktur.md | 2 +- docs/konfiguration.md | 18 ++--- docs/update-backup.md | 6 +- ...lyse_nextcloud.sh => analyse-nextcloud.sh} | 2 +- scripts/{ => container}/e2e-cleanup.php | 6 +- scripts/{ => container}/e2e-user.php | 8 +-- .../encrypt-bank-secrets.php} | 4 +- scripts/{ => container}/ki-batch.php | 6 +- scripts/{ => container}/nextcloud-import.php | 6 +- scripts/{ => container}/reset-password.php | 4 +- scripts/{ => container}/run-tests.sh | 4 +- scripts/{ => container}/seed-testdata.php | 4 +- scripts/e2e.sh | 12 ++++ scripts/{ => intern}/migrate.php | 2 +- scripts/{ => intern}/uploads-uebernahme.sh | 8 +-- scripts/migrate-datenverzeichnis.sh | 2 +- scripts/migrate-uploads-to-bindmount.sh | 67 ------------------- scripts/restore.sh | 2 +- scripts/test.sh | 12 ++++ scripts/update.sh | 2 +- src/Views/settings/form.php | 2 +- tests/75_AnalyseNextcloudSkriptTest.php | 8 +-- tests/76_DatenverzeichnisUebernahmeTest.php | 8 +-- tests/e2e/README.md | 8 +-- tests/run.php | 2 +- 29 files changed, 113 insertions(+), 146 deletions(-) rename scripts/{analyse_nextcloud.sh => analyse-nextcloud.sh} (99%) rename scripts/{ => container}/e2e-cleanup.php (93%) rename scripts/{ => container}/e2e-user.php (91%) rename scripts/{encrypt_bank_secrets.php => container/encrypt-bank-secrets.php} (93%) rename scripts/{ => container}/ki-batch.php (94%) rename scripts/{ => container}/nextcloud-import.php (92%) rename scripts/{ => container}/reset-password.php (85%) rename scripts/{ => container}/run-tests.sh (97%) rename scripts/{ => container}/seed-testdata.php (99%) create mode 100755 scripts/e2e.sh rename scripts/{ => intern}/migrate.php (98%) rename scripts/{ => intern}/uploads-uebernahme.sh (87%) delete mode 100755 scripts/migrate-uploads-to-bindmount.sh create mode 100755 scripts/test.sh diff --git a/CLAUDE.md b/CLAUDE.md index 4a268b3..5a406c6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,9 +56,9 @@ Tests liegen in `tests/` und folgen dem Namensschema `NN_FunktionsnameTest.php`. Jede Testklasse erweitert `TestCase` und implementiert `run()`. ```bash -docker compose exec -u www-data web php tests/run.php # alle Tests ausführen -docker compose exec -u www-data web ./scripts/run-tests.sh # Tests + automatische Issue-Erstellung bei Fehlern -docker compose exec -u www-data web ./scripts/run-tests.sh 42 # Tests; bei Fehlern Kommentar in Issue #42 +./scripts/test.sh # alle Tests ausführen +docker compose exec -u www-data web ./scripts/container/run-tests.sh # Tests + automatische Issue-Erstellung bei Fehlern +docker compose exec -u www-data web ./scripts/container/run-tests.sh 42 # Tests; bei Fehlern Kommentar in Issue #42 ``` **Immer `-u www-data`:** ohne die Option laufen die Tests als root und legen @@ -97,9 +97,9 @@ es Playwright-Tests in `tests/e2e/`, die im Container des e2e-runner laufen (auf dem Host wird nichts installiert). Details: `tests/e2e/README.md`. ```bash -docker compose exec web php scripts/e2e-user.php # einmalig: Testnutzer anlegen -tests/e2e/run.sh # alle E2E-Tests -docker compose exec web php scripts/e2e-cleanup.php --force # Reste entfernen +docker compose exec web php scripts/container/e2e-user.php # einmalig: Testnutzer anlegen +./scripts/e2e.sh # alle E2E-Tests +docker compose exec web php scripts/container/e2e-cleanup.php --force # Reste entfernen ``` Fehlt Docker, der Runner oder das Image, wird mit Exit 0 übersprungen. @@ -123,7 +123,7 @@ Redirects, gerenderte Views, JavaScript-Umrechnungen, DOM-XSS. Schemaänderungen (neue Spalten, Tabellen, Kontenanpassungen) kommen **ausschließlich** als nummerierte Migrations-Datei in `migrations/`: - Schema: `NNN_kurze_beschreibung.sql` (NNN = nächste freie dreistellige Nummer, z.B. `011_...`) -- Migrationen werden **nie manuell** eingespielt – `scripts/migrate.php` erledigt das automatisch beim Container-Start +- Migrationen werden **nie manuell** eingespielt – `scripts/intern/migrate.php` erledigt das automatisch beim Container-Start - Jede Migration muss idempotent sein oder mit `IF NOT EXISTS` / `ON DUPLICATE KEY` abgesichert werden ```bash @@ -131,7 +131,7 @@ Schemaänderungen (neue Spalten, Tabellen, Kontenanpassungen) kommen **ausschlie ls migrations/ | tail -1 # Migration manuell testen (ohne Neustart): -docker compose exec web php scripts/migrate.php +docker compose exec web php scripts/intern/migrate.php ``` ## Geplante Aufgaben (Cron) diff --git a/Changes.md b/Changes.md index 8c00ded..0be8b8d 100644 --- a/Changes.md +++ b/Changes.md @@ -2,6 +2,16 @@ --- +## 2026-08-21 – scripts/ nach Ausführungsort aufgeräumt: Host / Container / intern (#196) + +- **`scripts/`:** Bislang lagen 16 Skripte mit vier verschiedenen Aufrufkonventionen flach nebeneinander – von außen war nicht erkennbar, was direkt vom Host aufrufbar ist, was `docker compose exec` braucht und was nie manuell laufen darf. Jetzt gibt es genau ein Ordnungskriterium: `scripts/` (Host, ausnahmslos direkt aufrufbar), `scripts/container/` (`docker compose exec web ...`) und `scripts/intern/` (Entrypoint/Container-Start, nie manuell). +- **Verschoben und auf Bindestriche vereinheitlicht:** `analyse_nextcloud.sh` → `scripts/analyse-nextcloud.sh`; `e2e-cleanup.php`, `e2e-user.php`, `ki-batch.php`, `nextcloud-import.php`, `reset-password.php`, `seed-testdata.php`, `run-tests.sh` → `scripts/container/`; `encrypt_bank_secrets.php` → `scripts/container/encrypt-bank-secrets.php`; `migrate.php`, `uploads-uebernahme.sh` → `scripts/intern/`. `backup.sh`, `restore.sh`, `update.sh`, `migrate-datenverzeichnis.sh` bleiben unverändert in `scripts/`. +- **`scripts/migrate-uploads-to-bindmount.sh`** entfernt: beschrieb einen Umzug in ein `./uploads/`-Verzeichnis, das es seit dem Datenverzeichnis-Umbau nicht mehr gibt, und verwies selbst bereits auf seinen Nachfolger `migrate-datenverzeichnis.sh`. +- **`scripts/test.sh`, `scripts/e2e.sh`** (neu): dünne Host-Wrapper für die beiden meistgenutzten Abläufe – delegieren an `docker compose exec -u www-data web php tests/run.php` bzw. `tests/e2e/run.sh`, ohne eigene Logik. +- **`docker-entrypoint.sh`, `scripts/update.sh`, `scripts/restore.sh`, `scripts/migrate-datenverzeichnis.sh`** sowie die PHP-Einstiegspunkte selbst (`dirname(__DIR__)`/`chdir(dirname(__DIR__))`-Aufrufe) auf die neue Verzeichnistiefe angepasst. +- **`tests/71_DatenverzeichnisPfadeTest.php`, `tests/75_AnalyseNextcloudSkriptTest.php`, `tests/76_DatenverzeichnisUebernahmeTest.php`:** Pfade auf die neue Struktur nachgezogen. +- Doku (`README.md`, `CLAUDE.md`, `docs/konfiguration.md`, `docs/update-backup.md`, `docs/datenbankstruktur.md`, `tests/e2e/README.md`) folgt den neuen Pfaden; Verweise in älteren Changelog-Einträgen oben bleiben unverändert, da sie den zum jeweiligen Zeitpunkt gültigen Stand dokumentieren. + ## 2026-08-21 – Lieferantenliste (/suppliers) lief in HTTP 500: fehlender View-Import (#198) - **`src/Views/suppliers/list.php`:** Die View rief `View::formatDate()` ohne `use Steuerkram\View;` auf. Views werden per `include` eingebunden und laufen dadurch im globalen Namespace – das `use` des einbindenden Controllers gilt dort nicht, der Aufruf ging also auf `\View::formatDate()` und die Seite starb mit `Class "View" not found`. Sichtbar wurde das erst mit dem ersten Lieferanten, weil die Zeile in der `foreach`-Schleife steht. Import ergänzt; alle übrigen Views hatten ihn bereits. diff --git a/README.md b/README.md index d093bdd..390e7d3 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ docker compose up -d --build ## Belege automatisch aus Nextcloud importieren -Belege können automatisch aus einem Nextcloud-Ordner via WebDAV importiert werden. Das CLI-Skript `scripts/nextcloud-import.php` liest alle unterstützten Dateien (PDF, JPEG, PNG, TIFF, XML) aus dem konfigurierten Ordner, legt sie als Belege an und verschiebt sie anschließend in einen Erledigte-Ordner (oder löscht sie, wenn kein Ziel-Ordner konfiguriert ist). +Belege können automatisch aus einem Nextcloud-Ordner via WebDAV importiert werden. Das CLI-Skript `scripts/container/nextcloud-import.php` liest alle unterstützten Dateien (PDF, JPEG, PNG, TIFF, XML) aus dem konfigurierten Ordner, legt sie als Belege an und verschiebt sie anschließend in einen Erledigte-Ordner (oder löscht sie, wenn kein Ziel-Ordner konfiguriert ist). ### Konfiguration @@ -148,7 +148,7 @@ Empfehlung: Ein Nextcloud-App-Passwort verwenden (Nextcloud → Einstellungen ### Manueller Aufruf ```bash -docker compose exec web php scripts/nextcloud-import.php +docker compose exec web php scripts/container/nextcloud-import.php ``` ### Automatisch per Cron (auf dem Host) @@ -159,7 +159,7 @@ Für den automatischen Abruf muss ein Cron-Eintrag auf dem Host angelegt werden. ```cron */15 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ - php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 + php scripts/container/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 ``` Kein `sudo` im Befehl nötig. Nachteil: Root-Crons sind schwerer zu überblicken. @@ -177,7 +177,7 @@ Anschließend in der User-Crontab (ohne `sudo`): ```cron */15 * * * * docker compose -f /home/fds/projects/Steuerkram/docker-compose.yml exec -T web \ - php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 + php scripts/container/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 ``` > **Hinweis:** Das `-T`-Flag ist bei Cron zwingend erforderlich – ohne TTY schlägt `docker compose exec` sonst still fehl. @@ -256,7 +256,7 @@ Das Skript erstellt ein Dump der Datenbank sowie ein Archiv der Uploads im Verze Seit dem 11.04.2026 bin ich mit meiner Software für mich selber produktiv. Ab diesem Zeitpunkt sollten keine breaking changes mehr vorkommen. -Datenbankmigrationen werden **automatisch** beim nächsten Container-Start eingespielt – kein manueller Eingriff nötig. Das Script `scripts/migrate.php` prüft beim Start, welche Migrations-Dateien noch nicht angewendet wurden, und führt sie der Reihe nach aus. +Datenbankmigrationen werden **automatisch** beim nächsten Container-Start eingespielt – kein manueller Eingriff nötig. Das Script `scripts/intern/migrate.php` prüft beim Start, welche Migrations-Dateien noch nicht angewendet wurden, und führt sie der Reihe nach aus. ### Vollständige Update-Anleitung @@ -295,7 +295,7 @@ git log --oneline -5 docker compose up -d --build ``` -Dieser Befehl baut das PHP-Image neu (falls sich `Dockerfile` oder `composer.json` geändert haben) und startet alle Container. Der Web-Container führt beim Start automatisch `scripts/migrate.php` aus. +Dieser Befehl baut das PHP-Image neu (falls sich `Dockerfile` oder `composer.json` geändert haben) und startet alle Container. Der Web-Container führt beim Start automatisch `scripts/intern/migrate.php` aus. #### Schritt 4 – Migrationen prüfen @@ -355,15 +355,15 @@ Anschließend im Browser unter `http://localhost:8080` (oder dem konfigurierten ## Tests ```bash -docker compose exec -u www-data web php tests/run.php # PHP-Suite (Models, Services, Berechnungen) -tests/e2e/run.sh # Browser-Tests (Routing, Views, Formular-JS) +./scripts/test.sh # PHP-Suite (Models, Services, Berechnungen) +./scripts/e2e.sh # Browser-Tests (Routing, Views, Formular-JS) ``` Die PHP-Suite läuft ohne weitere Voraussetzungen. Das `-u www-data` gehört dazu: sonst laufen die Tests als root und hinterlassen in `data/` Dateien, die Apache/PHP später nicht mehr beschreiben kann. Die E2E-Tests brauchen Docker, das [e2e-runner-Projekt](../e2e-runner/) und einen Testnutzer -(`docker compose exec web php scripts/e2e-user.php`); fehlt etwas davon, werden +(`docker compose exec web php scripts/container/e2e-user.php`); fehlt etwas davon, werden sie mit einer Warnung übersprungen. Details in [tests/e2e/README.md](tests/e2e/README.md). --- diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 8961b1d..6a86c3f 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -14,10 +14,10 @@ mkdir -p "$BELEGE" "$LOGO" 2>/dev/null || true # Installation endet, deren Dateien nicht mehr gefunden werden. # Idempotent: Nach dem Umzug sind die Quellverzeichnisse leer. -# Die eigentliche Übernahme liegt in scripts/uploads-uebernahme.sh – dieses -# Verzeichnis ist als Volume gemountet, der Entrypoint dagegen steckt im Image. -# So wirkt eine Korrektur am Umzug auch ohne Rebuild. -UEBERNAHME=/var/www/html/scripts/uploads-uebernahme.sh +# Die eigentliche Übernahme liegt in scripts/intern/uploads-uebernahme.sh – +# dieses Verzeichnis ist als Volume gemountet, der Entrypoint dagegen steckt +# im Image. So wirkt eine Korrektur am Umzug auch ohne Rebuild. +UEBERNAHME=/var/www/html/scripts/intern/uploads-uebernahme.sh # Logo aus dem alten Web-Root (Stand vor Fix #116). Übergeben wird direkt der # logo-Ordner – alles darin ist Logo, deshalb steht data/logo an beiden Zielstellen. @@ -35,6 +35,6 @@ sh "$UEBERNAHME" "$LEGACY" "$BELEGE" "$LOGO" || true chown -R www-data:www-data "$BELEGE" "$LOGO" 2>/dev/null || true # Datenbank-Migrationen automatisch einspielen -php /var/www/html/scripts/migrate.php +php /var/www/html/scripts/intern/migrate.php exec "$@" diff --git a/docs/datenbankstruktur.md b/docs/datenbankstruktur.md index 2631656..27330e1 100644 --- a/docs/datenbankstruktur.md +++ b/docs/datenbankstruktur.md @@ -281,4 +281,4 @@ UNIQUE KEY auf `(year, month)`. ## Migrationen -Schemaänderungen kommen ausschließlich als nummerierte SQL-Dateien in `migrations/` und werden vom Script `scripts/migrate.php` beim Container-Start automatisch eingespielt. Die Tabelle `schema_migrations` protokolliert, welche Dateien bereits angewendet wurden. +Schemaänderungen kommen ausschließlich als nummerierte SQL-Dateien in `migrations/` und werden vom Script `scripts/intern/migrate.php` beim Container-Start automatisch eingespielt. Die Tabelle `schema_migrations` protokolliert, welche Dateien bereits angewendet wurden. diff --git a/docs/konfiguration.md b/docs/konfiguration.md index 8030d8c..9710ce4 100644 --- a/docs/konfiguration.md +++ b/docs/konfiguration.md @@ -62,7 +62,7 @@ LLM_MODEL=gpt-4o-mini ## Nextcloud-Import einrichten -Belege können automatisch aus einem Nextcloud-Ordner via WebDAV importiert werden. Das CLI-Skript `scripts/nextcloud-import.php` liest alle unterstützten Dateien (PDF, JPEG, PNG, TIFF, XML) aus dem konfigurierten Ordner, legt sie als Belege an und verschiebt sie anschließend in einen Erledigte-Ordner. +Belege können automatisch aus einem Nextcloud-Ordner via WebDAV importiert werden. Das CLI-Skript `scripts/container/nextcloud-import.php` liest alle unterstützten Dateien (PDF, JPEG, PNG, TIFF, XML) aus dem konfigurierten Ordner, legt sie als Belege an und verschiebt sie anschließend in einen Erledigte-Ordner. ### Zugangsdaten konfigurieren @@ -73,7 +73,7 @@ Hinweis: Ein Nextcloud-App-Passwort verwenden (Nextcloud → Einstellungen → S ### Manueller Aufruf ```bash -docker compose exec web php scripts/nextcloud-import.php +docker compose exec web php scripts/container/nextcloud-import.php ``` ### Automatisch per Cron @@ -82,7 +82,7 @@ Cron-Eintrag auf dem Host anlegen (`crontab -e`): ```cron */15 * * * * docker compose -f /opt/Steuerkram/docker-compose.yml exec -T web \ - php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 + php scripts/container/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 ``` > Das `-T`-Flag ist bei Cron zwingend erforderlich – ohne TTY schlägt `docker compose exec` sonst still fehl. @@ -96,11 +96,11 @@ sudo usermod -aG docker $USER ### Verbindung analysieren -Meldet der Import „PROPFIND fehlgeschlagen" oder findet er keine Dateien, grenzt `scripts/analyse_nextcloud.sh` die Ursache ein. Das Skript wird auf dem Host aufgerufen (nicht im Container) und arbeitet rein lesend – es importiert, verschiebt und löscht nichts. Das Passwort wird nie im Klartext ausgegeben. +Meldet der Import „PROPFIND fehlgeschlagen" oder findet er keine Dateien, grenzt `scripts/analyse-nextcloud.sh` die Ursache ein. Das Skript wird auf dem Host aufgerufen (nicht im Container) und arbeitet rein lesend – es importiert, verschiebt und löscht nichts. Das Passwort wird nie im Klartext ausgegeben. ```bash -./scripts/analyse_nextcloud.sh # Konfiguration, DNS, Verbindungsversuch -./scripts/analyse_nextcloud.sh --verbose # zusätzlich ein Auszug der Server-Antwort +./scripts/analyse-nextcloud.sh # Konfiguration, DNS, Verbindungsversuch +./scripts/analyse-nextcloud.sh --verbose # zusätzlich ein Auszug der Server-Antwort ``` Ausgegeben werden die hinterlegte Konfiguration, die Namensauflösung samt IP-Pinning des SSRF-Schutzes, ein PROPFIND auf den Import-Ordner, der Inhalt des Ordners (inkl. Dateien, die wegen ihres Typs übersprungen würden) und die Erreichbarkeit des Erledigte-Ordners. Schlägt der Verbindungsversuch fehl, folgen Gegenproben ohne IP-Pinning sowie je Adressfamilie einzeln – daran ist ablesbar, ob Zugangsdaten, Ordnerpfad oder die Netzwerkverbindung (z.B. fehlende IPv6-Konnektivität) die Ursache sind. Exit-Code 0 bedeutet, dass die Verbindung steht. @@ -109,11 +109,11 @@ Ausgegeben werden die hinterlegte Konfiguration, die Namensauflösung samt IP-Pi ## Benutzer-Passwort zurücksetzen -Das Skript `scripts/reset-password.php` setzt das Passwort eines vorhandenen Benutzers auf einen neuen Wert. Es nimmt den Benutzernamen und das neue Passwort als Parameter entgegen, erzeugt einen bcrypt-Hash und aktualisiert den Eintrag in der Datenbank. Wird der Benutzer nicht gefunden, gibt es eine Fehlermeldung und ändert nichts. +Das Skript `scripts/container/reset-password.php` setzt das Passwort eines vorhandenen Benutzers auf einen neuen Wert. Es nimmt den Benutzernamen und das neue Passwort als Parameter entgegen, erzeugt einen bcrypt-Hash und aktualisiert den Eintrag in der Datenbank. Wird der Benutzer nicht gefunden, gibt es eine Fehlermeldung und ändert nichts. ```bash -docker compose exec web php scripts/reset-password.php +docker compose exec web php scripts/container/reset-password.php # Beispiel: -docker compose exec web php scripts/reset-password.php Meinuser geheim123 +docker compose exec web php scripts/container/reset-password.php Meinuser geheim123 ``` diff --git a/docs/update-backup.md b/docs/update-backup.md index 80fd755..da7ee1c 100644 --- a/docs/update-backup.md +++ b/docs/update-backup.md @@ -61,7 +61,7 @@ Das Script erledigt in dieser Reihenfolge: 4. **Image-Rebuild** – Das Docker-Image wird neu gebaut, wenn `Dockerfile`, `composer.json`, `composer.lock`, `php/`, `apache/` oder `docker-entrypoint.sh` geändert wurden 5. **Container-Neustart** – `docker compose down --remove-orphans` + `up -d` 6. **Healthcheck** – Wartet bis die Datenbank `healthy` meldet (max. 120 s) -7. **Migrationen** – `scripts/migrate.php` wird explizit ausgeführt (idempotent) +7. **Migrationen** – `scripts/intern/migrate.php` wird explizit ausgeführt (idempotent) 8. **Tests als Gate** – PHP-Suite und `tests/e2e/run.sh` werden ausgeführt; schlägt eine der beiden fehl, endet `update.sh` mit Exit-Code 1 ### Tests als Gate @@ -91,7 +91,7 @@ zum manuellen Zurückspielen. ### Migrationen -Datenbankmigrationen werden von `scripts/migrate.php` verwaltet. Das Script prüft, welche Migrations-Dateien noch nicht angewendet wurden, und führt sie der Reihe nach aus. Es wird sowohl vom Container-Entrypoint als auch von `update.sh` aufgerufen. +Datenbankmigrationen werden von `scripts/intern/migrate.php` verwaltet. Das Script prüft, welche Migrations-Dateien noch nicht angewendet wurden, und führt sie der Reihe nach aus. Es wird sowohl vom Container-Entrypoint als auch von `update.sh` aufgerufen. ``` [migrate] Starte Migrations-Prüfung... @@ -102,7 +102,7 @@ Datenbankmigrationen werden von `scripts/migrate.php` verwaltet. Das Script prü Migrationen können auch manuell ausgelöst werden: ```bash -docker compose exec web php scripts/migrate.php +docker compose exec web php scripts/intern/migrate.php ``` ### Diagnose bei Fehlern diff --git a/scripts/analyse_nextcloud.sh b/scripts/analyse-nextcloud.sh similarity index 99% rename from scripts/analyse_nextcloud.sh rename to scripts/analyse-nextcloud.sh index 3a248c9..49acc5d 100755 --- a/scripts/analyse_nextcloud.sh +++ b/scripts/analyse-nextcloud.sh @@ -11,7 +11,7 @@ # Das Passwort wird nie im Klartext ausgegeben. # # Usage: -# ./analyse_nextcloud.sh [--verbose] +# ./analyse-nextcloud.sh [--verbose] # # Exit-Code: 0 = Verbindung steht, 1 = Konfigurations- oder Verbindungsfehler # ════════════════════════════════════════════════════════════════════════════ diff --git a/scripts/e2e-cleanup.php b/scripts/container/e2e-cleanup.php similarity index 93% rename from scripts/e2e-cleanup.php rename to scripts/container/e2e-cleanup.php index d3a2cf4..2f6e718 100644 --- a/scripts/e2e-cleanup.php +++ b/scripts/container/e2e-cleanup.php @@ -12,11 +12,11 @@ declare(strict_types=1); * echte Buchführungsdaten bleiben unangetastet. * * Verwendung: - * docker compose exec web php scripts/e2e-cleanup.php # nur anzeigen - * docker compose exec web php scripts/e2e-cleanup.php --force # wirklich löschen + * docker compose exec web php scripts/container/e2e-cleanup.php # nur anzeigen + * docker compose exec web php scripts/container/e2e-cleanup.php --force # wirklich löschen */ -$projectRoot = dirname(__DIR__); +$projectRoot = dirname(__DIR__, 2); if (!file_exists($projectRoot . '/vendor/autoload.php')) { fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n"); exit(1); diff --git a/scripts/e2e-user.php b/scripts/container/e2e-user.php similarity index 91% rename from scripts/e2e-user.php rename to scripts/container/e2e-user.php index a750ed7..16c65a6 100644 --- a/scripts/e2e-user.php +++ b/scripts/container/e2e-user.php @@ -14,14 +14,14 @@ declare(strict_types=1); * und Status zurückgesetzt (aktiv, keine Fehlversuche, nicht gesperrt). * * Verwendung: - * docker compose exec web php scripts/e2e-user.php - * docker compose exec web php scripts/e2e-user.php - * docker compose exec web php scripts/e2e-user.php --delete + * docker compose exec web php scripts/container/e2e-user.php + * docker compose exec web php scripts/container/e2e-user.php + * docker compose exec web php scripts/container/e2e-user.php --delete * * Die Defaults entsprechen tests/e2e/helpers/env.js. */ -$projectRoot = dirname(__DIR__); +$projectRoot = dirname(__DIR__, 2); if (!file_exists($projectRoot . '/vendor/autoload.php')) { fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n"); exit(1); diff --git a/scripts/encrypt_bank_secrets.php b/scripts/container/encrypt-bank-secrets.php similarity index 93% rename from scripts/encrypt_bank_secrets.php rename to scripts/container/encrypt-bank-secrets.php index a4b9ca3..0eaec64 100644 --- a/scripts/encrypt_bank_secrets.php +++ b/scripts/container/encrypt-bank-secrets.php @@ -7,14 +7,14 @@ * werden übersprungen. Nach Ausführung kann es gefahrlos erneut aufgerufen werden. * * Aufruf: - * docker compose exec web php scripts/encrypt_bank_secrets.php + * docker compose exec web php scripts/container/encrypt-bank-secrets.php * * Voraussetzung: APP_KEY in .env gesetzt (64-stellige Hex-Zeichenkette). */ declare(strict_types=1); -require __DIR__ . '/../vendor/autoload.php'; +require __DIR__ . '/../../vendor/autoload.php'; use Steuerkram\Config; use Steuerkram\Database; diff --git a/scripts/ki-batch.php b/scripts/container/ki-batch.php similarity index 94% rename from scripts/ki-batch.php rename to scripts/container/ki-batch.php index 114b454..57c1c5e 100644 --- a/scripts/ki-batch.php +++ b/scripts/container/ki-batch.php @@ -9,16 +9,16 @@ * Voraussetzung: ki_vorschlag_aktiv = 1 in den Einstellungen, Ollama erreichbar. * * Aufruf: - * docker compose exec web php scripts/ki-batch.php + * docker compose exec web php scripts/container/ki-batch.php * * Für optionalen eigenen Cronjob (manuelle Uploads ohne Nextcloud): * *\/5 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ - * php scripts/ki-batch.php >> /var/log/steuerkram-ki.log 2>&1 + * php scripts/container/ki-batch.php >> /var/log/steuerkram-ki.log 2>&1 */ declare(strict_types=1); -chdir(dirname(__DIR__)); +chdir(dirname(__DIR__, 2)); // DB-Zugangsdaten aus .env laden (für CLI-Kontext ohne Apache-Umgebung) if (file_exists('.env')) { diff --git a/scripts/nextcloud-import.php b/scripts/container/nextcloud-import.php similarity index 92% rename from scripts/nextcloud-import.php rename to scripts/container/nextcloud-import.php index 11c7107..b26066d 100644 --- a/scripts/nextcloud-import.php +++ b/scripts/container/nextcloud-import.php @@ -11,16 +11,16 @@ * Konfiguration: Einstellungen → Nextcloud-Belegimport in der Steuerkram-UI. * * Aufruf: - * docker compose exec web php scripts/nextcloud-import.php + * docker compose exec web php scripts/container/nextcloud-import.php * * Für Cronjob (auf dem Host): * *\/15 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ - * php scripts/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 + * php scripts/container/nextcloud-import.php >> /var/log/steuerkram-import.log 2>&1 */ declare(strict_types=1); -chdir(dirname(__DIR__)); +chdir(dirname(__DIR__, 2)); // DB-Zugangsdaten aus .env laden (für CLI-Kontext ohne Apache-Umgebung) if (file_exists('.env')) { diff --git a/scripts/reset-password.php b/scripts/container/reset-password.php similarity index 85% rename from scripts/reset-password.php rename to scripts/container/reset-password.php index bc37ee9..c11b69d 100644 --- a/scripts/reset-password.php +++ b/scripts/container/reset-password.php @@ -1,11 +1,11 @@ + * Aufruf: php scripts/container/reset-password.php */ if ($argc !== 3) { - echo 'Verwendung: php scripts/reset-password.php ' . PHP_EOL; + echo 'Verwendung: php scripts/container/reset-password.php ' . PHP_EOL; exit(1); } diff --git a/scripts/run-tests.sh b/scripts/container/run-tests.sh similarity index 97% rename from scripts/run-tests.sh rename to scripts/container/run-tests.sh index b2e3a1b..ae70c8b 100755 --- a/scripts/run-tests.sh +++ b/scripts/container/run-tests.sh @@ -4,7 +4,7 @@ # Steuerkram Test-Runner mit automatischer Issue-Erstellung # # Verwendung (im Container): -# docker compose exec -u www-data web ./scripts/run-tests.sh [ISSUE_NUMMER] +# docker compose exec -u www-data web ./scripts/container/run-tests.sh [ISSUE_NUMMER] # # Parameter: # ISSUE_NUMMER (optional) Nummer des aktuell bearbeiteten Issues, @@ -19,7 +19,7 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +PROJECT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")" CURRENT_ISSUE="${1:-}" REPO="fds2610/Steuerkram" API_BASE="https://codeberg.org/api/v1" diff --git a/scripts/seed-testdata.php b/scripts/container/seed-testdata.php similarity index 99% rename from scripts/seed-testdata.php rename to scripts/container/seed-testdata.php index eef2a4f..4f8841c 100755 --- a/scripts/seed-testdata.php +++ b/scripts/container/seed-testdata.php @@ -13,10 +13,10 @@ declare(strict_types=1); * - 3 Ausgangsrechnungen (Januar, Februar, März 2026) * * Verwendung: - * php scripts/seed-testdata.php + * php scripts/container/seed-testdata.php */ -$projectRoot = dirname(__DIR__); +$projectRoot = dirname(__DIR__, 2); if (!file_exists($projectRoot . '/vendor/autoload.php')) { fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n"); exit(1); diff --git a/scripts/e2e.sh b/scripts/e2e.sh new file mode 100755 index 0000000..08ed096 --- /dev/null +++ b/scripts/e2e.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Dünner Host-Wrapper für die E2E-Testsuite. +# Delegiert an tests/e2e/run.sh. +# +# Usage: +# ./scripts/e2e.sh + +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +exec "$SCRIPT_DIR/../tests/e2e/run.sh" "$@" diff --git a/scripts/migrate.php b/scripts/intern/migrate.php similarity index 98% rename from scripts/migrate.php rename to scripts/intern/migrate.php index 5ce51a4..cbdada7 100644 --- a/scripts/migrate.php +++ b/scripts/intern/migrate.php @@ -9,7 +9,7 @@ $host = getenv('DB_HOST') ?: 'db'; $dbName = getenv('DB_NAME') ?: 'steuerkram'; $dbUser = getenv('DB_USER') ?: 'steuerkram'; $dbPass = getenv('DB_PASS') ?: 'steuerkram'; -$migrDir = __DIR__ . '/../migrations'; +$migrDir = __DIR__ . '/../../migrations'; function mlog(string $msg): void { echo '[migrate] ' . $msg . PHP_EOL; diff --git a/scripts/uploads-uebernahme.sh b/scripts/intern/uploads-uebernahme.sh similarity index 87% rename from scripts/uploads-uebernahme.sh rename to scripts/intern/uploads-uebernahme.sh index 2bb8fe0..7273dcc 100755 --- a/scripts/uploads-uebernahme.sh +++ b/scripts/intern/uploads-uebernahme.sh @@ -2,11 +2,11 @@ # Übernimmt den Altbestand aus dem gemeinsamen uploads-Verzeichnis in die # getrennten Datenverzeichnisse data/belege und data/logo. # -# scripts/uploads-uebernahme.sh +# scripts/intern/uploads-uebernahme.sh # -# Aufrufer sind docker-entrypoint.sh (bei jedem Container-Start) und -# scripts/migrate-datenverzeichnis.sh (manueller Umzug). Die Logik liegt bewusst -# hier und nicht doppelt in beiden Skripten. +# Nie manuell aufrufen. Aufrufer sind docker-entrypoint.sh (bei jedem +# Container-Start) und scripts/migrate-datenverzeichnis.sh (manueller Umzug). +# Die Logik liegt bewusst hier und nicht doppelt in beiden Skripten. # # Idempotent: Ist der Altbestand übernommen, findet ein zweiter Lauf nichts mehr. # Exit-Code 0 = nichts zu tun oder vollständig übernommen, 1 = Reste verblieben. diff --git a/scripts/migrate-datenverzeichnis.sh b/scripts/migrate-datenverzeichnis.sh index b9767c3..d5ad0e0 100755 --- a/scripts/migrate-datenverzeichnis.sh +++ b/scripts/migrate-datenverzeichnis.sh @@ -51,7 +51,7 @@ docker run --rm \ mkdir -p /neu/belege /neu/logo # Gemeinsame Übernahme-Logik – dieselbe, die auch beim Container-Start läuft. - sh /skripte/uploads-uebernahme.sh /alt /neu/belege /neu/logo || true + sh /skripte/intern/uploads-uebernahme.sh /alt /neu/belege /neu/logo || true chown -R 33:33 /neu/belege /neu/logo diff --git a/scripts/migrate-uploads-to-bindmount.sh b/scripts/migrate-uploads-to-bindmount.sh deleted file mode 100755 index 5ad4151..0000000 --- a/scripts/migrate-uploads-to-bindmount.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash -# VERALTET – nur noch für historische Stände relevant. -# Das damalige Ziel ./uploads/ gibt es nicht mehr; Belege liegen inzwischen in -# ./data/belege/, das Logo in ./data/logo/. Für den Umzug aus ./uploads/ in die -# heutige Struktur ist scripts/migrate-datenverzeichnis.sh zuständig. -# -# Einmalige Migration: Uploads vom named Docker Volume in ./uploads/ (Bind Mount) -# Vor dem ersten "docker compose up" mit dem neuen docker-compose.yml ausführen. -# -# Das Script ist idempotent: bereits vorhandene Dateien in ./uploads/ werden -# nicht überschrieben. Es ist sicher, es mehrfach auszuführen. -# -# Usage: ./scripts/migrate-uploads-to-bindmount.sh - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" -TARGET_DIR="$PROJECT_DIR/uploads" -VOLUME_NAME="steuerkram_uploads" - -echo "=== Uploads-Migration: named Volume → Bind Mount ===" -echo "Zielverzeichnis: $TARGET_DIR" -echo "" - -# Prüfen ob das named Volume existiert -if ! docker volume ls --format '{{.Name}}' | grep -qx "$VOLUME_NAME"; then - echo "Named Volume '$VOLUME_NAME' nicht gefunden – nichts zu migrieren." - echo "Zielverzeichnis wird angelegt (falls nicht vorhanden)." - mkdir -p "$TARGET_DIR" - echo "Fertig." - exit 0 -fi - -# Anzahl Dateien im Volume ermitteln -FILE_COUNT=$(docker run --rm \ - -v "$VOLUME_NAME:/src" \ - alpine \ - find /src -type f | wc -l) - -echo "Dateien im named Volume: $FILE_COUNT" - -if [ "$FILE_COUNT" -eq 0 ]; then - echo "Volume ist leer – nichts zu kopieren." - mkdir -p "$TARGET_DIR" - echo "Fertig." - exit 0 -fi - -mkdir -p "$TARGET_DIR" - -echo "Kopiere Dateien ins Bind-Mount-Verzeichnis..." -docker run --rm \ - -v "$VOLUME_NAME:/src" \ - -v "$TARGET_DIR:/dst" \ - alpine \ - sh -c 'cp -r /src/. /dst/' - -COPIED=$(find "$TARGET_DIR" -type f | wc -l) -echo "" -echo "Migration abgeschlossen: $COPIED Dateien in $TARGET_DIR" -echo "" -echo "Nächste Schritte:" -echo " docker compose up -d" -echo "" -echo "Das named Volume '$VOLUME_NAME' kann danach gelöscht werden:" -echo " docker volume rm $VOLUME_NAME" diff --git a/scripts/restore.sh b/scripts/restore.sh index 4f12082..70c2f46 100755 --- a/scripts/restore.sh +++ b/scripts/restore.sh @@ -134,7 +134,7 @@ echo " OK" # --- 3. Migrationen --- echo "[3/3] Migrationen prüfen und einspielen..." -MIGRATE_OUT=$($COMPOSE exec -T web php scripts/migrate.php 2>&1) +MIGRATE_OUT=$($COMPOSE exec -T web php scripts/intern/migrate.php 2>&1) echo "$MIGRATE_OUT" | sed 's/^/ /' if echo "$MIGRATE_OUT" | grep -q "FEHLER"; then echo "" diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..acf2490 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# Dünner Host-Wrapper für die PHP-Testsuite. +# Delegiert an tests/run.php; -u www-data ist zwingend, siehe CLAUDE.md. +# +# Usage: +# ./scripts/test.sh + +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +exec docker compose -f "$SCRIPT_DIR/../docker-compose.yml" --project-directory "$SCRIPT_DIR/.." exec -u www-data web php tests/run.php "$@" diff --git a/scripts/update.sh b/scripts/update.sh index 145ea83..f6200ab 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -347,7 +347,7 @@ ok "Autoloader aktualisiert" # nochmals explizit aus um sicherzustellen, dass alle eingespielt sind # und um den Status im Log zu sehen. info "Führe Migrationen aus (idempotent)..." -MIGRATE_OUTPUT=$(docker compose exec -T web php /var/www/html/scripts/migrate.php 2>&1) || { +MIGRATE_OUTPUT=$(docker compose exec -T web php /var/www/html/scripts/intern/migrate.php 2>&1) || { fail "Migrationen fehlgeschlagen" echo "$MIGRATE_OUTPUT" | while read -r line; do info "$line"; done UPDATE_FAILED=true diff --git a/src/Views/settings/form.php b/src/Views/settings/form.php index 61980d5..b9c9548 100644 --- a/src/Views/settings/form.php +++ b/src/Views/settings/form.php @@ -345,7 +345,7 @@
Import manuell starten: - docker compose exec web php scripts/nextcloud-import.php + docker compose exec web php scripts/container/nextcloud-import.php
diff --git a/tests/75_AnalyseNextcloudSkriptTest.php b/tests/75_AnalyseNextcloudSkriptTest.php index 8eb5c3b..6a96331 100644 --- a/tests/75_AnalyseNextcloudSkriptTest.php +++ b/tests/75_AnalyseNextcloudSkriptTest.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** - * Test 75: Analyse-Skript für die Nextcloud-Verbindung (scripts/analyse_nextcloud.sh). + * Test 75: Analyse-Skript für die Nextcloud-Verbindung (scripts/analyse-nextcloud.sh). * * Das Skript ist ein Diagnosewerkzeug für den Produktivbetrieb. Es muss * ausführbar und syntaktisch gültig sein und darf ausschließlich lesen – @@ -16,10 +16,10 @@ class AnalyseNextcloudSkriptTest extends TestCase public function run(): void { - $path = dirname(__DIR__) . '/scripts/analyse_nextcloud.sh'; + $path = dirname(__DIR__) . '/scripts/analyse-nextcloud.sh'; - $this->assert(file_exists($path), 'scripts/analyse_nextcloud.sh existiert'); - $this->assert(is_executable($path), 'scripts/analyse_nextcloud.sh ist ausführbar'); + $this->assert(file_exists($path), 'scripts/analyse-nextcloud.sh existiert'); + $this->assert(is_executable($path), 'scripts/analyse-nextcloud.sh ist ausführbar'); exec('bash -n ' . escapeshellarg($path) . ' 2>&1', $output, $exitCode); $this->assertEquals(0, $exitCode, 'bash-Syntax ist gültig (' . implode(' ', $output) . ')'); diff --git a/tests/76_DatenverzeichnisUebernahmeTest.php b/tests/76_DatenverzeichnisUebernahmeTest.php index ae52ebf..37ee1ae 100644 --- a/tests/76_DatenverzeichnisUebernahmeTest.php +++ b/tests/76_DatenverzeichnisUebernahmeTest.php @@ -11,7 +11,7 @@ declare(strict_types=1); * unterdrückt wurden, meldete die Migration trotzdem Erfolg. Ergebnis: eine * Installation, in der die Belege nicht mehr auffindbar waren. * - * Der Test führt scripts/uploads-uebernahme.sh real aus und prüft, dass es + * Der Test führt scripts/intern/uploads-uebernahme.sh real aus und prüft, dass es * zusammenführt statt zu verschachteln und keine vorhandene Zieldatei überschreibt. */ class DatenverzeichnisUebernahmeTest extends TestCase @@ -20,9 +20,9 @@ class DatenverzeichnisUebernahmeTest extends TestCase public function run(): void { - $skript = dirname(__DIR__) . '/scripts/uploads-uebernahme.sh'; + $skript = dirname(__DIR__) . '/scripts/intern/uploads-uebernahme.sh'; - $this->assert(file_exists($skript), 'scripts/uploads-uebernahme.sh existiert'); + $this->assert(file_exists($skript), 'scripts/intern/uploads-uebernahme.sh existiert'); exec('sh -n ' . escapeshellarg($skript) . ' 2>&1', $syntax, $syntaxExit); $this->assertEquals(0, $syntaxExit, 'sh-Syntax ist gültig (' . implode(' ', $syntax) . ')'); @@ -40,7 +40,7 @@ class DatenverzeichnisUebernahmeTest extends TestCase $migration = dirname(__DIR__) . '/scripts/migrate-datenverzeichnis.sh'; $this->assert( str_contains((string)file_get_contents($migration), 'uploads-uebernahme.sh'), - 'migrate-datenverzeichnis.sh verwendet scripts/uploads-uebernahme.sh' + 'migrate-datenverzeichnis.sh verwendet scripts/intern/uploads-uebernahme.sh' ); // ── Funktionaler Lauf: Ziel enthält bereits ein Jahresverzeichnis ────── diff --git a/tests/e2e/README.md b/tests/e2e/README.md index cc7182d..312b173 100644 --- a/tests/e2e/README.md +++ b/tests/e2e/README.md @@ -28,7 +28,7 @@ aufgetretenen Fehler: 2. **Testnutzer existiert** (einmalig, idempotent wiederholbar): ```bash - docker compose exec web php scripts/e2e-user.php + docker compose exec web php scripts/container/e2e-user.php ``` Legt `e2e` / `e2e-test-Pass123!` an bzw. setzt Passwort und Sperrstatus @@ -77,8 +77,8 @@ Die Tests laufen gegen eine Instanz mit **echten Arbeitsdaten**. Deshalb gilt: Reste nach einem abgebrochenen Lauf entfernen: ```bash -docker compose exec web php scripts/e2e-cleanup.php # nur anzeigen -docker compose exec web php scripts/e2e-cleanup.php --force # löschen +docker compose exec web php scripts/container/e2e-cleanup.php # nur anzeigen +docker compose exec web php scripts/container/e2e-cleanup.php --force # löschen ``` ### Nummernkreise @@ -96,7 +96,7 @@ finalisiert wurde), bleibt der Zähler stehen — eine Nummer wird nie zweimal vergeben. In dem Fall entsteht wie bisher eine Lücke im Nummernkreis. Bricht ein Lauf mittendrin ab, bleiben Testdatensätze liegen; -`scripts/e2e-cleanup.php --force` räumt sie nachträglich ab und arbeitet dabei +`scripts/container/e2e-cleanup.php --force` räumt sie nachträglich ab und arbeitet dabei absteigend, damit möglichst viele Nummern zurückwandern. ## Aufbau diff --git a/tests/run.php b/tests/run.php index 4724a07..d0bb08a 100644 --- a/tests/run.php +++ b/tests/run.php @@ -32,7 +32,7 @@ TestCase::setQuiet($jsonMode); // PHP-Warnungen/-Notices einzelner Tests (z. B. absichtlich provozierte // Fehlerfälle) dürfen die JSON-Ausgabe auf STDOUT nicht verunreinigen – -// scripts/run-tests.sh wertet sie sonst nicht mehr auswertbar aus. +// scripts/container/run-tests.sh wertet sie sonst nicht mehr auswertbar aus. if ($jsonMode) { ini_set('display_errors', 'stderr'); }