feat: Versionierung mit Changelog, Version 1.0.0

Bis jetzt stand nirgends, welcher Stand laeuft. Beim Aktualisieren auf
einem Server ist das die erste Frage.

CHANGELOG.md nach Keep a Changelog, Versionsnummern nach SemVer. Der
Changelog sagt, was sich beim Arbeiten aendert - nicht, welche Datei
angefasst wurde. Dazu eine Tabelle, was Major, Minor und Patch fuer den
Betreiber praktisch bedeuten, und der Hinweis, dass sich eine
Datenbank-Migration nicht zurueckrollen laesst.

Die Version steht bewusst an zwei Stellen: package.json und
src/lib/version.ts. Ein Import der package.json waere im
Standalone-Build eine andere Datei als die des Projekts - die Zahl haenge
dann an der Laufzeit. Ein Test haelt beide zusammen, und
"npm run version" setzt sie gemeinsam samt Changelog-Abschnitt.

Committet und getaggt wird nicht automatisch. Ein Changelog-Eintrag, den
niemand gegengelesen hat, ist keinen Deut besser als keiner.

Sichtbar wird die Version unten in der Seitenleiste und unter
/api/gesundheit - dort mit Git-Stand, den der Docker-Build hineinreicht.
Auf der Anmeldeseite steht sie nicht: wer nicht angemeldet ist, muss
nicht wissen, welche Fassung hier laeuft.

Etappe: 8
Phase: Haertung
This commit is contained in:
Florian Wilde
2026-08-22 10:41:33 +02:00
parent 4b953e7887
commit b2ac13e128
10 changed files with 442 additions and 8 deletions
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env node
/**
* Version anheben.
*
* npm run version -- patch 1.0.0 -> 1.0.1
* npm run version -- minor 1.0.0 -> 1.1.0
* npm run version -- major 1.0.0 -> 2.0.0
* npm run version -- 1.2.3 genau diese Version
*
* Was passiert:
* 1. package.json und src/lib/version.ts werden gesetzt - beide, damit
* sie nicht auseinanderlaufen.
* 2. Im CHANGELOG wird aus "## [Unveroeffentlicht]" ein Abschnitt mit
* Versionsnummer und heutigem Datum, darueber ein neuer leerer
* Abschnitt.
* 3. Der Vergleichslink am Dateiende wird nachgezogen.
*
* Was NICHT passiert: committen und taggen. Das machst du selbst, nachdem
* du den Changelog-Eintrag gegengelesen hast. Ein automatisch erzeugter
* Changelog, den niemand liest, ist keinen Deut besser als keiner.
*
* Danach:
* git add -A && git commit -m "chore: Version 1.0.1"
* git tag -a v1.0.1 -m "..." && git push --follow-tags
*/
import { readFileSync, writeFileSync } from 'node:fs'
const WURZEL = new URL('..', import.meta.url)
const pfad = (name) => new URL(name, WURZEL)
function lesen(name) {
return readFileSync(pfad(name), 'utf8')
}
function schreiben(name, inhalt) {
writeFileSync(pfad(name), inhalt, 'utf8')
console.log(` ${name}`)
}
// --- Ziel bestimmen ---------------------------------------------------------
const argument = process.argv[2]
if (!argument) {
console.error('Aufruf: npm run version -- <patch|minor|major|x.y.z>')
process.exit(1)
}
const paket = JSON.parse(lesen('package.json'))
const alt = paket.version
function naechste(aktuell, art) {
const treffer = aktuell.match(/^(\d+)\.(\d+)\.(\d+)$/)
if (!treffer) throw new Error(`Version "${aktuell}" ist kein x.y.z`)
const [haupt, neben, fehler] = treffer.slice(1).map(Number)
switch (art) {
case 'major':
return `${haupt + 1}.0.0`
case 'minor':
return `${haupt}.${neben + 1}.0`
case 'patch':
return `${haupt}.${neben}.${fehler + 1}`
default:
if (!/^\d+\.\d+\.\d+$/.test(art)) {
throw new Error(`"${art}" ist weder patch/minor/major noch x.y.z`)
}
return art
}
}
const neu = naechste(alt, argument)
if (neu === alt) {
console.error(`Version ist bereits ${alt}.`)
process.exit(1)
}
console.log(`\n${alt} -> ${neu}\n`)
console.log('Geaendert:')
// --- 1. package.json --------------------------------------------------------
paket.version = neu
schreiben('package.json', `${JSON.stringify(paket, null, 2)}\n`)
// --- 2. src/lib/version.ts --------------------------------------------------
const versionsdatei = lesen('src/lib/version.ts')
const ersetzt = versionsdatei.replace(
/export const VERSION = '[^']+'/,
`export const VERSION = '${neu}'`,
)
if (ersetzt === versionsdatei) {
console.error('FEHLER: VERSION in src/lib/version.ts nicht gefunden.')
process.exit(1)
}
schreiben('src/lib/version.ts', ersetzt)
// --- 3. CHANGELOG.md --------------------------------------------------------
const heute = new Date().toISOString().slice(0, 10)
let changelog = lesen('CHANGELOG.md')
const UNVEROEFFENTLICHT = '## [Unveröffentlicht]'
if (!changelog.includes(UNVEROEFFENTLICHT)) {
console.error(`FEHLER: "${UNVEROEFFENTLICHT}" steht nicht im CHANGELOG.`)
process.exit(1)
}
const leererAbschnitt = [
UNVEROEFFENTLICHT,
'',
'_Noch nichts._',
'',
`## [${neu}] – ${heute}`,
].join('\n')
changelog = changelog.replace(UNVEROEFFENTLICHT, leererAbschnitt)
// Vergleichslinks am Dateiende nachziehen.
const basis = 'https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare'
changelog = changelog.replace(
/^\[Unveröffentlicht\]:.*$/m,
`[Unveröffentlicht]: ${basis}/v${neu}...HEAD\n[${neu}]: ${basis}/v${alt}...v${neu}`,
)
schreiben('CHANGELOG.md', changelog)
// --- Hinweis ----------------------------------------------------------------
console.log(`
Jetzt:
1. CHANGELOG.md gegenlesen - steht dort, was sich fuer den Nutzer
tatsaechlich aendert? Nicht, welche Datei angefasst wurde.
2. git add -A && git commit -m "chore: Version ${neu}"
3. git tag -a v${neu} -m "Version ${neu}"
4. git push --follow-tags
`)