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:
co-authored by
Claude Opus 5
parent
e3390c88f4
commit
a88273117c
@@ -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)
|
||||
Reference in New Issue
Block a user