Fix #196: scripts/ nach Ausführungsort aufgeräumt (Host / Container / intern)

This commit is contained in:
fds2610-bot 2026-08-21 11:47:56 +02:00
parent 941f69b5e3
commit 9866b77c7d
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()`.
```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)

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

View file

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

View file

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

View file

@ -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 <username> <neues_passwort>
docker compose exec web php scripts/container/reset-password.php <username> <neues_passwort>
# 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
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

View file

@ -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
# ════════════════════════════════════════════════════════════════════════════

View file

@ -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);

View file

@ -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 <username> <passwort>
* 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 <username> <passwort>
* 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);

View file

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

View file

@ -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')) {

View file

@ -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')) {

View file

@ -1,11 +1,11 @@
<?php
/**
* 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) {
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);
}

View file

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

View file

@ -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);

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';
$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;

View file

@ -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 <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
# 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.

View file

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

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

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
# 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

View file

@ -345,7 +345,7 @@
</div>
<div class="form-hint">
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>

View file

@ -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) . ')');

View file

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

View file

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

View file

@ -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');
}