aus Merge-Request-Titeln und Labels, als letzter Schritt jeder Deploy-Pipeline
Manuell gepflegte Release Notes veralten fast immer schneller, als sie geschrieben werden, weil niemand nach einem hektischen Deploy noch Zeit findet, sie sauber nachzutragen. Mit einer Conventional-Commits-Konvention, klaren Merge-Request-Labels und der GitLab Releases API lassen sich Release Notes stattdessen automatisiert aus genau den Informationen erzeugen, die ohnehin bei jedem Merge Request anfallen. Dieser Artikel zeigt den kompletten Weg von der Commit-Konvention bis zur Integration als letzter Schritt der Deploy-Pipeline.
Inhaltsverzeichnis
- 1. Warum manuelle Release Notes ein Wartungsproblem sind
- 2. Conventional Commits als strukturelle Grundlage
- 3. Merge Request Titel und Labels als Datenquelle
- 4. Der Aufbau der GitLab Releases API
- 5. Ein Pipeline-Job, der Release Notes aus Merge Requests generiert
- 6. Kategorisierung nach Labels in der Release-Beschreibung
- 7. Integration in die Deploy-Pipeline als letzter Schritt
- 8. Grenzen der Automatisierung: Was sie nicht ersetzt
- 9. Checkliste fuer den Einstieg in automatisierte Release Notes
- 10. Zusammenfassung
- 11. FAQ
1. Warum manuelle Release Notes ein Wartungsproblem sind
Release Notes von Hand zu pflegen bedeutet in der Praxis fast immer, dass jemand kurz vor oder nach einem Deploy in Eile eine Liste der wichtigsten Aenderungen zusammenschreibt, meist aus dem Gedaechtnis und ohne systematischen Abgleich mit dem, was tatsaechlich in den Merge Requests seit dem letzten Release gelandet ist. Kleinere, aber fuer Kunden durchaus relevante Aenderungen fallen dabei regelmaessig unter den Tisch, weil sie schlicht vergessen werden.
Das eigentliche Problem ist dabei nicht Faulheit, sondern dass die Information, was sich geaendert hat, technisch bereits vollstaendig vorliegt: in den Merge-Request-Titeln, den vergebenen Labels und der Commit-Historie seit dem letzten Tag. Diese Daten muessen nur noch strukturiert ausgelesen und automatisiert zusammengefasst werden, statt sie ein zweites Mal von Hand zu rekonstruieren.
2. Conventional Commits als strukturelle Grundlage
Die Conventional-Commits-Konvention schreibt vor, dass jede Commit-Message mit einem Praefix wie feat:, fix:, docs: oder chore: beginnt, gefolgt von einer kurzen Beschreibung der Aenderung, sowie optional einem ! nach dem Typ oder einer BREAKING CHANGE:-Zeile im Body fuer inkompatible Aenderungen. Diese einfache, maschinenlesbare Struktur macht es moeglich, Commits automatisch nach Kategorien wie Neue Funktionen, Fehlerbehebungen und Breaking Changes zu sortieren.
Damit die Konvention tatsaechlich eingehalten wird, lohnt sich ein Commit-Message-Linter als eigener CI-Job, der jeden Merge Request auf das korrekte Praefix prueft und fehlschlaegt, wenn keine Commit-Message dem erwarteten Muster entspricht. Ohne eine solche Durchsetzung verwaessert die Konvention erfahrungsgemaess innerhalb weniger Wochen, weil einzelne Commits ohne Praefix committet werden und die automatische Kategorisierung dadurch luecken bekommt.
# Beispiele fuer Conventional-Commits-konforme Messages
feat(checkout): add support for saved payment methods
fix(catalog): correct price rounding for bundle products
feat(api)!: remove deprecated v1 product endpoint
BREAKING CHANGE: Clients must migrate to the v2 product endpoint.
3. Merge Request Titel und Labels als Datenquelle
Neben der Commit-Historie liefert GitLab selbst bereits eine strukturierte Datenquelle: jeder Merge Request hat einen Titel, ein oder mehrere Labels und ein Ziel-Milestone. Eine sinnvolle Label-Konvention, etwa type::feature, type::fix, type::breaking und type::internal, macht die Kategorisierung fuer Release Notes unabhaengig davon, ob jeder einzelne Commit innerhalb des Merge Requests der Conventional-Commits-Konvention folgt.
Der Merge-Request-Titel selbst sollte dabei bewusst kundenverstaendlich formuliert werden, statt technischer Implementierungsdetails, weil er haeufig direkt oder nur leicht angepasst in die finalen Release Notes uebernommen wird. Ein Titel wie Neue Zahlungsart: Rechnungskauf fuer B2B-Kunden eignet sich fuer Release Notes deutlich besser als refactor PaymentMethodProvider interface, auch wenn beide Formulierungen fuer den Code-Review selbst gleichermassen gueltig waeren.
4. Der Aufbau der GitLab Releases API
Die GitLab Releases API erlaubt es, ueber einen POST-Request an /projects/:id/releases ein neues Release mit Tag-Name, Beschreibung und optionalen Release-Assets wie kompilierten Artefakten zu erstellen. Die Beschreibung selbst wird als Markdown-Text uebergeben, wodurch sich Ueberschriften, Aufzaehlungen und Links zu den jeweiligen Merge Requests direkt in der Release-Ansicht von GitLab darstellen lassen.
Praktisch bewaehrt sich dabei die Kombination mit der GitLab-eigenen release-cli, die als vorgefertigtes CI/CD-Component fuer genau diesen Zweck existiert und den API-Aufruf kapselt, sodass in der eigenen Pipeline nur noch die generierte Markdown-Beschreibung als Datei uebergeben werden muss, statt den rohen API-Request von Hand zu bauen.
curl --request POST \
--header "PRIVATE-TOKEN: $CI_JOB_TOKEN" \
--header "Content-Type: application/json" \
--data "{
\"tag_name\": \"v2.14.0\",
\"name\": \"Release v2.14.0\",
\"description\": \"$(cat release-notes.md)\"
}" \
"https://gitlab.mironsoft.de/api/v4/projects/$CI_PROJECT_ID/releases"
5. Ein Pipeline-Job, der Release Notes aus Merge Requests generiert
Ein dedizierter Job kurz vor dem eigentlichen Deploy ruft ueber die GitLab-API alle Merge Requests ab, die seit dem letzten Tag in den Ziel-Branch gemergt wurden, gefiltert nach dem Merge-Datum und dem Ziel-Branch. Fuer jeden gefundenen Merge Request werden Titel, Labels und die Merge-Request-URL ausgelesen und in einer strukturierten Markdown-Datei zusammengefasst, sortiert nach der Label-Kategorie.
Dieses Skript laesst sich in Python oder direkt in Bash mit curl und jq umsetzen, wobei Python bei komplexerer Filterlogik meist die wartbarere Wahl ist. Wichtig ist, dass der Job idempotent bleibt, also bei einem erneuten Lauf fuer denselben Tag dieselbe Ausgabe erzeugt, statt bei jedem erneuten Trigger unterschiedliche Ergebnisse zu liefern.
generate_release_notes:
stage: release
image: python:3.12-slim
script:
- pip install --quiet python-gitlab
- python scripts/generate_release_notes.py
--project-id "$CI_PROJECT_ID"
--since-tag "$(git describe --tags --abbrev=0 HEAD^)"
--output release-notes.md
artifacts:
paths:
- release-notes.md
rules:
- if: '$CI_COMMIT_TAG'
6. Kategorisierung nach Labels in der Release-Beschreibung
Die generierte Markdown-Datei gliedert sich sinnvollerweise in feste Abschnitte wie ## Neue Funktionen, ## Fehlerbehebungen und ## Breaking Changes, wobei jeder Merge Request anhand seines type::-Labels der passenden Kategorie zugeordnet wird. Merge Requests ohne passendes Label landen in einer separaten Kategorie Sonstige Aenderungen, statt stillschweigend aus den Release Notes zu verschwinden, was gleichzeitig als Signal dient, die Label-Disziplin im Team zu verbessern.
Breaking Changes verdienen dabei eine optisch hervorgehobene, eigene Position ganz oben in den Release Notes, weil sie fuer Kunden und andere Teams die groesste Aufmerksamkeit brauchen. Ein type::breaking-Label sollte deshalb in der Pipeline-Logik immer Vorrang vor anderen Labels desselben Merge Requests haben, selbst wenn zusaetzlich noch type::feature gesetzt ist.
7. Integration in die Deploy-Pipeline als letzter Schritt
Der Release-Notes-Job sollte bewusst als letzter Schritt der Deploy-Pipeline laufen, nachdem der eigentliche Deploy erfolgreich abgeschlossen wurde, ausgeloest ueber eine rules:-Bedingung, die nur bei einem gesetzten Git-Tag greift. Damit entsteht ein Release-Eintrag ausschliesslich fuer tatsaechlich produktiv ausgerollte Versionen, nicht fuer jeden beliebigen Merge in den main-Branch.
Diese Reihenfolge stellt zusaetzlich sicher, dass die Release Notes niemals einen fehlgeschlagenen Deploy dokumentieren: Schlaegt der eigentliche Deploy-Job fehl, wird der nachgelagerte Release-Notes-Job ueber eine needs:-Abhaengigkeit gar nicht erst ausgefuehrt, wodurch keine irrefuehrende Release-Ankuendigung fuer eine Version entsteht, die in Wahrheit nie live ging.
publish_release:
stage: release
needs:
- job: deploy_production
artifacts: false
- job: generate_release_notes
script:
- >
release-cli create --name "Release $CI_COMMIT_TAG"
--tag-name "$CI_COMMIT_TAG"
--description "$(cat release-notes.md)"
rules:
- if: '$CI_COMMIT_TAG'
8. Grenzen der Automatisierung: Was sie nicht ersetzt
Automatisch generierte Release Notes ersetzen keine manuelle Kuration, sobald ein Release mehrere zusammenhaengende, aber technisch getrennte Merge Requests umfasst, die inhaltlich besser als ein einziger Punkt statt als mehrere separate Eintraege dargestellt werden. Hier bleibt ein kurzer manueller Redigier-Schritt vor der eigentlichen Veroeffentlichung sinnvoll, auch wenn der Grossteil der Arbeit automatisiert erledigt wird.
Auch Breaking Changes sollten trotz automatischer Hervorhebung nie ausschliesslich auf die automatisierte Beschreibung vertrauen, sondern zusaetzlich in einem separaten Migrationsleitfaden dokumentiert werden, der konkrete Schritte fuer betroffene Nutzer beschreibt. Die Automatisierung liefert die zuverlaessige Rohfassung, die redaktionelle Feinarbeit bei komplexeren Releases bleibt weiterhin Aufgabe eines Menschen.
9. Checkliste fuer den Einstieg in automatisierte Release Notes
Der Einstieg gelingt am einfachsten mit einer klaren Label-Konvention im Team, einem Commit-Message-Linter als CI-Job und einem einzelnen Pipeline-Job, der die GitLab-API abfragt und eine Markdown-Datei erzeugt. Erst danach folgt die Integration in die Releases API als letzter Schritt der Deploy-Pipeline.
Die folgende Tabelle vergleicht die drei zentralen Datenquellen fuer automatisierte Release Notes und zeigt, welche Kombination fuer welchen Anwendungsfall die verlaesslichsten Ergebnisse liefert.
| Datenquelle | Struktur | Kundenverstaendlichkeit | Empfehlung |
|---|---|---|---|
| Rohe Commit-Historie | Unstrukturiert ohne Konvention | Niedrig | Nicht direkt fuer Release Notes nutzen |
| Conventional Commits | Praefix-basiert, maschinenlesbar | Mittel | Fuer technische Changelogs geeignet |
| Merge Request Titel | Ein Titel pro fachlicher Aenderung | Hoch, bei sorgfaeltiger Formulierung | Beste Basis fuer Release Notes |
| Merge Request Labels | Explizite Kategorisierung | Hoch, mit Konvention | Fuer Sortierung nach Kategorie |
Mironsoft
CI/CD-Pipelines, Zero-Downtime-Deployments und Release-Automatisierung
Deployments, die ohne Ausfallzeit und ohne Nervenkitzel laufen?
Wir prüfen bestehende GitLab-Pipelines auf fragile Deployment-Schritte und fehlende Absicherung und bauen daraus einen Release-Prozess mit Zero-Downtime-Deployments, automatisierten Checks und einem Rollback, dem ihr im Ernstfall vertrauen könnt.
Pipeline-Review
Bestehende .gitlab-ci.yml auf Fragilität, fehlende Stages und Sicherheitslücken prüfen.
Zero-Downtime-Deployment
Symlink-Releases, Health-Checks und Rollback-Strategien für Magento-Shops aufbauen.
CI/CD-Automatisierung
Tests, Security-Scans und Deployments zu einer zuverlässigen Pipeline verbinden.
10. Zusammenfassung
Automatische Release Notes: Das Wichtigste auf einen Blick
Kernidee
Release Notes aus Informationen generieren, die ohnehin in Merge-Request-Titeln, Labels und Commits vorliegen, statt sie doppelt von Hand zu schreiben.
Strukturelle Basis
Conventional Commits fuer maschinenlesbare Commits, ergaenzt um eine type::-Label-Konvention fuer Merge Requests.
Technischer Weg
Ein Pipeline-Job liest Merge Requests seit dem letzten Tag ueber die GitLab-API aus und erzeugt daraus eine Markdown-Beschreibung fuer die Releases API.
Grenze der Automatisierung
Komplexe, zusammenhaengende Releases und Breaking-Change-Migrationsleitfaeden brauchen weiterhin manuelle redaktionelle Nacharbeit.