Maven Release mit der GitLab CI Pipeline

photostockeditor-WbfvGBPDXfA-unsplash

Mit Hilfe des Maven-Release-Plugins kann der Release-Prozess eines Maven-Projektes voll automatisiert werden. Eine solche Automatisierung ist im Zuge einer Continuous Integration und Delivery-Pipeline wünschenswert. 

In Verbindung mit Maven ist unter dem Begriff Release das Deployment eines fertigen Software-Artefakts in ein entsprechendes Repository, wie beispielsweise einem Sonartype Nexus, zu verstehen. Eine fertige Version lässt sich durch die Versionsnummer erkennen, so ist die Version 1.0.0-SNAPSHOT eine vorläufige Version, während die Version 1.0.0 ein fertiges Artefakt symbolisiert.

Setzt man als CI/CD-Platform auf GitLab, so sind ein paar Handgriffe notwendig, damit der Prozess reibungslos durchgeführt werden kann. Diese sind im weiteren Verlauf genauer erläutert.

Aufbau des Plugins

Das Maven-Release-Plugin setzt grundsätzlich auf folgende zwei Phasen, die auch bei der Ausführung angegeben werden müssen (mvn release:prepare release:perform):

Prepare

Innerhalb der Prepare -Phase, wird die aktuelle Version des Maven-Projekts vom "SNAPSHOT"-Marker befreit. Diese vollwertige Version wird daraufhin getaggt und in einer Versionsverwaltung eingecheckt. Daraufhin wird die Versionsnummer erhöht und der "SNAPSHOT"-Marker wieder hinzugefügt. Anschließend werden alle Änderungen dem zentralen Git-Repository, mittels git push, bekannt gemacht.

Perform

Nachdem Prepare folgt die Perform -Phase. In dieser Phase wird die zuvor getaggte Version in ein geeignetes Repository hochgeladen (bspw. Nexus). Hierfür wird die zuvor getaggte Version wieder aus der Versionsverwaltung ausgecheckt und standardmäßig durch den Befehl mvn deploy site-deploy an das hinterlegt Software-Repository (bspw. Nexus) übermittelt.

Weitere Informationen bzgl. dieser Phasen und der gesamten Konfigurationsmöglichkeiten lassen sich auf der Homepage des Plugins entnehmen [1]. Auf Basis dieser beiden Phasen ergeben sich unterschiedliche Voraussetzungen bei der Verbindung des Plugins mit der GitLab-Pipeline, die im nachfolgenden genauer erklärt werden.

Voraussetzung

Folgende Voraussetzungen müssen erfüllt sein, damit das Maven-Release-Plugin innerhalb einer GitLab-Pipeline funktionieren kann:

  • Maven in Version 3.0.4 oder höher
  • Einen lauffähigen GitLab-Runner
  • Zugriff auf die GitLab-CI Pipeline (Datei: .gitlab-ci.yaml)
  • Schreibrechte auf dem GitLab-Repository, welches das Maven-Projekt beinhaltet. Die Schreibrechte sind notwendig, da wir dem Plugin einen Nutzer mitteilen müssen, in dessen Namen die Anpassungen an der Versionsnummer sowie der Tag eingecheckt werden darf.

Darüber hinaus benötigt das Plugin die Informationen über die URL des Repositories, welche in der POM hinterlegt werden müssen:

<project>
    ...
    <scm>
	    <connection>scm:git:https://gitlab.company.de/gitlab/demo-backend.git</connection>
	    <url>https://gitlab.company.de/gitlab/demo-backend.git</url>
    	<tag>HEAD</tag>
    </scm>
    ...
</project> 

Die Gitlab Release-Stage 

Wird eine neue Pipeline in GitLab gestartet, so wird diese automatisch von einem GitLab-Runner entgegen genommen.

Der GitLab-Runner checkt allerdings nicht, wie häufig vermutet, den definierten Branch aus, sondern den exakten Commit (detached HEAD). Auf einem "detached HEAD" kann kein neuer Commit abgesetzt werden, da es sich hierbei nur um einen Zeiger handelt. Der zugrundeliegende Commit könnte sowohl Teil verschiedener Branches als auch Tags sein.

Damit das Maven-Release-Plugin also reibungslos funktionieren kann, ist es notwendig, zunächst auf den definierten Branch, mittels git checkout -B "$CI_BUILD_REF_NAME", zu wechseln. Die Variable CI_BUILD_REF_NAME wird hierbei von GitLab selbst als Umgebungsvariable hinzugefügt und beinhaltet den aktuellen Namen des Branches, für den die Pipeline gestartet wurde.[2]

Nachdem der Wechsel auf den aktuellen Branch erfolgt ist, kann mittels "mvn -B release:prepare release:perform" das Maven-Release-Plugin ausgeführt werden. Die Verwendung von "-B" ist hierbei ebenfalls von großer Bedeutung, da es Maven im Batch-Mode ausführt und daher keinerlei Eingaben über die Kommandozeile erwartet. Eine Stage, welche das Maven-Release-Plugin ausführt könnte bisher also wie folgt aussehen:

