diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a55fc3..c9b2673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,51 @@ Wiederherstellung*. _Noch nichts._ +## [1.1.1] – 2026-08-23 + +### Behoben + +- **`./scripts/update.sh` endete in „Permission denied".** Die + Shell-Skripte lagen ohne Ausführungsrecht im Repository (Modus 644 statt + 755). Die Anleitung sagt `./scripts/update.sh` — auf Linux ging genau + das nicht. + + Ursache ist der Entwicklungsrechner: Unter Windows steht + `core.filemode=false`, dort merkt git von einem fehlenden + Ausführungsrecht nichts. Aufgefallen ist es erst auf dem Server. + + **Wer von 1.1.0 oder älter kommt**, braucht einmalig den Umweg — das + alte Skript ist ja noch das nicht ausführbare: + + ```bash + bash scripts/update.sh + ``` + + Danach stimmt das Recht, und der Punktschrägstrich funktioniert. + +- **`update.sh` setzt das Recht nach dem Umschalten selbst.** Wer Dateien + von Hand kopiert hat — etwa um an das Skript zu kommen, bevor es in der + laufenden Fassung existierte —, verliert es dabei. Ein `chmod +x` nach + dem Umschalten fängt das ab. + +- **Das Versionsskript datierte in UTC.** Eine Veröffentlichung nach + 22 Uhr deutscher Sommerzeit bekam im Changelog das Datum des Vortages — + diese Fassung hier war der erste Fall. Es rechnet jetzt in + `Europe/Berlin`, wie alles andere in dieser Anwendung auch. + +### Geprüft + +- Vier neue Prüfungen (`tests/skripte.test.ts`) sehen sich nicht das + Programm an, sondern die **Verpackung**: Liegt jedes `.sh` ausführbar im + Index? Hat es eine Shebang-Zeile? Kommt `scripts/` ins Docker-Abbild? + Sind die Sicherungsskripte in der Compose-Datei eingehängt? + + Das ist der zweite Fehler dieser Art — vorher fehlte `scripts/` ganz im + Abbild (1.0.1). Beide Male war der Code richtig und kein Test schlug an, + weil keiner hinsah, wie die Dateien beim Server ankommen. Jetzt tut es + einer. Die Gegenprobe ist gemacht: Mit zurückgesetztem Recht schlägt er + fehl und nennt die Datei. + ## [1.1.0] – 2026-08-22 ### Hinzugefügt @@ -466,7 +511,8 @@ Was davon wann angegangen wird, steht in [`docs/ROADMAP.md`](docs/ROADMAP.md). --- -[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.1.0...HEAD +[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.1.1...HEAD +[1.1.1]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.1.0...v1.1.1 [1.1.0]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.5...v1.1.0 [1.0.5]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.4...v1.0.5 [1.0.4]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.3...v1.0.4 diff --git a/README.md b/README.md index 3e76ee4..e9032f2 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,16 @@ existiert. ./scripts/update.sh ``` +> **Kommst du von 1.1.0 oder älter?** Dort lag `update.sh` ohne +> Ausführungsrecht im Repository — `./scripts/update.sh` endet dann in +> „Permission denied". Einmalig hilft: +> +> ```bash +> bash scripts/update.sh +> ``` +> +> Ab 1.1.1 ist das Recht gesetzt und der Punktschrägstrich funktioniert. + Das Skript sichert zuerst, zeigt den Changelog zwischen altem und neuem Stand, fragt nach, schaltet um, baut neu und führt danach die beiden SQL-Skripte aus. Schlägt die Sicherung fehl, wird nicht aktualisiert. @@ -573,7 +583,7 @@ Fehler, die während des Baus aufgefallen sind. | Zeilen TypeScript/TSX | 72.080 | `git ls-files "*.ts" "*.tsx" \| xargs wc -l` | | Prisma-Modelle | 33 | `grep -c '^model ' prisma/schema.prisma` | | Seiten | 79 | `find src/app -name page.tsx \| wc -l` | -| Unit-Tests (Vitest) | 496 | `npm test` | +| Unit-Tests (Vitest) | 500 | `npm test` | | End-to-End-Tests (Playwright) | 37 | `npm run test:e2e` | | TypeScript-Fehler | 0 | `npm run typecheck` | diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md index ec60e10..68ab85d 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -131,14 +131,14 @@ cd /opt/betreuungs-cockpit # Auf die letzte veröffentlichte Fassung wechseln, # nicht auf den laufenden Entwicklungsstand -git checkout v1.1.0 +git checkout v1.1.1 ``` ### Kontrolle ```bash ls docker-compose.yml Dockerfile .env.example # alle drei da -git describe --tags # v1.1.0 +git describe --tags # v1.1.1 ``` --- @@ -299,7 +299,7 @@ betreuungs-cockpit-sicherung-1 Up ```bash # Antwortet die Anwendung? (Port anpassen, falls du APP_PORT gesetzt hast) curl -s http://127.0.0.1:3000/api/gesundheit -# {"status":"bereit","version":"1.1.0","commit":"..."} +# {"status":"bereit","version":"1.1.1","commit":"..."} ``` Kommt hier nichts, sieh in die Logs: @@ -649,7 +649,7 @@ Erst **vom Proxy-Server aus** — kommt er überhaupt an? ```bash curl -s http://10.0.0.5:3000/api/gesundheit -# {"status":"bereit","version":"1.1.0",...} +# {"status":"bereit","version":"1.1.1",...} ``` Kommt hier nichts, liegt es an 7a: falsche `APP_BIND`, Firewall, oder der @@ -700,7 +700,7 @@ Zum Schluss: **Fristen → Pflichtfristen ableiten**. - Du kommst mit E-Mail, Passwort und dem Code aus der App herein. - Unter **Fristen** stehen Einträge mit Paragraphenbezug. - Unter **Protokoll** siehst du deine eigene Anmeldung. -- Unten links in der Seitenleiste steht `Version 1.1.0 · `. +- Unten links in der Seitenleiste steht `Version 1.1.1 · `. --- @@ -843,6 +843,16 @@ cd /opt/betreuungs-cockpit ./scripts/update.sh ``` +> **Kommst du von 1.1.0 oder älter?** Dort lag `update.sh` ohne +> Ausführungsrecht im Repository — `./scripts/update.sh` endet dann in +> „Permission denied". Einmalig hilft: +> +> ```bash +> bash scripts/update.sh +> ``` +> +> Ab 1.1.1 ist das Recht gesetzt und der Punktschrägstrich funktioniert. + Das war es. Das Skript nimmt dir die Schritte ab, die man von Hand vergisst: @@ -903,7 +913,7 @@ git fetch --tags git log --oneline HEAD..origin/main # 3. Umschalten -git checkout v1.1.0 +git checkout v1.1.1 # 4. Neu bauen und starten GIT_COMMIT=$(git rev-parse --short HEAD) BUILD_ZEIT=$(date -Iseconds) docker compose up -d --build diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index c09d1e3..4ac534b 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -9,7 +9,7 @@ Zeit spart. Er steht weit unten, wenn er nur schön wäre. Diese Datei ist eine Absichtserklärung, kein Vertrag. Was sich im Betrieb als wichtiger herausstellt, rutscht nach oben. -Stand: 22.08.2026 · Aktuelle Fassung: **1.1.0** ([CHANGELOG](../CHANGELOG.md)) +Stand: 23.08.2026 · Aktuelle Fassung: **1.1.1** ([CHANGELOG](../CHANGELOG.md)) --- diff --git a/package.json b/package.json index 2727f67..dd60e6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "betreuungs-cockpit", - "version": "1.1.0", + "version": "1.1.1", "private": true, "description": "Arbeits- und Dokumentationswerkzeug fuer die ehrenamtliche rechtliche Betreuung", "engines": { diff --git a/scripts/sicherung.sh b/scripts/sicherung.sh old mode 100644 new mode 100755 diff --git a/scripts/update.sh b/scripts/update.sh old mode 100644 new mode 100755 index 64ced70..3cc67a8 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -288,6 +288,13 @@ fi gut "$(git describe --tags --always)" +# Das Ausfuehrbarkeitsbit kommt normalerweise aus dem Repository mit. Wer +# Dateien von Hand kopiert hat - etwa um an dieses Skript zu kommen, bevor +# es in der laufenden Fassung existierte -, hat es verloren. Dann waere der +# naechste Aufruf ein "Permission denied", und man sucht an der falschen +# Stelle. +chmod +x scripts/*.sh 2>/dev/null || true + schritt "Bauen und starten" printf ' Das dauert beim ersten Mal einige Minuten.\n\n' diff --git a/scripts/version.mjs b/scripts/version.mjs index a662f69..ad9addb 100644 --- a/scripts/version.mjs +++ b/scripts/version.mjs @@ -97,7 +97,12 @@ schreiben('src/lib/version.ts', ersetzt) // --- 3. CHANGELOG.md -------------------------------------------------------- -const heute = new Date().toISOString().slice(0, 10) +// Deutsche Zeit, nicht UTC. Sonst bekommt eine Veroeffentlichung nach +// 22 Uhr (Sommerzeit) das Datum des Vortages - dieselbe Zeitzonenfalle, +// derentwegen die Fristen im Schema @db.Date sind. +const heute = new Intl.DateTimeFormat('sv-SE', { + timeZone: 'Europe/Berlin', +}).format(new Date()) let changelog = lesen('CHANGELOG.md') const UNVEROEFFENTLICHT = '## [Unveröffentlicht]' diff --git a/scripts/wiederherstellung.sh b/scripts/wiederherstellung.sh old mode 100644 new mode 100755 diff --git a/src/lib/version.ts b/src/lib/version.ts index 8c00fcc..40fcc5c 100644 --- a/src/lib/version.ts +++ b/src/lib/version.ts @@ -17,7 +17,7 @@ */ /** Muss zur "version" in der package.json passen. */ -export const VERSION = '1.1.0' +export const VERSION = '1.1.1' /** * Kurzer Git-Stand, beim Bauen hineingereicht: diff --git a/tests/skripte.test.ts b/tests/skripte.test.ts new file mode 100644 index 0000000..6a2fdbf --- /dev/null +++ b/tests/skripte.test.ts @@ -0,0 +1,87 @@ +import { execFileSync } from 'node:child_process' +import { readFileSync } from 'node:fs' +import { describe, expect, it } from 'vitest' + +/** + * Die Betriebsskripte müssen im Repository so liegen, dass sie auf dem + * Server auch laufen. + * + * Anlass sind zwei Fehler, die beide erst im Betrieb aufgefallen sind und + * beide nichts mit dem Programm zu tun hatten, sondern mit der Verpackung: + * + * 1. `scripts/` fehlte im Docker-Abbild — `node scripts/totp-geheimnis.mjs` + * lief ins Leere (behoben in 1.0.1). + * 2. Die `.sh`-Dateien lagen mit Modus 644 im Repository. Die Anleitung + * sagt `./scripts/update.sh`, auf Linux kam „Permission denied". + * + * Beide Male hat kein Test angeschlagen, weil beide Male der Code richtig + * war. Diese Datei prüft deshalb nicht Verhalten, sondern Verpackung. + * + * Der Ausführbarkeitsbit wird aus dem Git-Index gelesen, nicht vom + * Dateisystem: Unter Windows steht `core.filemode=false`, dort sagt das + * Dateisystem nichts aus. Maßgeblich ist, was ausgecheckt wird. + */ + +function gitVerfuegbar(): boolean { + try { + execFileSync('git', ['rev-parse', '--git-dir'], { stdio: 'pipe' }) + return true + } catch { + return false + } +} + +/** Liefert [Modus, Pfad] je Datei, so wie sie im Index steht. */ +function eintraegeImIndex(muster: string): [string, string][] { + const ausgabe = execFileSync('git', ['ls-files', '-s', muster], { encoding: 'utf8' }) + return ausgabe + .split('\n') + .filter((zeile) => zeile.trim() !== '') + .map((zeile) => { + const [angaben, pfad] = zeile.split('\t') + return [angaben!.split(' ')[0]!, pfad!] + }) +} + +const mitGit = gitVerfuegbar() + +describe.skipIf(!mitGit)('Betriebsskripte', () => { + it('legt jedes Shell-Skript ausführbar ab', () => { + const skripte = eintraegeImIndex('scripts/*.sh') + + // Wenn hier nichts steht, stimmt das Muster nicht mehr - das waere + // ein stiller Ausfall der Pruefung. + expect(skripte.length).toBeGreaterThan(0) + + for (const [modus, pfad] of skripte) { + expect(modus, `${pfad} muss ausführbar sein (100755, ist ${modus})`).toBe('100755') + } + }) + + it('gibt jedem Shell-Skript eine Shebang-Zeile', () => { + for (const [, pfad] of eintraegeImIndex('scripts/*.sh')) { + const ersteZeile = readFileSync(pfad, 'utf8').split('\n')[0] ?? '' + expect(ersteZeile, `${pfad} braucht eine Shebang-Zeile`).toMatch(/^#!/) + } + }) + + it('nimmt scripts/ ins Docker-Abbild auf', () => { + // Der Fehler aus 1.0.1: Die Wartungsskripte lagen im Repository, aber + // nicht im Behaelter. Aufgefallen ist es erst, als sie gebraucht wurden. + const dockerfile = readFileSync('Dockerfile', 'utf8') + // Die Zeile traegt zusaetzliche Schalter (--from, --chown) - deshalb + // nicht auf eine feste Reihenfolge festlegen, sondern nur darauf, dass + // scripts/ ins Abbild kopiert wird. + expect(dockerfile).toMatch(/^COPY.*\/scripts\s+\.\/scripts\s*$/m) + }) + + it('bindet die Sicherungsskripte aus scripts/ in die Compose-Datei ein', () => { + // Sie werden als Volume in den Sicherungsdienst gehaengt. Ein + // umbenanntes Skript faellt sonst erst um 03:15 Uhr auf - im Log, + // das niemand liest. + const compose = readFileSync('docker-compose.yml', 'utf8') + for (const name of ['sicherung.sh', 'wiederherstellung.sh']) { + expect(compose, `${name} wird nicht eingebunden`).toContain(`./scripts/${name}:`) + } + }) +})