Files
FEDEO/docs/releases.md
flfeders f6996c1d7b
All checks were successful
Build and Release FEDEO / verify (push) Successful in 25s
Build and Release FEDEO / build-and-release (push) Successful in 6m4s
fix(release): exclude legacy main history from commit checks
2026-09-20 18:44:53 +02:00

13 KiB

Versionierung und Releases

Normaler Ablauf

Entwicklung erfolgt auf dev. Ein Push nach main ist die bewusste Freigabe zum Veröffentlichen. Der Workflow .gitea/workflows/build-push.yaml startet automatisch, prüft die Commit-Nachrichten und Release-Tests, baut alle sechs Images und veröffentlicht bei relevanten Änderungen einen Gitea-Release.

Nach abgeschlossener Entwicklung in einem sauberen Checkout:

git switch dev
git pull --ff-only origin dev
npm ci --ignore-scripts
npm test
npm run release:preview
git push origin dev
git switch main
git pull --ff-only origin main
git merge --no-ff dev -m "chore(release): merge dev into main"
git push origin main
git switch dev

Alternativ einen Pull Request von dev nach main mit einem normalen Merge übernehmen. Der Merge-Commit selbst wird bei der Versionsberechnung übersprungen; die enthaltenen feat:-/fix:-Commits bestimmen das Release. Bei Squash-Merges muss die Squash-Nachricht die größte Änderung einschließlich etwaiger Breaking Changes ausdrücken.

Es ist kein separater Versions-Commit erforderlich. npm run release:preview ist eine lokale, schreibfreie Vorschau; vorher mit git fetch origin --tags die Release-Tags aktualisieren. Ein Push, der nur Wartungsänderungen enthält, erzeugt nach dem ersten Release keine neue Version.

Ein manueller Start ist zusätzlich unter Gitea → Actions → Build and Release FEDEO → Run workflow möglich. Wähle main für einen Release und dev für einen Entwicklungsbuild. Dieser Start verwendet dieselbe Pipeline und dieselbe automatisch berechnete Version. npm run release:ci ist ausschließlich für die CI vorgesehen.

Versionsregeln

Die Pipeline nutzt die fest gepinnten Pakete @semantic-release/commit-analyzer und @semantic-release/release-notes-generator mit dem Conventional-Commits-Preset. Der Veröffentlichungsablauf ist auf Gitea 1.25.3 abgestimmt.

Commit Bedeutung Release
fix(scope): … Fehlerkorrektur Patch
perf(scope): … Leistungsverbesserung Patch
feat(scope): … Neue Funktion Minor
feat(scope)!: … / BREAKING CHANGE: … Inkompatible Änderung Major
docs:, test:, refactor:, build:, ci:, chore:, style: Wartung ohne Breaking Change Keiner
revert: … mit Git-Revert-Information Zurücknahme bereits ausgelieferter Änderungen Patch

Es zählt der höchste erforderliche Sprung seit dem letzten stabilen Tag vMAJOR.MINOR.PATCH. Eine Änderung und ihr vollständiger Git-Revert innerhalb desselben Release-Bereichs heben sich auf. Selbst verfasste Nachrichten müssen dem Schema entsprechen; echte Merge-Commits und automatisch generierte Git-Reverts werden gesondert behandelt.

Der erste Release heißt 3.0.0. bootstrapCommit in release.config.json begrenzt die Prüfung und die erste Änderungsliste auf die Einführung dieser Versionierung auf dev. legacyHistoryHeads hält zusätzlich den letzten alten main-Stand fest (e7554fa2… vor der ersten Übernahme von dev). Dessen Historie enthält ältere Änderungen an Issue-Vorlagen, die nicht vom dev-Ausgangscommit erreichbar sind. Beide festgelegten Historien werden aus Commit-Prüfung, Versionsberechnung und Release Notes ausgeschlossen. Neue Commits zählen auch dann, wenn ihr Zweig von einem dieser alten Stände abzweigt.

Ältere Commits werden weder umgeschrieben noch nachträglich klassifiziert. Diese festen Ausgangscommits nach Einführung nicht verschieben und keine späteren Änderungen hinzufügen, um die Commit-Prüfung zu umgehen. Der vollständige Checkout muss auch die alten Branch-Historien enthalten; ein fehlender Ausgangscommit führt zum Abbruch.

Versionen und Artefakte

