Conventional Commits und Semantic Versioning

Daniel Schock
13 Oct 2025

Implementierung eines automatisierten Release-Workflows mit Conventional Commits und Semantic Versioning

Manuelle Software-Releases stellen eine häufige Quelle für Belastungen und Ineffizienz in Entwicklungsteams dar. Der Prozess umfasst oft nächtliche Deployments, manuelle Checklisten und die mühsame Erstellung von Changelogs. Dieser risikobehaftete, manuelle Arbeitsablauf birgt nicht nur ein erhebliches Potenzial für menschliche Fehler, sondern kann auch eine „Release Anxiety“ begünstigen, welche agile Entwicklungszyklen verlangsamt. Die Entscheidung, an einem Freitagnachmittag einen kritischen Fix zu mergen, wird dadurch häufig verzögert.

Ein idealer Workflow würde es jedoch ermöglichen, dass jeder Merge in den Main-Branch sicher einen neuen, korrekt versionierten Release auslöst, komplett mit strukturierten, automatisch generierten Release Notes. Dies ist nicht nur ein Prinzip moderner CI/CD-Praktiken, sondern eine erreichbare Realität. Durch die Nutzung einer strukturierten Commit History können Software-Releases zu einem routinierten, vorhersagbaren und vollständig automatisierten Prozess werden.

Dieser Artikel bietet eine technische Anleitung, um dieses Automatisierungsniveau durch die Implementierung der Conventional Commits”-Spezifikation und des semantic-release-Tools zu erreichen.


Das Kernprinzip: Die Steuerung des Releases durch die Commit History

Der Schlüssel zu dieser Automatisierung liegt in einem konzeptionellen Wandel: Die Git Commit History wird nicht mehr nur als Protokoll von Änderungen betrachtet, sondern als die alleinige Quelle der Wahrheit für die Versionierung. Anstatt dass ein Entwickler entscheidet, ob eine Änderung einen Patch-, Minor- oder Major-Release rechtfertigt, wird diese Entscheidung direkt aus der Historie des Codes abgeleitet.

Der daraus resultierende Vorteil ist signifikant: Der Release-Prozess wird von subjektiven Entscheidungen entkoppelt und zu einer direkten, logischen Konsequenz der geleisteten Arbeit. Jeder Commit trägt eine eigene semantische Bedeutung, wodurch die Versionsnummer zu einem vorhersagbaren Ergebnis anstatt einer Schätzung wird. Diese Philosophie bildet die Grundlage der Automatisierung.


Die Grundlage: Etablierung eines Standards für Commit-Nachrichten

Die Voraussetzung für jede Automatisierung ist die Etablierung einer standardisierten, maschinenlesbaren Sprache für die Kommunikation. Die Conventional Commits-Spezifikation bietet einen solchen Standard, indem sie eine klare und verbindliche Struktur für Git Commit Messages vorschreibt, die Mehrdeutigkeiten beseitigt und die Absicht jeder Änderung explizit macht.

Der type-Bezeichner ist die entscheidende Komponente, die die Auswirkung eines Commits auf die Versionierung gemäß den Prinzipien des Semantic Versioning diktiert:

  • fix: Kennzeichnet eine Fehlerbehebung, die ein Problem in der Codebasis korrigiert. Dies entspricht einem Patch Release (z. B. 1.0.0 -> 1.0.1).
  • feat: Bezeichnet die Einführung eines neuen Features, das Funktionalität hinzufügt. Dies entspricht einem Minor Release (z. B. 1.0.1 -> 1.1.0).
  • feat! oder fix!: Signalisieren eine nicht abwärtskompatible Änderung (ein „Breaking Change“). Dieser Indikator erfordert einen Major Release (z. B. 1.1.0 -> 2.0.0).

Commits mit anderen Typen wie docs, chore, style, refactor oder test sind für die Wartung der Codebasis unerlässlich, lösen jedoch keine Anpassung der Versionsnummer aus. Diese Unterscheidung ist entscheidend, da sie sicherstellt, dass das für den Benutzer sichtbare Changelog auf wertschöpfende Änderungen, nämlich neue Features und Fehlerbehebungen, konzentriert bleibt. Dieser disziplinierte Ansatz führt zu einer transparenten und vorhersagbaren Git History.


Der Autopilot: Orchestrierung des Release-Prozesses mit semantic-release

Mit einer strukturierten Commit History kann der Release-Prozess mithilfe von Tools wie semantic-release automatisiert werden. Dieses Dienstprogramm fungiert als Orchestrator des Release-Workflows und integriert die Git History mit dem Paketmanager des Projekts und dem Repository-Host.

semantic-release automatisiert den gesamten Package Release Workflow, der die Bestimmung der nächsten Versionsnummer, die Erstellung von Release Notes und die Veröffentlichung des Pakets umfasst.

Das Tool analysiert die Commit History des Projekts, um die nächste semantische Version abzuleiten, und führt anschließend eine konfigurierbare Abfolge von Release-Schritten aus. Obwohl es als Node.js-Paket vertrieben wird, bietet seine plugin-basierte Architektur die Flexibilität, Projekte zu unterstützen, die in allen gängigen Programmiersprache entwickelt wurden. Die folgenden Abschnitte demonstrieren die Anwendung innerhalb eines Spring-Boot-Projekts.


Praktische Implementierung: Eine schrittweise Anleitung

Die Konfiguration lässt sich in drei Hauptschritte unterteilen.


Schritt 1: Vorbereitung des Gradle-Builds (gradle.properties)

