Autonummerierung von Go-Builds: Das Versionierungsproblem ein für alle Mal lösen

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Warum manuelle Versionierung der Schmerz eines jeden Go-Entwicklers ist

\n

Haben Sie sich jemals dabei ertappt zu denken: «Welche Version läuft gerade in der Produktion?» oder eine Stunde damit verbracht, herauszufinden, welcher Commit einen Fehler in einem vor einem Monat erstellten Binary verursacht hat? Wenn ja, sind Sie hier genau richtig. Die Frage «Application auto build versioning» ist eine der beliebtesten auf Stack Overflow (tausende Aufrufe und dutzende Lösungen). Und das zu Recht: In der modernen Entwicklung, wo CI/CD-Pipelines alle 10 Minuten Builds durchführen, ist das manuelle Setzen von Versionen der Weg ins Chaos.

\n

In diesem Artikel behandeln wir die automatische Versionierung von Builds in Go von A bis Z. Sie erfahren, wie Sie ein System implementieren, das selbstständig die Versionsnummer, den Commit-Hash, das Build-Datum und sogar ein Flag für ein «schmutziges» Repository setzt. Der Code funktioniert sowohl in der lokalen Entwicklung als auch in GitHub Actions, GitLab CI oder Jenkins.

\n\n

Das Problem: Was ist falsch an manuellem version.go?

\n

Viele Anfänger erstellen eine Datei version.go und schreiben:

\n
package main\n\nvar Version = "1.0.0"
\n

Und das funktioniert genau bis zum ersten Commit. Dann beginnt es:

\n
    \n
  • «Ich habe vergessen, die Version vor dem Build zu aktualisieren»
  • \n
  • «In der Produktion läuft eine Version, im Code eine andere»
  • \n
  • «Welcher Commit entspricht diesem Build?»
  • \n
\n

Das Problem ist, dass die Version Metadaten des Builds sind, kein Teil des Quellcodes. Sie sollte automatisch während der Kompilierung generiert werden.

\n\n

Die Lösung: ldflags — die Geheimwaffe von Go

\n

Der Go-Compiler erlaubt es, Werte von Variablen während des Builds über das Flag -ldflags zu «ersetzen». Das ist Auto Build Versioning in seiner klassischen Form. Wir speichern die Version nicht im Code — wir übergeben sie dem Compiler.

\n\n

Schritt 1: Definieren der Versionsstruktur

\n

Erstellen wir ein Paket version mit Variablen, die während des Builds gefüllt werden:

\n
// version/version.go\npackage version\n\nvar (\n    // Version — semantische Version (manuell oder via Git-Tag gesetzt)\n    Version = "dev"\n\n    // Commit — kurzer Commit-Hash\n    Commit = "none"\n\n    // BuildTime — Build-Zeit im ISO-8601-Format\n    BuildTime = "unknown"\n\n    // IsDirty — Flag, dass das Repository nicht-committete Änderungen enthält\n    IsDirty = "false"\n)\n\nfunc String() string {\n    return "v" + Version + " (commit: " + Commit + ", built: " + BuildTime + ")"\n}
\n\n

Schritt 2: Bauen mit ldflags

\n

Jetzt kommt der interessante Teil. Anstelle von go build verwenden wir ein Skript oder Makefile, das die benötigten Werte berechnet und an den Compiler übergibt:

\n
#!/bin/bash\n# build.sh\n\nVERSION=$(git describe --tags --always --dirty 2>/dev/null || echo "dev")\nCOMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "none")\nBUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")\nIS_DIRTY=$(git diff --quiet || echo "true")\n\ngo build -ldflags="\\\n    -X 'main/version.Version=$VERSION' \\\n    -X 'main/version.Commit=$COMMIT' \\\n    -X 'main/version.BuildTime=$BUILD_TIME' \\\n    -X 'main/version.IsDirty=$IS_DIRTY'" \\\n    -o myapp .
\n

Wichtig: Der Pfad main/version.Version hängt von Ihrer Projektstruktur ab. Wenn das Paket version im Stammverzeichnis liegt — schreiben Sie main/version.Version. Wenn es in einem separaten Verzeichnis liegt — github.com/ihr-benutzername/projekt/version.Version.

\n\n

Schritt 3: Verwendung in der Anwendung

\n

Jetzt kann an jeder Stelle im Programm die Version ausgegeben werden:

\n
// main.go\npackage main\n\nimport (\n    "fmt"\n    "main/version"\n)\n\nfunc main() {\n    fmt.Println("App version:", version.String())\n    fmt.Println("Is dirty repo:", version.IsDirty)\n}
\n

Bauen und starten Sie:

\n
$ ./build.sh\n$ ./myapp\nApp version: v1.2.3 (commit: a1b2c3d, built: 2024-03-15T10:30:00Z)\nIs dirty repo: false
\n\n

Fortgeschrittenes Niveau: Semantisch

Blogs

Buchempfehlungen