Release:
  stage: release
  script:
    - git checkout -B "$CI_BUILD_REF_NAME"
    - mvn -B release:prepare release:perform
  tags:
    - maven-runner
  when: manual
  only:
    refs:
      - master 
Ein erster Testlauf mit der aktuellen Konfiguration liefert allerdings einen Fehler, dass der aktuelle Nutzer keine Berechtigung zum Hinzufügen von Commits im Git-Repository besitzt (Unable to commit files):
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  16.611 s
[INFO] Finished at: 2019-11-18T13:57:57+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-release-plugin:2.5.3:prepare (default-cli) on project demoBackend: Unable to commit files
[ERROR] Provider message:
[ERROR] The git-push command failed.
[ERROR] Command output:
[ERROR] fatal: could not read Benutzername for 'https://gitlab.company.de': Kein passendes Gerät bzw. keine passende Adresse gefunden 

​Konfiguration im Maven-Projekt selbst

Der GitLab-Runner verwendete einen technischen Nutzer zum Auschecken des Repositories, welcher nur Lesenden-Zugriff auf das Repository besitzt. Somit ist in der Regel sichergestellt, dass die Pipeline selbst keine Veränderungen an den Sourcen vornehmen kann. In unserem Fall ist allerdings genau dies das Problem.

Die Lösung für dieses Problem ist die Verwendung eines Nutzers, welcher sowohl Lese- als auch Schreibrechte besitzt. Die Anmeldeinformationen dieses Benutzers können daraufhin in der POM hinterlegt werden, und werden vom Maven-Release-Plugin automatisch verwendet (siehe Zeile 4):

<project>
    ...
    <scm>
	    <connection>scm:git:https:// <<nutzername:password>> @gitlab.company.de/gitlab/demo-backend.git</connection>
	    <url>https://gitlab.company.de/gitlab/demo-backend.git</url>
    	<tag>HEAD</tag>
    </scm>
    ...
</project> 

Damit kein Nutzer sein eigenes Passwort in die Versionskontrolle einchecken muss, ist es möglich, einen Access-Token für GitLab zu erzeugen, der mit einer bestimmten Ausstellungsdauer versehen wird. Weiteres hierzu kann der GitLab Dokumentation [3] entnommen werden.

Ergebnis 

Ist die Konfiguration vollständig, so kann nun ein Testlauf durchgeführt werden. Die Pipeline sollte durchlaufen und in GitLab sollten beispielhaft folgendes dargestellt werden:

Durch das Maven-Release-Plugin wurden drei neue Pipelines gestartet (im Bild von unten nach oben):

  1. Commit für das Entfernen des Suffix "SNAPSHOT" bei der Version
  2. Tag der Release-Version (In diesem Fall 3.0.4)
  3. Commit für das Erhöhen der Version inkl. "SNAPSHOT" auf 3.0.5-SNAPSHOT

Wobei die erste Pipeline (#2240) aufgrund der direkten Nachfolge eines neuen Commits automatisch von GitLab abgebrochen wird.​

Der letzte Feinschliff

Grundsätzlich ist die Konfiguration der Pipeline somit abgeschlossen. Allerdings lassen sich noch ein paar Kleinigkeiten verbessern:

  1. Pipeline für die neuen Commits/Tags skippen
    Es kann unter umständen sinnvoll sein, nach dem Maven Release keine automatischen Pipelines über das Plugin zu triggern. Hierzu muss der Commit-Message nur der Präfix "[skip ci]" hinzugefügt werden. [4]
  2. Name des Tags ändern
    Der Name des Tags wird durch das Maven-Plugin bestimmt und kann entsprechend angepasst werden. Die einfache Version reicht in den meisten Fällen aus. Informationen hierzu können der Dokumentation entnommen werden. [5]
  3. Auslagern der Anmeldeinformation
    Die Anmeldeinformationen bzgl. des Git-Repositores können ebenfalls dem Plugin bei der Ausführung mitgegeben werden. Informationen hierzu sind ebenfalls in der Dokumentation zu entnehmen. [5]

Entsprechend kann die Release-Stage wie folgt angepasst werden und in der POM die Anmeldeinformationen wieder entfernt werden:​

Release:
  stage: release
  script:
    - git checkout -B "$CI_BUILD_REF_NAME"
    - mvn -B -Dusername=$OWN_GITLAB_ENV_USER -Dpassword=$OWN_GITLAB_ENV_PASS -DtagNameFormat="@{project.artifactId}" -DscmCommentPrefix="[skip ci]" release:prepare release:perform
  tags:
    - maven-runner
  when: manual
  only:
    refs:
      - master 

Quellen 

By accepting you will be accessing a service provided by a third-party external to https://blog.ordix.de/