chore: Screenshot-Kette und Demo-Seed

npm run screenshots startet eine Wegwerf-Instanz in einem eigenen
Compose-Projekt mit eigener Datenbank, spielt ausschließlich erfundene
Daten ein, fährt die Seiten mit Playwright ab und räumt am Ende alles
wieder weg. Je Seite entstehen vier Bilder: hell und dunkel, Schreibtisch
und Handy. Die Uhr steht fest, damit sich zwischen zwei Läufen nur das
ändert, was sich wirklich geändert hat.

Der Demo-Seed verweigert den Dienst ohne DEMO_MODUS=true — er würde sonst
erfundene Daten in eine echte Datenbank schreiben.

Test- und Werkzeugordner sind jetzt aus dem Docker-Kontext ausgenommen.
Ein Typfehler in scripts/screenshots.ts hatte den Bau des Abbilds
blockiert, obwohl die Datei dort nichts zu suchen hat.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-10 14:29:52 +02:00
co-authored by Claude Opus 5
parent e3390c88f4
commit a88273117c
8 changed files with 503 additions and 3 deletions
+142
View File
@@ -0,0 +1,142 @@
/**
* Nimmt die Bilder für das Handbuch auf.
*
* Startet eine Wegwerf-Instanz in einem eigenen Compose-Projekt mit eigener
* Datenbank, spielt ausschließlich erfundene Daten ein, fährt die Seiten mit
* Playwright ab und räumt am Ende alles wieder weg — auch wenn unterwegs etwas
* schiefgeht.
*
* An eine Instanz mit echten Daten kommt dieser Ablauf nicht heran: die
* Datenbank entsteht neu, wird nur über ihren eigenen Port angesprochen und
* samt Volume wieder entfernt.
*
* Aufruf: npm run screenshots
*/
import { spawnSync } from 'node:child_process'
import { fileURLToPath } from 'node:url'
const WURZEL = fileURLToPath(new URL('..', import.meta.url))
const PROJEKT = 'planer-screenshots'
const COMPOSE_DATEI = 'docker-compose.screenshots.yml'
const APP_PORT = process.env.SCREENSHOT_APP_PORT ?? '3200'
const DB_PORT = process.env.SCREENSHOT_DB_PORT ?? '5455'
const UMGEBUNG = {
...process.env,
SCREENSHOT_APP_PORT: APP_PORT,
SCREENSHOT_DB_PORT: DB_PORT,
}
function meldung(text: string): void {
console.log(`▸ ${text}`)
}
function laufen(
befehl: string,
argumente: string[],
zusatz: Record<string, string> = {},
): number {
const ergebnis = spawnSync(befehl, argumente, {
cwd: WURZEL,
stdio: 'inherit',
env: { ...UMGEBUNG, ...zusatz },
})
return ergebnis.status ?? 1
}
function compose(argumente: string[]): number {
return laufen('docker', ['compose', '-p', PROJEKT, '-f', COMPOSE_DATEI, ...argumente])
}
function node(skript: string, zusatz: Record<string, string>): number {
return laufen(
process.execPath,
[`${WURZEL}node_modules/tsx/dist/cli.mjs`, skript],
zusatz,
)
}
async function wartenAufGesundheit(sekunden: number): Promise<boolean> {
const ziel = `http://127.0.0.1:${APP_PORT}/api/health`
const frist = Date.now() + sekunden * 1000
while (Date.now() < frist) {
try {
const antwort = await fetch(ziel)
if (antwort.ok) {
const inhalt = (await antwort.json()) as { status?: string }
if (inhalt.status === 'ok') return true
}
} catch {
// Noch nicht da, gleich noch einmal.
}
await new Promise((weiter) => setTimeout(weiter, 2000))
}
return false
}
function aufraeumen(): void {
meldung('Wegwerf-Instanz entfernen.')
compose(['down', '-v', '--remove-orphans'])
}
async function main(): Promise<number> {
if (laufen('docker', ['info']) !== 0) {
console.error('Der Docker-Daemon läuft nicht.')
return 1
}
// Reste eines abgebrochenen Laufs zuerst wegräumen.
compose(['down', '-v', '--remove-orphans'])
meldung(`Wegwerf-Instanz bauen und starten (App auf ${APP_PORT}, DB auf ${DB_PORT}).`)
if (compose(['up', '-d', '--build']) !== 0) {
console.error('Die Wegwerf-Instanz ließ sich nicht starten.')
return 1
}
meldung('Auf die Anwendung warten.')
if (!(await wartenAufGesundheit(180))) {
console.error('Die Anwendung ist nicht gesund geworden.')
compose(['logs', '--tail', '50', 'app'])
return 1
}
meldung('Erfundene Daten einspielen.')
const demoUrl = `postgresql://demo:demo@127.0.0.1:${DB_PORT}/demo?schema=public`
if (node('prisma/demo-seed.ts', { DATABASE_URL: demoUrl, DEMO_MODUS: 'true' }) !== 0) {
console.error('Der Demo-Seed ist fehlgeschlagen.')
return 1
}
meldung('Bilder aufnehmen.')
const playwright = laufen(
process.execPath,
[
`${WURZEL}node_modules/@playwright/test/cli.js`,
'test',
'--config',
'playwright.screenshots.config.ts',
],
{ SCREENSHOT_APP_PORT: APP_PORT },
)
if (playwright !== 0) {
console.error('Die Aufnahme ist fehlgeschlagen.')
return 1
}
meldung('Fertig. Die Bilder liegen in docs/bilder/.')
return 0
}
let code = 1
try {
code = await main()
} catch (fehler) {
console.error('Der Screenshot-Lauf ist abgebrochen:', fehler)
} finally {
aufraeumen()
}
process.exit(code)