assert( str_ends_with((string)Config::get('upload_dir'), '/' . $belegeRel), 'Config::upload_dir zeigt auf ' . $belegeRel ); $this->assert( str_ends_with((string)Config::get('logo_dir'), '/' . $logoRel), 'Config::logo_dir zeigt auf ' . $logoRel ); // backup.sh muss beide Verzeichnisse sichern und darf nicht mehr den // alten Web-Root-Pfad verwenden $backup = (string)file_get_contents($root . '/scripts/backup.sh'); $this->assertNotEmpty($backup, 'scripts/backup.sh ist lesbar'); $this->assert( str_contains($backup, 'cd /var/www/html/data') && str_contains($backup, 'for d in belege logo legacy-uploads uploads'), 'backup.sh sichert data/belege und data/logo samt Altablagen' ); $this->assert( !str_contains($backup, '-C /var/www/html/public/static uploads/'), 'backup.sh sichert nicht mehr fest aus dem Web-Root' ); $this->assert( str_contains($backup, 'WARNUNG: Das Datei-Backup enthält keine einzige Datei'), 'backup.sh warnt bei einem leeren Archiv' ); // restore.sh muss beide Archivformate bedienen können $restore = (string)file_get_contents($root . '/scripts/restore.sh'); $this->assertNotEmpty($restore, 'scripts/restore.sh ist lesbar'); $this->assert( str_contains($restore, '/var/www/html/data/belege') && str_contains($restore, '/var/www/html/data/logo'), 'restore.sh stellt Belege und Logo wieder her' ); $this->assert( str_contains($restore, 'for old in legacy-uploads uploads'), 'restore.sh verteilt alte Archive im uploads/-Format' ); $this->assert( !str_contains($restore, '-C /var/www/html/public/static/'), 'restore.sh entpackt nicht mehr in den Web-Root' ); // docker-compose.yml muss die Volumes bereitstellen $compose = (string)file_get_contents($root . '/docker-compose.yml'); $this->assert( str_contains($compose, './data/belege:/var/www/html/data/belege'), 'docker-compose.yml mountet data/belege' ); $this->assert( str_contains($compose, './data/logo:/var/www/html/data/logo'), 'docker-compose.yml mountet data/logo' ); $this->assert( !str_contains($compose, './data:/var/www/html/data'), 'Das gesamte data/ (inkl. mariadb) wird nicht mehr in den web-Container gemountet' ); // Der Entrypoint muss Altbestände selbsttätig übernehmen, damit ein // reines "git pull + docker compose up" keine Installation zerlegt $entrypoint = (string)file_get_contents($root . '/docker-entrypoint.sh'); $this->assert( str_contains($entrypoint, 'legacy-uploads'), 'Entrypoint übernimmt Altbestände aus der gemeinsamen uploads-Ablage' ); $this->assert( str_contains($entrypoint, 'public/static/uploads'), 'Entrypoint übernimmt auch ein Logo aus dem alten Web-Root' ); $this->assert( str_contains($compose, './uploads:/var/www/html/data/legacy-uploads'), 'docker-compose.yml stellt dem Entrypoint die alte Ablage bereit' ); // update.sh muss scripts/ aktualisieren, BEVOR backup.sh benutzt wird – // sonst sichert ein Update mit den Mitteln des Vorgängerstands. $update = (string)file_get_contents($root . '/scripts/update.sh'); $this->assertNotEmpty($update, 'scripts/update.sh ist lesbar'); $posSelfUpdate = strpos($update, 'STEUERKRAM_SELF_UPDATED'); $posBackupCall = strpos($update, 'scripts/backup.sh'); $this->assert($posSelfUpdate !== false, 'update.sh aktualisiert die Skripte vorab'); $this->assert( $posSelfUpdate !== false && $posBackupCall !== false && $posSelfUpdate < $posBackupCall, 'Die Skript-Vorabaktualisierung steht vor dem Backup-Aufruf' ); $this->assert( str_contains($update, 'STEUERKRAM_BEFORE_COMMIT'), 'update.sh reicht den Ausgangs-Commit weiter, damit die Rebuild-Erkennung greift' ); // .gitignore darf die Nutzdaten nicht ins Repo lassen, aber die README behalten $gitignore = (string)file_get_contents($root . '/.gitignore'); $this->assert(str_contains($gitignore, 'data/*'), '.gitignore schließt data/* aus'); $this->assert(str_contains($gitignore, '!data/README.md'), '.gitignore behält data/README.md'); } }