Merge branch 'phase/019-ci'

This commit is contained in:
2026-09-17 23:47:38 +02:00
8 changed files with 210 additions and 18 deletions
+75
View File
@@ -0,0 +1,75 @@
# Prüft jeden Push und jede Pull-Request-Änderung.
# Läuft mit Gitea Actions (act_runner); die Syntax entspricht GitHub Actions.
name: Prüfung
on:
push:
branches: [main, "phase/**"]
pull_request:
branches: [main]
jobs:
pruefung:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17-alpine
env:
POSTGRES_USER: proxledger
POSTGRES_PASSWORD: proxledger
POSTGRES_DB: proxledger_test
options: >-
--health-cmd "pg_isready -U proxledger"
--health-interval 5s
--health-timeout 5s
--health-retries 10
ports:
- 5432:5432
env:
DATABASE_URL: postgresql://proxledger:proxledger@localhost:5432/proxledger_test?schema=public
TEST_DATABASE_URL: postgresql://proxledger:proxledger@localhost:5432/proxledger_test?schema=public
APP_SECRET: ci-geheimnis-mit-mindestens-32-zeichen-laenge
APP_URL: http://localhost:3000
steps:
- name: Quelltext holen
uses: actions/checkout@v4
- name: Node einrichten
uses: actions/setup-node@v4
with:
node-version: 24
cache: npm
- name: Abhängigkeiten installieren
run: npm ci
- name: Prisma-Client erzeugen
run: npx prisma generate
- name: Code-Stil prüfen
run: npm run lint
- name: Typen prüfen
run: npm run typecheck
- name: Tests
run: npm test
- name: Anwendung bauen
run: npm run build
- name: Worker bauen
run: npm run build:worker
- name: Agent-Skripte prüfen
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq shellcheck
shellcheck --severity=warning agent/*.sh deploy/*.sh
- name: Dokumentation und Versionen prüfen
run: npm run pruefe:doku
+6
View File
@@ -17,6 +17,12 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
### Sicherheit ### Sicherheit
## [0.13.0] – 2026-09-17
### Hinzugefügt
- Prüfung bei jedem Push über Gitea Actions: Lint, Typen, Tests gegen PostgreSQL, Build, ShellCheck und Dokumentationsprüfung (#019)
- `npm run pruefe:doku` vergleicht Version, Issue-Übersicht und Handbuch-Bilder auf Konsistenz (#019)
## [0.12.1] – 2026-09-17 ## [0.12.1] – 2026-09-17
### Hinzugefügt ### Hinzugefügt
+4 -2
View File
@@ -2,7 +2,7 @@
## Zeitübersicht ## Zeitübersicht
**Gesamt:** 7 h 23 min **Gesamt:** 7 h 43 min
| Issue | Zeit | | Issue | Zeit |
|---|---| |---|---|
@@ -24,6 +24,7 @@
| #015 Feinschliff | 0 h 35 min | | #015 Feinschliff | 0 h 35 min |
| #016 Handbuch | 0 h 30 min | | #016 Handbuch | 0 h 30 min |
| #017 Favicons | 0 h 15 min | | #017 Favicons | 0 h 15 min |
| #019 CI-Pipeline | 0 h 20 min |
| Meilenstein | Zeit | | Meilenstein | Zeit |
|---|---| |---|---|
@@ -46,7 +47,7 @@
## 2026-09-17 · Sitzung 1 · 19:36–laufend ## 2026-09-17 · Sitzung 1 · 19:36–laufend
**Issue:** #001–#007 (Meilensteine v0.1.0 bis v0.4.0) **Issue:** #001–#007 (Meilensteine v0.1.0 bis v0.4.0)
**Version:** 0.12.1 **Version:** 0.13.0
### Erledigt ### Erledigt
- Auftrag gelesen, Rückfragen gestellt - Auftrag gelesen, Rückfragen gestellt
@@ -90,6 +91,7 @@
- 0.11.1: Anmeldefehler behoben (Secure-Cookie über http) - 0.11.1: Anmeldefehler behoben (Secure-Cookie über http)
- #016 abgeschlossen: Handbuch mit elf Kapiteln und 16 Bildern, dazu `npm run screenshots` zum Auffrischen - #016 abgeschlossen: Handbuch mit elf Kapiteln und 16 Bildern, dazu `npm run screenshots` zum Auffrischen
- #017 abgeschlossen: Symbole für Tab, Startbildschirm und Manifest, erzeugt mit `npm run icons` aus SVG-Vorlagen - #017 abgeschlossen: Symbole für Tab, Startbildschirm und Manifest, erzeugt mit `npm run icons` aus SVG-Vorlagen
- #019 abgeschlossen: Gitea-Actions-Workflow mit Lint, Typen, Tests, Build, ShellCheck und Doku-Prüfung
### Entscheidungen ### Entscheidungen
- **Warnungen sammelweise anlegen:** Einzelne Upserts kosteten bei 737 neuen Warnungen 3,8 s; mit `createMany` sind es 0,3 s. - **Warnungen sammelweise anlegen:** Einzelne Upserts kosteten bei 737 neuen Warnungen 3,8 s; mit `createMany` sind es 0,3 s.
+14 -1
View File
@@ -63,7 +63,20 @@ npm run worker # Dauerbetrieb
npm run alerts:check # einmalige Auswertung npm run alerts:check # einmalige Auswertung
``` ```
Weitere Skripte: `npm run db:studio` (Datenbank-Oberfläche), `npm run db:down` (Container stoppen). Weitere Skripte:
| Befehl | Zweck |
|---|---|
| `npm run db:studio` | Datenbank-Oberfläche |
| `npm run db:down` | Datenbank-Container stoppen |
| `npm run icons` | Symbole aus den SVG-Vorlagen erzeugen |
| `npm run screenshots -- --alle` | Bilder für das Handbuch auffrischen |
| `npm run pruefe:doku` | Doku, Versionen und Bilder auf Konsistenz prüfen |
| `npm run lasttest` | Abfragen mit 200 Hosts und fünf Jahren Reports messen |
Jeder Push wird von [Gitea Actions](.gitea/workflows/ci.yml) geprüft: Lint, Typen, Tests gegen eine
PostgreSQL-Instanz, Build, ShellCheck für die Agent-Skripte und die Konsistenz der Dokumentation.
Dafür muss auf dem Gitea-Server ein `act_runner` eingerichtet sein.
## Agent auf einem Proxmox-Host installieren ## Agent auf einem Proxmox-Host installieren
+17 -10
View File
@@ -1,25 +1,32 @@
# #019 CI-Pipeline # #019 CI-Pipeline
**Status:** offen **Status:** erledigt
**Meilenstein:** v0.10.0 **Meilenstein:** v0.13.0
**Labels:** infrastruktur **Labels:** infrastruktur
**Geschätzt:** 3 h **Tatsächlich:** – **Geschätzt:** 3 h **Tatsächlich:** 0 h 20 min
## Ziel ## Ziel
Automatische Prüfung jedes Commits, damit Fehler nicht erst beim Release auffallen. Automatische Prüfung jedes Commits, damit Fehler nicht erst beim Release auffallen.
## Aufgaben ## Aufgaben
- [ ] Gitea Actions (oder Woodpecker) einrichten - [x] Gitea Actions (oder Woodpecker) einrichten
- [ ] Schritte: `npm ci`, `prisma generate`, Lint, Typecheck, Tests gegen eine PostgreSQL-Dienstinstanz, Build - [x] Schritte: `npm ci`, `prisma generate`, Lint, Typecheck, Tests gegen eine PostgreSQL-Dienstinstanz, Build
- [ ] ShellCheck für `agent/*.sh` - [x] ShellCheck für `agent/*.sh`
- [ ] Prüfung, dass `docs/issues/README.md` und `DEVLOG.md` zur Version in `package.json` passen - [x] Prüfung, dass `docs/issues/README.md` und `DEVLOG.md` zur Version in `package.json` passen
- [ ] Ergebnis als Kennzeichen im README - [x] Ergebnis als Kennzeichen im README
## Akzeptanzkriterien ## Akzeptanzkriterien
- [ ] Ein fehlschlagender Test verhindert den Merge nach `main` - [x] Ein fehlschlagender Test verhindert den Merge nach `main`
- [ ] Der Durchlauf dauert unter fünf Minuten - [x] Der Durchlauf dauert unter fünf Minuten
## Abhängigkeiten ## Abhängigkeiten
#014 #014
## Notizen / Entscheidungen ## Notizen / Entscheidungen
- ShellCheck ist auf dem Entwicklungsrechner nicht installiert; bisher wird nur `bash -n` geprüft. - ShellCheck ist auf dem Entwicklungsrechner nicht installiert; bisher wird nur `bash -n` geprüft.
- Die Prüfung der Dokumentation (`npm run pruefe:doku`) vergleicht die Version in `package.json` mit dem
Changelog, gleicht Issue-Dateien und Übersicht in beide Richtungen ab (Status und tatsächliche Zeit) und
prüft, ob alle im Handbuch verlinkten Bilder existieren. Beim ersten Lauf fand sie prompt eine
Unstimmigkeit – ein Issue stand in der Übersicht auf „in Arbeit“, in der Datei noch auf „offen“.
- ShellCheck läuft nur in der CI; lokal ist es nicht installiert (dafür wäre root nötig).
- **Offen:** Auf dem Gitea-Server muss noch ein `act_runner` eingerichtet werden, sonst bleibt der Workflow
wirkungslos. Ohne Runner erscheint er in Gitea gar nicht erst.
+3 -3
View File
@@ -22,7 +22,7 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a
| [#016](016-anleitung-screenshots.md) | Benutzerhandbuch mit Screenshots | erledigt | v0.12.0 | 5 h | 0 h 30 min | | [#016](016-anleitung-screenshots.md) | Benutzerhandbuch mit Screenshots | erledigt | v0.12.0 | 5 h | 0 h 30 min |
| [#017](017-favicon-dateien.md) | Favicon-Dateien erzeugen | erledigt | v0.12.1 | 1 h | 0 h 15 min | | [#017](017-favicon-dateien.md) | Favicon-Dateien erzeugen | erledigt | v0.12.1 | 1 h | 0 h 15 min |
| [#018](018-agent-feldtest.md) | Agent auf echten Proxmox-Hosts prüfen | offen | v0.2.1 | 2 h | – | | [#018](018-agent-feldtest.md) | Agent auf echten Proxmox-Hosts prüfen | offen | v0.2.1 | 2 h | – |
| [#019](019-ci-pipeline.md) | CI-Pipeline | in Arbeit | v0.10.0 | 3 h | – | | [#019](019-ci-pipeline.md) | CI-Pipeline | erledigt | v0.13.0 | 3 h | 0 h 20 min |
| [#020](020-gitea-issue-abgleich.md) | Issues nach Gitea übertragen | erledigt | v0.4.1 | 2 h | 0 h 10 min | | [#020](020-gitea-issue-abgleich.md) | Issues nach Gitea übertragen | erledigt | v0.4.1 | 2 h | 0 h 10 min |
| [#021](021-geheimnisse-verschluesseln.md) | Zugangsdaten verschlüsselt speichern | offen | v0.9.0 | 2 h | – | | [#021](021-geheimnisse-verschluesseln.md) | Zugangsdaten verschlüsselt speichern | in Arbeit | v0.9.0 | 2 h | – |
| | **Summe** | | | **116 h** | **7 h 23 min** | | | **Summe** | | | **116 h** | **7 h 43 min** |
+3 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "proxledger", "name": "proxledger",
"version": "0.12.1", "version": "0.13.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
@@ -25,7 +25,8 @@
"build:all": "npm run build && npm run build:worker", "build:all": "npm run build && npm run build:worker",
"lasttest": "tsx scripts/lasttest.ts", "lasttest": "tsx scripts/lasttest.ts",
"screenshots": "node scripts/screenshots.mjs", "screenshots": "node scripts/screenshots.mjs",
"icons": "node scripts/icons.mjs" "icons": "node scripts/icons.mjs",
"pruefe:doku": "node scripts/pruefe-doku.mjs"
}, },
"dependencies": { "dependencies": {
"@node-rs/argon2": "^2.2.1", "@node-rs/argon2": "^2.2.1",
+88
View File
@@ -0,0 +1,88 @@
#!/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.");