Backend, Frontend, Dokumentation, Website und zentrale Dienste werden als gemeinsamer FEDEO-Stand gebaut. Die Produktversion stammt aus Git und wird beim Build eingebettet. Backend-/Frontend-Paketversionen dienen nicht als Release-Quelle.

  • Stabile Images: …/backend:3.2.0, …/backend:v3.2.0 und …/backend:sha-<vollständiger Commit>; entsprechend für alle sechs Komponenten.
  • Bewegliche Kanäle: latest und main zeigen nach erfolgreicher Veröffentlichung auf denselben Release.
  • Entwicklung: dev sowie unveränderliche dev-sha-<vollständiger Commit>-<UUID>-Images. Alle Komponenten eines erfolgreichen Laufs erhalten denselben Snapshot-Tag. Auch eine Wiederholung desselben Commits baut einen eigenen Snapshot. Die eingebettete Entwicklungsversion enthält Commit-Anzahl und Commit-ID.
  • Release-Builds erhalten zunächst einen eindeutigen temporären build-<UUID>-Tag. Diese Tags können später nach einer Aufbewahrungsfrist bereinigt werden; stabile Versionen und deren Digests müssen erhalten bleiben.
  • Release-Dateien: CHANGELOG.md, release-manifest.json, docker-compose.selfhost.yml, .env.example, selfhost-install.sh, selfhost-setup.sh und SHA256SUMS.

Das Manifest dokumentiert die Image-Digests, den Commit und SHA-256-Prüfsummen aller enthaltenen SQL-Migrationen. Es beschreibt den ausgelieferten Migrationsstand; den tatsächlich angewendeten Stand einer Installation bestimmt deren Datenbank.

Mobile-App und Device-Agent behalten ihre eigenen bestehenden Versionsreihen. Ihre Paketversionen und die lokale iOS-Buildnummer werden im Manifest festgehalten. Die iOS-Buildnummer aus einer externen EAS-Ausführung kann davon abweichen. Ein Server-Release startet keine App-Store-Veröffentlichung und behauptet keine automatisch geprüfte Kompatibilität mit unabhängig veröffentlichten Clients. Inkompatible API-Änderungen gehören als Breaking Change mit betroffenen Client-Versionen und Upgrade-Anleitung in die Release Notes.

Laufende Version abfragen

Im Frontend steht die Version unten in der Seitenleiste; der Tooltip enthält den Commit. Die Backend-Version ist öffentlich über diesen Endpunkt abrufbar:

curl https://app.example.com/backend/version

Beispiel: {"product":"FEDEO","version":"3.2.0","revision":"<Commit-ID>"}. Die zentrale Instanzmeldung verwendet dieselbe Backend-Version. Bei lokalen Builds ohne Build-Argumente lautet die Version 0.0.0-dev.

Gitea einmalig vorbereiten

Der vorhandene Actions-Runner benötigt Docker mit Buildx und Netzwerkzugriff auf npm, Basis-Images und die Registry. Alle Images werden wie der bisherige Backend-Build für linux/amd64 erzeugt. Eine zusätzliche ARM-Variante ist ein eigener Ausbau.

Unter Repository → Settings → Actions → Variables bzw. Secrets:

  • CI_TOKEN: Token des Registry-Benutzers flfeders, mindestens mit write:package. Die Registry-Anmeldung verwendet wie die bisherigen Build-Workflows vorrangig vars.CI_TOKEN; secrets.CI_TOKEN dient als Rückfallwert. Für eine spätere Umstellung auf ein Secret die gleichnamige Variable entfernen.
  • Optional RELEASE_TOKEN als Secret oder Variable: Token mit write:repository für Release-API und Git-Tags sowie zum Erzeugen/Löschen des temporären Sperr-Branches automation/release-lock. Nur main benötigt diese Rechte. Die Reihenfolge für Git-Anmeldung und Release-API ist secrets.RELEASE_TOKEN, vars.RELEASE_TOKEN, vars.CI_TOKEN, secrets.CI_TOKEN. Ohne separates Release-Token benötigt der verwendete CI_TOKEN zusätzlich diese Rechte.

Auf dev nutzt der Checkout wie die bisherigen Workflows den normalen Job-Token zum Lesen. Entwicklungsbuilds pushen keine Git-Refs und rufen die Release-API nicht auf; der Registry-Token benötigt dafür keine Repository-Schreibrechte.

Der Release-Token-Benutzer muss die entsprechenden Repository-Rechte haben. Geschützte Tags v* müssen ihn zulassen; automation/release-lock muss durch ihn erzeugt und entfernt werden können. Die Pipeline benötigt keine Schreibrechte auf den Inhalt von main. Ein 403 beim Anlegen der Main-Sperre führt vor dem Image-Build zum Abbruch mit einem Hinweis auf die fehlenden Rechte.

dev mit der neuen Konfiguration zu pushen prüft zunächst den Entwicklungsbuild. Erst die bewusste Übernahme nach main erstellt den ersten stabilen Release. Release-Erstellung und Deployment haben eigene Auslöser: Server werden durch diese Pipeline nicht neu gestartet.

Veröffentlichungsreihenfolge und Wiederaufnahme

Gitea 1.25 ignoriert das Workflow-Feld concurrency. Deshalb reserviert der Runner für Releases auf main atomar einen Git-Branch per leerem --force-with-lease. Ein zweiter Release-Runner wartet bis zu zwei Stunden, ohne die Sperre zu übernehmen. Veraltete Main-Läufe werden vor dem Build übersprungen, wenn bereits ein neuerer Main-Commit vorhanden ist; dessen Lauf enthält alle ausstehenden Änderungen.

