Frontend-Applikation - Anzeige der korrekten Versionsnummer

Semantic-Verisoning im Frontend – die Versionsnummer soll Teil des Build-Artefakts sein und im Frontend angezeigt werden. Daran habe ich mir bisher die Zähne ausgebissen – hier mein finaler Ansatz inklusive der notwendigen Anpassungen, um das Problem ein für alle Mal zu lösen.

4 Minuten
Hero image

Hintergrund

Bei der Versionierung von Software nutzen wir bei konzentrik das Konzept von Semantic Versioning. Dabei erhält das kommende Release anhand der Änderungshistorie automatisiert eine Versionsnummer. Diese wird bei uns beim Erstellen des Produktivcodes automatisch anhand der Commits ermittelt.

In unserem Fall soll diese Versionsnummer im Frontend immer verfügbar werden. Einerseits "unsichtbar" im Header der index.html falls der Kunde keine Anzeige wünscht, oder andererseits z. B. in der Sidebar, sodass diese Kennung für den Kunden sichtbar ist. Bei Bug-Reports ist der Kunde angehalten, diese Versionsnummer beim Report anzugeben.

Problem der bisherigen Lösungsansätze

Im Rahmen unserer Automatisierung wird die neue Versionsnummer als GitHub Action in die package.json in das Feld version geschrieben. Dies ist der letzte Schritt im automatischen Prozess für den Fall eines erfolgreichen Releases. Es erfolgt hierfür ein Commit mit der Änderung der package.json.

Für die Anzeige im Frontend haben wir eine Konstante genutzt, die im React-Kontext definiert ist. Dieser Wert gibt die Zeichenkette aus dem Feld version der package.jsonzurück. Dadurch, dass wir die neue Version am Ende des Workflows schreiben, ist die Versionsnummer im gebauten Paket nicht die aktuelle, sondern die vorherige Versionsnummer.

const appVersion = VERSION

Den Workflow anzupassen, ist der naheliegende Schluss – jedoch keine gute Idee. Erst wenn alle Schritte erfolgreich durchlaufen sind, können wir sicher sein, dass die neue Version gebaut werden kann. Und entsprechend erst zu dem Zeitpunkt das Commit erstellen. Wie also kommen wir aus diesem Henne-Ei-Problem heraus?

Die neue Lösung

Das Ziel bleibt: Das Build-Artefakt, ob in einem Docker-Container, als Zip-Datei oder direkter Upload zu einem Server, soll die korrekte Versionsnummer enthalten. Die Nutzung der Konstante zum Auslesen aus der package.json ist ausgeschlossen.

Folglich muss vor dem Bauen des Frontend-Codes die Versionsnummer ermittelt und in den HTML-Header und die Sidebar geschrieben werden. Erst danach kann der npm run build Befehl ausgeführt werden. Die notwendigen Anpassungen in der GitHub-Action-YAML sind folgende:

  1. Im Sourcecode Platzhalter für die Versionsnummer vorbereiten

In der index.html folgende Zeile im HTML-Header einfügen

Zusätzlich noch im EnvVar-Service, der gekapselten Zugriff auf Environment Variablen im Frontend bietet, den Platzhalter einfügen. Damit kann z. B. in der Sidebar auf den Wert zugegriffen werden. Oder woanders im Frontend, wie immer die genau UI-Vorlage aussieht.

const appVersion = 'SEMANTIC_RELEASE_VERSION'

  1. Notwendige Abhängigkeiten installieren und in .releaserc Plug-ins eintragen

npm install -D semantic-release-export-data

// in .releaserc
// ...
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "semantic-release-export-data",
    "@semantic-release/github",
// ...
  1. Ausführen sämtlicher Vorbedingungen, wie Check-out, Abhängigkeiten installieren, Tests, Linter, etc. innerhalb der GitHub-Action-YAML
  2. Dry-Run von Semantic Release ermöglicht danach Zugriff auf die nächste Version via ${{ steps.get-next-version.outputs.new-release-version }}
# In ci.yaml
- name: Semantic release dry run to determine version
   run: npx semantic-release --dry-run
   id: get-next-version
   env:
     GITHUB_TOKEN: ${{ secrets.PAT_SEMANTIC_RELEASE }}
  1. Build-Schritt mit Docker erhält die neue Versionsnummer als BUILD_ARG
- name: Build Docker image
   uses: docker/build-push-action@v5
   with:
     context: .
     labels: ${{ steps.meta.outputs.labels }}
     push: true
     tags: ${{ steps.meta.outputs.tags }}
     build-args: |
       GITHUB_NPM_TOKEN=${{ secrets.PACKAGE_REGISTRY_TOKEN }}
       NODE_VERSION=${{ steps.nvm.outputs.NODE_VERSION }}
       SEMANTIC_RELEASE_VERSION=${{ steps.get-next-version.outputs.new-release-version }}
  1. Im Dockerfile die Platzhalter durch die neue Versionsnummer ersetzen
FROM node:${NODE_VERSION}-alpine AS build
# ensure the build arg is accessible in the build stage
ARG SEMANTIC_RELEASE_VERSION

WORKDIR /app
ENV PATH=/app/node_modules/.bin:$PATH
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY . ./

# replace SEMANTIC_RELEASE_VERSION placeholder with actual version
RUN echo "SEMANTIC_RELEASE_VERSION: ${SEMANTIC_RELEASE_VERSION}"
RUN sed -i "s/SEMANTIC_RELEASE_VERSION/${SEMANTIC_RELEASE_VERSION}/g" index.html
RUN sed -i "s/SEMANTIC_RELEASE_VERSION/${SEMANTIC_RELEASE_VERSION}/g" src/services/EnvVars.ts

RUN npm run build

Achtung: es ist wichtig, das ARG SEMANTIC_RELEASE_VERSION nach der Build Stage zu benennen, weil sonst die Variable leer ist → siehe Docker Dokumentation für weitere Details.

Fazit

Mit dem oben beschriebenen Ansatz wird die Versionsnummer Teil des Build-Artefakts und garantiert somit die korrekte Anzeige in der Applikation. Das dafür notwendige Zusammenspiel aus Semantic Versioning und Docker Build in der GitHub-Action bedarf einer Handvoll Konfigurationen, die jedoch universell für jedes Frontend-Projekt einsetzbar sind. D. h. einmal eingerichtet, ist das Problem mit der korrekten Versionsnummer gelöst.

Schreib mich gerne an, wenn du Zugriff auf die komplette GitHub-Action-YAML-Datei, das Dockerfile oder die sonstigen Konfigurationsdateien benötigst.

call to action background image

Abonniere meinen Newsletter

Erhalte einmal im Monat Nachrichten aus den Bereichen Softwareentwicklung und Kommunikation gespikt mit Buch- und Linkempfehlungen.