Merge pull request 'Fix #196: scripts/ nach Ausführungsort aufgeräumt (Host / Container / intern)' (#199) from fds2610-bot/Steuerkram:main into main

Reviewed-on: https://codeberg.org/fds2610/Steuerkram/pulls/199
This commit is contained in:
fds2610 2026-08-21 12:52:58 +02:00
commit aa5ab7c06d
29 changed files with 113 additions and 146 deletions

View file

@ -56,9 +56,9 @@ Tests liegen in `tests/` und folgen dem Namensschema `NN_FunktionsnameTest.php`.
Jede Testklasse erweitert `TestCase` und implementiert `run()`. Jede Testklasse erweitert `TestCase` und implementiert `run()`.
```bash ```bash
docker compose exec -u www-data web php tests/run.php # alle Tests ausführen ./scripts/test.sh # 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/container/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 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 **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`. (auf dem Host wird nichts installiert). Details: `tests/e2e/README.md`.
```bash ```bash
docker compose exec web php scripts/e2e-user.php # einmalig: Testnutzer anlegen docker compose exec web php scripts/container/e2e-user.php # einmalig: Testnutzer anlegen
tests/e2e/run.sh # alle E2E-Tests ./scripts/e2e.sh # alle E2E-Tests
docker compose exec web php scripts/e2e-cleanup.php --force # Reste entfernen 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. 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ä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_...`) - 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 - Jede Migration muss idempotent sein oder mit `IF NOT EXISTS` / `ON DUPLICATE KEY` abgesichert werden
```bash ```bash
@ -131,7 +131,7 @@ Schemaänderungen (neue Spalten, Tabellen, Kontenanpassungen) kommen **ausschlie
ls migrations/ | tail -1 ls migrations/ | tail -1
# Migration manuell testen (ohne Neustart): # 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) ## Geplante Aufgaben (Cron)

View file

@ -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) ## 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. - **`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.

View file

@ -137,7 +137,7 @@ docker compose up -d --build
## Belege automatisch aus Nextcloud importieren ## 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 ### Konfiguration
@ -148,7 +148,7 @@ Empfehlung: Ein Nextcloud-App-Passwort verwenden (Nextcloud → Einstellungen
### Manueller Aufruf ### Manueller Aufruf
```bash ```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) ### 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 ```cron
*/15 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ */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. 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 ```cron
*/15 * * * * docker compose -f /home/fds/projects/Steuerkram/docker-compose.yml exec -T web \ */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. > **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. 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 ### Vollständige Update-Anleitung
@ -295,7 +295,7 @@ git log --oneline -5
docker compose up -d --build 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 #### Schritt 4 – Migrationen prüfen
@ -355,15 +355,15 @@ Anschließend im Browser unter `http://localhost:8080` (oder dem konfigurierten
## Tests ## Tests
```bash ```bash
docker compose exec -u www-data web php tests/run.php # PHP-Suite (Models, Services, Berechnungen) ./scripts/test.sh # PHP-Suite (Models, Services, Berechnungen)
tests/e2e/run.sh # Browser-Tests (Routing, Views, Formular-JS) ./scripts/e2e.sh # Browser-Tests (Routing, Views, Formular-JS)
``` ```
Die PHP-Suite läuft ohne weitere Voraussetzungen. Das `-u www-data` gehört dazu: 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 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, später nicht mehr beschreiben kann. Die E2E-Tests brauchen Docker,
das [e2e-runner-Projekt](../e2e-runner/) und einen Testnutzer 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). sie mit einer Warnung übersprungen. Details in [tests/e2e/README.md](tests/e2e/README.md).
--- ---

View file

@ -14,10 +14,10 @@ mkdir -p "$BELEGE" "$LOGO" 2>/dev/null || true
# Installation endet, deren Dateien nicht mehr gefunden werden. # Installation endet, deren Dateien nicht mehr gefunden werden.
# Idempotent: Nach dem Umzug sind die Quellverzeichnisse leer. # Idempotent: Nach dem Umzug sind die Quellverzeichnisse leer.
# Die eigentliche Übernahme liegt in scripts/uploads-uebernahme.sh – dieses # Die eigentliche Übernahme liegt in scripts/intern/uploads-uebernahme.sh –
# Verzeichnis ist als Volume gemountet, der Entrypoint dagegen steckt im Image. # dieses Verzeichnis ist als Volume gemountet, der Entrypoint dagegen steckt
# So wirkt eine Korrektur am Umzug auch ohne Rebuild. # im Image. So wirkt eine Korrektur am Umzug auch ohne Rebuild.
UEBERNAHME=/var/www/html/scripts/uploads-uebernahme.sh UEBERNAHME=/var/www/html/scripts/intern/uploads-uebernahme.sh
# Logo aus dem alten Web-Root (Stand vor Fix #116). Übergeben wird direkt der # 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. # 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 chown -R www-data:www-data "$BELEGE" "$LOGO" 2>/dev/null || true
# Datenbank-Migrationen automatisch einspielen # Datenbank-Migrationen automatisch einspielen
php /var/www/html/scripts/migrate.php php /var/www/html/scripts/intern/migrate.php
exec "$@" exec "$@"