Entwicklungsbuilds benötigen keine Git-Sperre. Parallele Läufe bauen unabhängige Snapshot-Tags. Nach dem vollständigen Build wird vor jeder Aktualisierung eines dev-Images geprüft, ob der Commit noch der aktuelle Stand von origin/dev ist. Ein erkennbar veralteter Lauf beendet die Aktualisierung. Diese Prüfung und die Registry-Aktualisierung sind nicht atomar; für einen festen Stand aller Komponenten denselben vollständigen Snapshot-Tag oder einen stabilen Release verwenden.

  1. Vollständigen Commit und Tags laden, Commit-Nachrichten prüfen, Version berechnen.
  2. Alle sechs Images aus diesem Checkout mit Lockfiles bauen. Der Backend-Build führt zusätzlich seine Tests aus. Vorher laufen die Release-Tests in einem eigenen Job.
  3. Gitea-Entwurf erzeugen und Dateien hochladen, zuerst das Manifest mit den gebauten Digests.
  4. Unveränderliche Image-Tags auf diese Digests setzen. Ein vorhandener abweichender Digest führt zum Abbruch.
  5. Release veröffentlichen; Gitea erzeugt dabei den Git-Tag auf dem konkreten Commit.
  6. latest und main auf die veröffentlichten Images umstellen, anschließend die Sperre entfernen.

Bei einem Release-Fehler den fehlgeschlagenen Workflow am ursprünglichen Commit erneut ausführen. Ein vorhandenes Manifest wird weiterverwendet; veröffentlichte Versionen werden nicht neu gebaut. Auch eine unterbrochene Aktualisierung von latest lässt sich so vervollständigen. Die Registry bietet keine gemeinsame Transaktion über mehrere Images: deshalb verwendet Selfhosting feste Versionen und das Manifest die exakten Digests.

Ein anderer noch offener Release-Entwurf blockiert einen neuen Release. Zuerst den ursprünglichen Lauf wiederaufnehmen; anschließend den aktuellen Main-Lauf erneut starten. Releases oder Versionstags nicht löschen, um einen fehlgeschlagenen Lauf zu überspringen.

Nach einem hart abgebrochenen Runner kann die Sperre bestehen bleiben. Zuerst in Actions sicherstellen, dass der zugehörige Lauf wirklich beendet ist. Danach ausschließlich dessen Sperre mit dem zuvor gelesenen Commit als Lease entfernen:

git ls-remote origin refs/heads/automation/release-lock
# Die ausgegebene Commit-ID anstelle von LOCK_SHA einsetzen:
git push --force-with-lease=refs/heads/automation/release-lock:LOCK_SHA origin :refs/heads/automation/release-lock

Keine aktive Sperre löschen. Der Lease verhindert, dass zwischenzeitlich eine andere Sperre entfernt wird. Entwicklungsbuilds erzeugen keine Sperre und können direkt erneut gestartet werden.

Selfhosting und Updates

Für eine Installation eines bereits veröffentlichten Releases, hier beispielhaft 3.0.0:

curl -fsSL https://git.federspiel.tech/flfeders/FEDEO/releases/download/v3.0.0/selfhost-install.sh -o /tmp/fedeo-install.sh
bash /tmp/fedeo-install.sh --simple --start

Dieser versionierte Installer lädt die Betriebsdateien vom zugehörigen Git-Tag und schreibt FEDEO_VERSION=3.0.0 in die neue .env. Vorhandene Installationen behalten ihren bisherigen Kanal, bis FEDEO_VERSION bewusst geändert wird. Die Dateien im Entwicklungszweig verwenden weiterhin dev als Rückfallwert.

Bei einem Update:

  1. Release Notes und Migrationshinweise lesen. Konsistentes Datenbankbackup (z. B. pg_dump) sowie Datei-/Objektspeicher und Konfiguration sichern. Ein TAR des laufenden PostgreSQL-Datenverzeichnisses ist kein verlässliches Datenbankbackup.
  2. Die benötigten Betriebsdateien vom gewünschten Release herunterladen; bestehende .env und Daten behalten. Downloads mit dessen SHA256SUMS überprüfen.
  3. In /opt/fedeo/.env die feste Zielversion setzen, z. B. FEDEO_VERSION=3.2.0.
  4. Images laden und Dienste neu erstellen:
docker compose --env-file /opt/fedeo/.env -f /opt/fedeo/docker-compose.yml pull
docker compose --env-file /opt/fedeo/.env -f /opt/fedeo/docker-compose.yml up -d
curl https://app.example.com/backend/version

Das Backend führt standardmäßig Vorwärtsmigrationen beim Start aus. Ein Downgrade der Images setzt die Datenbank nicht zurück. Zur vorherigen Version nur bei nachgewiesener Schema-Kompatibilität wechseln oder das passende Backup in einem geplanten Wiederherstellungsablauf einspielen.

Technische Referenzen