Gitea-Actions-Workflow mit Lint, Typen, Tests gegen PostgreSQL, Build, ShellCheck und einem Skript, das Version, Issue-Uebersicht und Handbuch-Bilder abgleicht. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
89 lines
3.1 KiB
JavaScript
89 lines
3.1 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Prüft, ob Dokumentation und Version zusammenpassen.
|
||
*
|
||
* npm run pruefe:doku
|
||
*
|
||
* Geprüft wird:
|
||
* 1. Die Version aus package.json hat einen Abschnitt im Changelog.
|
||
* 2. Jede Issue-Datei steht in der Übersicht – und umgekehrt.
|
||
* 3. Status und tatsächliche Zeit stimmen zwischen Issue-Datei und Übersicht überein.
|
||
* 4. Alle im Handbuch verlinkten Bilder sind vorhanden.
|
||
*/
|
||
|
||
import { readFile, readdir } from "node:fs/promises";
|
||
import path from "node:path";
|
||
|
||
const WURZEL = process.cwd();
|
||
const fehler = [];
|
||
|
||
function pruefe(bedingung, meldung) {
|
||
if (!bedingung) fehler.push(meldung);
|
||
}
|
||
|
||
async function versionImChangelog() {
|
||
const { version } = JSON.parse(await readFile(path.join(WURZEL, "package.json"), "utf8"));
|
||
const changelog = await readFile(path.join(WURZEL, "CHANGELOG.md"), "utf8");
|
||
|
||
pruefe(
|
||
changelog.includes(`## [${version}]`),
|
||
`Version ${version} aus package.json fehlt im CHANGELOG.md.`,
|
||
);
|
||
}
|
||
|
||
async function issuesUndUebersicht() {
|
||
const verzeichnis = path.join(WURZEL, "docs", "issues");
|
||
const dateien = (await readdir(verzeichnis)).filter((name) => /^\d{3}-.*\.md$/.test(name)).sort();
|
||
const uebersicht = await readFile(path.join(verzeichnis, "README.md"), "utf8");
|
||
|
||
for (const datei of dateien) {
|
||
pruefe(uebersicht.includes(`(${datei})`), `Issue ${datei} fehlt in docs/issues/README.md.`);
|
||
|
||
const inhalt = await readFile(path.join(verzeichnis, datei), "utf8");
|
||
const status = /^\*\*Status:\*\*\s*(.+)$/m.exec(inhalt)?.[1]?.trim();
|
||
const zeit = /^\*\*Geschätzt:\*\*.*\*\*Tatsächlich:\*\*\s*(.+)$/m.exec(inhalt)?.[1]?.trim();
|
||
|
||
const zeile = uebersicht.split("\n").find((z) => z.includes(`(${datei})`));
|
||
if (!zeile) continue;
|
||
|
||
pruefe(
|
||
zeile.includes(`| ${status} |`),
|
||
`Status von ${datei} ist „${status}“, die Übersicht sagt etwas anderes: ${zeile.trim()}`,
|
||
);
|
||
if (zeit && zeit !== "–") {
|
||
pruefe(
|
||
zeile.includes(zeit),
|
||
`Zeit von ${datei} ist „${zeit}“, steht so aber nicht in der Übersicht.`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// Umgekehrt: Zeilen in der Übersicht ohne zugehörige Datei.
|
||
for (const treffer of uebersicht.matchAll(/\((\d{3}-[^)]+\.md)\)/g)) {
|
||
pruefe(dateien.includes(treffer[1]), `Übersicht verweist auf ${treffer[1]}, die Datei fehlt.`);
|
||
}
|
||
}
|
||
|
||
async function bilderImHandbuch() {
|
||
const handbuch = path.join(WURZEL, "docs", "anleitung");
|
||
const text = await readFile(path.join(handbuch, "README.md"), "utf8");
|
||
const bilder = [...text.matchAll(/!\[[^\]]*\]\((bilder\/[^)]+)\)/g)].map((t) => t[1]);
|
||
const vorhanden = new Set(await readdir(path.join(handbuch, "bilder")));
|
||
|
||
for (const bild of bilder) {
|
||
pruefe(vorhanden.has(path.basename(bild)), `Handbuch verweist auf ${bild}, die Datei fehlt.`);
|
||
}
|
||
}
|
||
|
||
await versionImChangelog();
|
||
await issuesUndUebersicht();
|
||
await bilderImHandbuch();
|
||
|
||
if (fehler.length > 0) {
|
||
console.error("Die Dokumentation passt nicht zusammen:\n");
|
||
for (const meldung of fehler) console.error(` · ${meldung}`);
|
||
process.exit(1);
|
||
}
|
||
|
||
console.log("Dokumentation, Versionen und Bilder passen zusammen.");
|