View file

@ -281,4 +281,4 @@ UNIQUE KEY auf `(year, month)`.
## Migrationen ## 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.

View file

@ -62,7 +62,7 @@ LLM_MODEL=gpt-4o-mini
## Nextcloud-Import einrichten ## 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 ### Zugangsdaten konfigurieren
@ -73,7 +73,7 @@ Hinweis: Ein Nextcloud-App-Passwort verwenden (Nextcloud → Einstellungen → S
### Manueller Aufruf ### Manueller Aufruf
```bash ```bash
docker compose exec web php scripts/nextcloud-import.php docker compose exec web php scripts/container/nextcloud-import.php
``` ```
### Automatisch per Cron ### Automatisch per Cron
@ -82,7 +82,7 @@ Cron-Eintrag auf dem Host anlegen (`crontab -e`):
```cron ```cron
*/15 * * * * docker compose -f /opt/Steuerkram/docker-compose.yml exec -T web \ */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. > 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 ### 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 ```bash
./scripts/analyse_nextcloud.sh # Konfiguration, DNS, Verbindungsversuch ./scripts/analyse-nextcloud.sh # Konfiguration, DNS, Verbindungsversuch
./scripts/analyse_nextcloud.sh --verbose # zusätzlich ein Auszug der Server-Antwort ./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. 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 ## 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 ```bash
docker compose exec web php scripts/reset-password.php <username> <neues_passwort> docker compose exec web php scripts/container/reset-password.php <username> <neues_passwort>
# Beispiel: # Beispiel:
docker compose exec web php scripts/reset-password.php Meinuser geheim123 docker compose exec web php scripts/container/reset-password.php Meinuser geheim123
``` ```

View file

@ -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 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` 5. **Container-Neustart** – `docker compose down --remove-orphans` + `up -d`
6. **Healthcheck** – Wartet bis die Datenbank `healthy` meldet (max. 120 s) 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 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 ### Tests als Gate
@ -91,7 +91,7 @@ zum manuellen Zurückspielen.
### Migrationen ### 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... [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: Migrationen können auch manuell ausgelöst werden:
```bash ```bash
docker compose exec web php scripts/migrate.php docker compose exec web php scripts/intern/migrate.php
``` ```
### Diagnose bei Fehlern ### Diagnose bei Fehlern

View file

@ -11,7 +11,7 @@
# Das Passwort wird nie im Klartext ausgegeben. # Das Passwort wird nie im Klartext ausgegeben.
# #
# Usage: # Usage:
# ./analyse_nextcloud.sh [--verbose] # ./analyse-nextcloud.sh [--verbose]
# #
# Exit-Code: 0 = Verbindung steht, 1 = Konfigurations- oder Verbindungsfehler # Exit-Code: 0 = Verbindung steht, 1 = Konfigurations- oder Verbindungsfehler
# ════════════════════════════════════════════════════════════════════════════ # ════════════════════════════════════════════════════════════════════════════

View file

@ -12,11 +12,11 @@ declare(strict_types=1);
* echte Buchführungsdaten bleiben unangetastet. * echte Buchführungsdaten bleiben unangetastet.
* *
* Verwendung: * Verwendung:
* docker compose exec web php scripts/e2e-cleanup.php # nur anzeigen * docker compose exec web php scripts/container/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 --force # wirklich löschen
*/ */
$projectRoot = dirname(__DIR__); $projectRoot = dirname(__DIR__, 2);
if (!file_exists($projectRoot . '/vendor/autoload.php')) { if (!file_exists($projectRoot . '/vendor/autoload.php')) {
fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n"); fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n");
exit(1); exit(1);

View file

@ -14,14 +14,14 @@ declare(strict_types=1);
* und Status zurückgesetzt (aktiv, keine Fehlversuche, nicht gesperrt). * und Status zurückgesetzt (aktiv, keine Fehlversuche, nicht gesperrt).
* *
* Verwendung: * Verwendung:
* docker compose exec web php scripts/e2e-user.php * docker compose exec web php scripts/container/e2e-user.php
* docker compose exec web php scripts/e2e-user.php <username> <passwort> * docker compose exec web php scripts/container/e2e-user.php <username> <passwort>
* docker compose exec web php scripts/e2e-user.php --delete * docker compose exec web php scripts/container/e2e-user.php --delete
* *
* Die Defaults entsprechen tests/e2e/helpers/env.js. * Die Defaults entsprechen tests/e2e/helpers/env.js.
*/ */
$projectRoot = dirname(__DIR__); $projectRoot = dirname(__DIR__, 2);
if (!file_exists($projectRoot . '/vendor/autoload.php')) { if (!file_exists($projectRoot . '/vendor/autoload.php')) {
fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n"); fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n");
exit(1); exit(1);

View file

@ -7,14 +7,14 @@
* werden übersprungen. Nach Ausführung kann es gefahrlos erneut aufgerufen werden. * werden übersprungen. Nach Ausführung kann es gefahrlos erneut aufgerufen werden.
* *
* Aufruf: * 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). * Voraussetzung: APP_KEY in .env gesetzt (64-stellige Hex-Zeichenkette).
*/ */
declare(strict_types=1); declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../../vendor/autoload.php';
use Steuerkram\Config; use Steuerkram\Config;
use Steuerkram\Database; use Steuerkram\Database;

View file

@ -9,16 +9,16 @@
* Voraussetzung: ki_vorschlag_aktiv = 1 in den Einstellungen, Ollama erreichbar. * Voraussetzung: ki_vorschlag_aktiv = 1 in den Einstellungen, Ollama erreichbar.
* *
* Aufruf: * 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): * Für optionalen eigenen Cronjob (manuelle Uploads ohne Nextcloud):
* *\/5 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ * *\/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); declare(strict_types=1);
chdir(dirname(__DIR__)); chdir(dirname(__DIR__, 2));
// DB-Zugangsdaten aus .env laden (für CLI-Kontext ohne Apache-Umgebung) // DB-Zugangsdaten aus .env laden (für CLI-Kontext ohne Apache-Umgebung)
if (file_exists('.env')) { if (file_exists('.env')) {

View file

@ -11,16 +11,16 @@
* Konfiguration: Einstellungen → Nextcloud-Belegimport in der Steuerkram-UI. * Konfiguration: Einstellungen → Nextcloud-Belegimport in der Steuerkram-UI.
* *
* Aufruf: * 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): * Für Cronjob (auf dem Host):
* *\/15 * * * * docker compose -f /opt/steuerkram/docker-compose.yml exec -T web \ * *\/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); declare(strict_types=1);
chdir(dirname(__DIR__)); chdir(dirname(__DIR__, 2));
// DB-Zugangsdaten aus .env laden (für CLI-Kontext ohne Apache-Umgebung) // DB-Zugangsdaten aus .env laden (für CLI-Kontext ohne Apache-Umgebung)
if (file_exists('.env')) { if (file_exists('.env')) {

View file

@ -1,11 +1,11 @@
<?php <?php
/** /**
* Passwort eines Benutzers zurücksetzen. * Passwort eines Benutzers zurücksetzen.
* Aufruf: php scripts/reset-password.php <username> <neues_passwort> * Aufruf: php scripts/container/reset-password.php <username> <neues_passwort>
*/ */
if ($argc !== 3) { if ($argc !== 3) {
echo 'Verwendung: php scripts/reset-password.php <username> <neues_passwort>' . PHP_EOL; echo 'Verwendung: php scripts/container/reset-password.php <username> <neues_passwort>' . PHP_EOL;
exit(1); exit(1);
} }

View file

@ -4,7 +4,7 @@
# Steuerkram Test-Runner mit automatischer Issue-Erstellung # Steuerkram Test-Runner mit automatischer Issue-Erstellung
# #
# Verwendung (im Container): # 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: # Parameter:
# ISSUE_NUMMER (optional) Nummer des aktuell bearbeiteten Issues, # ISSUE_NUMMER (optional) Nummer des aktuell bearbeiteten Issues,
@ -19,7 +19,7 @@
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")" PROJECT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
CURRENT_ISSUE="${1:-}" CURRENT_ISSUE="${1:-}"
REPO="fds2610/Steuerkram" REPO="fds2610/Steuerkram"
API_BASE="https://codeberg.org/api/v1" API_BASE="https://codeberg.org/api/v1"

View file

@ -13,10 +13,10 @@ declare(strict_types=1);
* - 3 Ausgangsrechnungen (Januar, Februar, März 2026) * - 3 Ausgangsrechnungen (Januar, Februar, März 2026)
* *
* Verwendung: * 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')) { if (!file_exists($projectRoot . '/vendor/autoload.php')) {
fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n"); fwrite(STDERR, "Fehler: vendor/autoload.php nicht gefunden. Bitte 'composer install' ausführen.\n");
exit(1); exit(1);

12
scripts/e2e.sh Executable file
View file

@ -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" "$@"

View file

@ -9,7 +9,7 @@ $host = getenv('DB_HOST') ?: 'db';
$dbName = getenv('DB_NAME') ?: 'steuerkram'; $dbName = getenv('DB_NAME') ?: 'steuerkram';
$dbUser = getenv('DB_USER') ?: 'steuerkram'; $dbUser = getenv('DB_USER') ?: 'steuerkram';
$dbPass = getenv('DB_PASS') ?: 'steuerkram'; $dbPass = getenv('DB_PASS') ?: 'steuerkram';
$migrDir = __DIR__ . '/../migrations'; $migrDir = __DIR__ . '/../../migrations';
function mlog(string $msg): void { function mlog(string $msg): void {
echo '[migrate] ' . $msg . PHP_EOL; echo '[migrate] ' . $msg . PHP_EOL;

View file

@ -2,11 +2,11 @@
# Übernimmt den Altbestand aus dem gemeinsamen uploads-Verzeichnis in die # Übernimmt den Altbestand aus dem gemeinsamen uploads-Verzeichnis in die
# getrennten Datenverzeichnisse data/belege und data/logo. # getrennten Datenverzeichnisse data/belege und data/logo.
# #
# scripts/uploads-uebernahme.sh <uploads-verzeichnis> <belege-verzeichnis> <logo-verzeichnis> # scripts/intern/uploads-uebernahme.sh <uploads-verzeichnis> <belege-verzeichnis> <logo-verzeichnis>
# #
# Aufrufer sind docker-entrypoint.sh (bei jedem Container-Start) und # Nie manuell aufrufen. Aufrufer sind docker-entrypoint.sh (bei jedem
# scripts/migrate-datenverzeichnis.sh (manueller Umzug). Die Logik liegt bewusst # Container-Start) und scripts/migrate-datenverzeichnis.sh (manueller Umzug).
# hier und nicht doppelt in beiden Skripten. # Die Logik liegt bewusst hier und nicht doppelt in beiden Skripten.
# #
# Idempotent: Ist der Altbestand übernommen, findet ein zweiter Lauf nichts mehr. # Idempotent: Ist der Altbestand übernommen, findet ein zweiter Lauf nichts mehr.
# Exit-Code 0 = nichts zu tun oder vollständig übernommen, 1 = Reste verblieben. # Exit-Code 0 = nichts zu tun oder vollständig übernommen, 1 = Reste verblieben.

View file

@ -51,7 +51,7 @@ docker run --rm \
mkdir -p /neu/belege /neu/logo mkdir -p /neu/belege /neu/logo
# Gemeinsame Übernahme-Logik – dieselbe, die auch beim Container-Start läuft. # 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 chown -R 33:33 /neu/belege /neu/logo

View file

@ -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"

View file

@ -134,7 +134,7 @@ echo " OK"
# --- 3. Migrationen --- # --- 3. Migrationen ---
echo "[3/3] Migrationen prüfen und einspielen..." 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/^/ /' echo "$MIGRATE_OUT" | sed 's/^/ /'
if echo "$MIGRATE_OUT" | grep -q "FEHLER"; then if echo "$MIGRATE_OUT" | grep -q "FEHLER"; then
echo "" echo ""

12
scripts/test.sh Executable file
View file

@ -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 "$@"

View file

@ -347,7 +347,7 @@ ok "Autoloader aktualisiert"
# nochmals explizit aus um sicherzustellen, dass alle eingespielt sind # nochmals explizit aus um sicherzustellen, dass alle eingespielt sind
# und um den Status im Log zu sehen. # und um den Status im Log zu sehen.
info "Führe Migrationen aus (idempotent)..." 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" fail "Migrationen fehlgeschlagen"
echo "$MIGRATE_OUTPUT" | while read -r line; do info "$line"; done echo "$MIGRATE_OUTPUT" | while read -r line; do info "$line"; done
UPDATE_FAILED=true UPDATE_FAILED=true

View file

@ -345,7 +345,7 @@
</div> </div>
<div class="form-hint"> <div class="form-hint">
Import manuell starten: Import manuell starten:
<code>docker compose exec web php scripts/nextcloud-import.php</code> <code>docker compose exec web php scripts/container/nextcloud-import.php</code>
</div> </div>
</div> </div>
</div> </div>

View file

@ -3,7 +3,7 @@
declare(strict_types=1); 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 * Das Skript ist ein Diagnosewerkzeug für den Produktivbetrieb. Es muss
* ausführbar und syntaktisch gültig sein und darf ausschließlich lesen – * ausführbar und syntaktisch gültig sein und darf ausschließlich lesen –
@ -16,10 +16,10 @@ class AnalyseNextcloudSkriptTest extends TestCase
public function run(): void 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(file_exists($path), 'scripts/analyse-nextcloud.sh existiert');
$this->assert(is_executable($path), 'scripts/analyse_nextcloud.sh ist ausführbar'); $this->assert(is_executable($path), 'scripts/analyse-nextcloud.sh ist ausführbar');
exec('bash -n ' . escapeshellarg($path) . ' 2>&1', $output, $exitCode); exec('bash -n ' . escapeshellarg($path) . ' 2>&1', $output, $exitCode);
$this->assertEquals(0, $exitCode, 'bash-Syntax ist gültig (' . implode(' ', $output) . ')'); $this->assertEquals(0, $exitCode, 'bash-Syntax ist gültig (' . implode(' ', $output) . ')');

View file

@ -11,7 +11,7 @@ declare(strict_types=1);
* unterdrückt wurden, meldete die Migration trotzdem Erfolg. Ergebnis: eine * unterdrückt wurden, meldete die Migration trotzdem Erfolg. Ergebnis: eine
* Installation, in der die Belege nicht mehr auffindbar waren. * 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. * zusammenführt statt zu verschachteln und keine vorhandene Zieldatei überschreibt.
*/ */
class DatenverzeichnisUebernahmeTest extends TestCase class DatenverzeichnisUebernahmeTest extends TestCase
@ -20,9 +20,9 @@ class DatenverzeichnisUebernahmeTest extends TestCase
public function run(): void 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); exec('sh -n ' . escapeshellarg($skript) . ' 2>&1', $syntax, $syntaxExit);
$this->assertEquals(0, $syntaxExit, 'sh-Syntax ist gültig (' . implode(' ', $syntax) . ')'); $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'; $migration = dirname(__DIR__) . '/scripts/migrate-datenverzeichnis.sh';
$this->assert( $this->assert(
str_contains((string)file_get_contents($migration), 'uploads-uebernahme.sh'), 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 ────── // ── Funktionaler Lauf: Ziel enthält bereits ein Jahresverzeichnis ──────

View file

@ -28,7 +28,7 @@ aufgetretenen Fehler:
2. **Testnutzer existiert** (einmalig, idempotent wiederholbar): 2. **Testnutzer existiert** (einmalig, idempotent wiederholbar):
```bash ```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 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: Reste nach einem abgebrochenen Lauf entfernen:
```bash ```bash
docker compose exec web php scripts/e2e-cleanup.php # nur anzeigen docker compose exec web php scripts/container/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 --force # löschen
``` ```
### Nummernkreise ### 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. vergeben. In dem Fall entsteht wie bisher eine Lücke im Nummernkreis.
Bricht ein Lauf mittendrin ab, bleiben Testdatensätze liegen; 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. absteigend, damit möglichst viele Nummern zurückwandern.
## Aufbau ## Aufbau

View file

@ -32,7 +32,7 @@ TestCase::setQuiet($jsonMode);
// PHP-Warnungen/-Notices einzelner Tests (z. B. absichtlich provozierte // PHP-Warnungen/-Notices einzelner Tests (z. B. absichtlich provozierte
// Fehlerfälle) dürfen die JSON-Ausgabe auf STDOUT nicht verunreinigen – // 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) { if ($jsonMode) {
ini_set('display_errors', 'stderr'); ini_set('display_errors', 'stderr');
} }