Das gradle-semantic-release-plugin benötigt einen festgelegten Ort, um die Version des Projekts zu aktualisieren. Die Datei gradle.properties erfüllt diesen Zweck effektiv. Die Entkopplung der Versionsnummer in eine Properties-Datei ist eine empfohlene Praxis, da sie externen Tools ermöglicht, die Version zu lesen und zu schreiben, ohne komplexe Build-Skripte parsen zu müssen.
gradle.properties

version = 0.0.0

Schritt 2: Konfiguration des Release-Workflows (.releaserc.yml)

Diese Konfigurationsdatei definiert die genaue Abfolge der Operationen, die semantic-release ausführen wird.

.releaserc.yml

release:
  branches:
    - 'main'
preset: 'conventionalcommits'
plugins:
  - '@semantic-release/commit-analyzer'
  - '@semantic-release/release-notes-generator'
  - - '@semantic-release/changelog'
    - changelogFile: 'CHANGELOG.md'
      changelogTitle: '# Changelog'
  - - '@semantic-release/github'
    - assets:
        - path: 'CHANGELOG.md'
          label: 'Changelog'
  - 'gradle-semantic-release-plugin'
  - - '@semantic-release/git'
    - assets:
        - 'CHANGELOG.md'
        - 'gradle.properties'

Die Anweisungen in dieser Datei sind wie folgt zu verstehen:

  • branches: Beschränkt die Ausführung des Release-Prozesses auf Commits im main-Branch.
  • preset: Ermöglicht das Einbinden von vordefinierten Konfigurationen. Hier wird eine auf die Conventional-Commits-Konvention abgestimmte Konfiguration genutzt.
  • plugins: Definiert die geordnete Pipeline von Aktionen:
  • commit-analyzer: Analysiert Git-Commits seit dem letzten Release-Tag, um den erforderlichen Release-Typ (Major, Minor oder Patch) zu bestimmen.
  • release-notes-generator: Erstellt strukturierte Release Notes aus den relevanten Commit-Nachrichten.
  • changelog: Fügt die generierten Notes an eine CHANGELOG.md-Datei an und pflegt so eine beständige Aufzeichnung der Änderungen.
  • github: Erstellt einen offiziellen, getaggten Release auf der GitHub-Plattform und fügt angegebene Assets, wie das Changelog, hinzu.
  • gradle-semantic-release-plugin: Führt sprachspezifische Aufgaben aus, in diesem Fall die Aktualisierung der Version in gradle.properties und die Ausführung des publish-Tasks.
  • git: Committet die geänderten Dateien (CHANGELOG.md, gradle.properties) zurück in das Repository, um dessen Zustand mit dem neuen Release zu synchronisieren.

Schritt 3: CI/CD-Integration (GitHub Actions)

Schließlich ist eine CI-Pipeline erforderlich, um den Release-Workflow bei einem Push in den main-Branch auszulösen. Der folgende GitHub-Actions-Workflow realisiert dies.

.github/workflows/release.yml

name: Release
on:
  push:
    branches:
      - main
permissions:
  contents: read
jobs:
  release:
    name: Release
    runs-on: ubuntu-latest
    permissions:
      contents: write
      issues: write
      pull-requests: write
      id-token: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Node einrichten
        uses: actions/setup-node@v4
        with:
          node-version: 'lts/*'
      - name: Install dependencies
        run: |
          npm install -g \
            semantic-release \
            @semantic-release/git \
            @semantic-release/changelog \
            conventional-changelog-conventionalcommits \
            gradle-semantic-release-plugin
      - name: Release durchführen
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: npx semantic-release

Dieser Workflow installiert semantic-release und seine Plugins und führt dann den Release-Prozess aus. Der Parameter fetch-depth: 0 ist von entscheidender Bedeutung, da er sicherstellt, dass die gesamte Git History zur Analyse verfügbar ist. Der permissions-Block gewährt dem Job ausreichende Berechtigungen, um Releases zu erstellen und Dateien zurück in das Repository zu committen.


Fazit: Die strategischen Vorteile der Release-Automatisierung

Die Implementierung dieser automatisierten Release-Pipeline führt zu erheblichen strategischen Vorteilen. Sie eliminiert manuellen Aufwand und das Potenzial für menschliche Fehler im Versionierungs- und Release-Prozess. Die Praxis, Versionsnummern zu schätzen und Changelogs manuell zusammenzustellen, wird durch ein deterministisches, automatisiertes System ersetzt.

Diese Transformation bewirkt mehr als nur eine Zeitersparnis; sie verbessert den Softwareentwicklungszyklus grundlegend. Sie ermöglicht eine höhere Deployment-Frequenz mit größerem Vertrauen, was eine schnellere Bereitstellung von Features und Fehlerbehebungen erlaubt. Die mit Release-Ereignissen verbundene Anspannung wird durch die Effizienz von Continuous Delivery ersetzt. Die Hauptverantwortung des Entwicklungsteams reduziert sich auf die Aufrechterhaltung einer hohen Standard an Commit-Hygiene; der Release-Mechanismus übernimmt die übrigen Aufgaben.

Daniel Schock

Senior Software Engineer

Daniel ist ein erfahrener Fullstack-Entwickler und DevOps-Spezialist mit einem klaren Schwerpunkt auf der Backend-Entwicklung mit Java und Kotlin. Seine Expertise wird durch praktische Erfahrung in der Frontend-Entwicklung mit Vue.js abgerundet. Er verfügt über umfassende Kenntnisse in der Konzeption von CI/CD-Pipelines sowie im Einsatz von Cloud-Technologien wie Kubernetes und AWS, die er erfolgreich in den Branchen Versicherung, Logistik und E-Commerce einsetzte. Daniel arbeitet bevorzugt in agilen Umgebungen und zeichnet sich durch seine analytische Denkweise und hohe Problemlösungskompetenz aus.

Weitere Artikel