Build on Commit mit Jenkins und Git

In der Entwicklungsphase eines jeden Software-Projektes ist eine frühe Erkennung von Fehlern unabdingbar. Bei großen Projekten mit vielen Abhängigkeiten kann es mühsam und langwierig sein, das gesamte Projekt auf dem lokalen Rechner zu kompilieren und zu testen. Abhilfe hierbei können Continuous Integration (CI) und Delivery (CD) Plattformen wie der Jenkins schaffen.​ Diese ermöglichen einen dezentralen Build-Prozess auf einem Server der meist deutlich mehr Leistung hat, als der eigene Rechner. Eine solche CI-Umgebung ermöglicht einen automatisierten Buildprozess, wahlweise event- u./o. zeitgesteuert (z. B. NightlyBuilds). Die Konfiguration einer Build-On-Commit Pipeline im Jenkins ist allerdings auf den ersten Blick nicht so einfach einzurichten. Dieser Blog-Beitrag soll ein Grundverständnis für den Aufbau einer solchen Build-On-Commit-Pipeline vermitteln und an einen Beispiel verdeutlichen.

Zeit gesteuerte Builds

Abb. 1: Mögliche Auslöser für einen Build-Job im Jenkins

In der Abbildung 1 werden die verschiedenen Arten zum Auslösen eines Builds im Jenkins dargestellt. Diese können wie folgt beschrieben werden:

  • ​Builds von außerhalb starten
  • Gerrit event
  • Source Code Management System abfragen
  • Starte Build, nachdem andere Projekte gebaut wurden
Der am häufigsten verwendete Build-Auslöser ist der Zeit gesteuerte Build. Dieser kann durch die Eingabe einer speziellen Syntax, ähnlich der von Cron, genauer spezifiziert werden. Eine Beschreibung der möglichen Eingabewerte kann im Jenkins durch das klicken des '?'-Buttons erfragt werden.

Mit Hilfe dieser Syntax lassen sich somit zeitgesteuerte Builds konfigurieren, die beispielsweise wie folgt aussehen könnten:
​Jenkins-Regel ​Beschreibung
​H/15 * * * 1-5 Löse den Build alle 15 Minuten aus, zwischen Montag und Freitag.
H H 1,15 1-11 *​Löse den Build immer am 1. und 15. eines Monats aus, außer im Dezember.
​45 H(9-16)/2 * * *​Löse den Build alle zwei Stunden zwischen 09:00 Uhr und 16:00 Uhr aus, aber 45 Minuten nach dem ersten Start (09:45-15:45 Uhr).

​Das Problem von zeitgesteuerten Builds liegt allerdings auf der Hand. Das Feedback, ob ein Build erfolgreich war und ob alle Unit-Tests etc. erfolgreich durchgelaufen sind, lässt sich somit für den Entwickler nicht unmittelbar nach dem Commit seines Arbeitsstandes prüfen, sondern erst nach dem Buildlauf, gem. definierter Zeiten. Abhilfe hierbei kann das Auslösen eines Build-Jobs durch das Abfragen des Source-Code Management Systems schaffen.

Reagieren auf Veränderung

Eine Reaktion auf Veränderung im Projekt-Repository lässt sich durch die Verwendung des Build-Auslösers 'Source Code Management System abfragen' realisieren.

Durch die Verwendung dieses Triggers wird beim Abfragen des Source Code Management Systems, in unserem Fall Git, anhand der Commit-ID geprüft, ob der aktuellste Stand bereits vom Jenkins gebaut wurde. Ist dies der Fall, passiert gar nichts. Andernfalls wird der gesamte Build-Prozess, der im Jenkins konfiguriert wurde, durchgeführt. Das Git-Abfrage-Protokoll wird im Jenkins-Job hinterlegt und sieht beispielhaft wie folgt aus:

Started on 16.07.2018 11:10:15
Using strategy: Default
[poll] Last Built Revision: Revision ea6b1a7109306f8443d9bd817d207cbf6e87f59f (origin/master)
using GIT_ASKPASS to set credentials Jenkins
 > /opt/git/bin/git ls-remote -h http://dein-git.server/git/project/test.git # timeout=10
Found 54 remote heads on http://dein-git.server/git/project/test.git
[poll] Latest remote head revision on refs/heads/master is: 6cbcee84ec7170575255f67af82494744f792bcb
Done. Took 0,16 Sekunden
Changes found 

Das Problem:

Auch beim Abfragen des Source Code Management Systems muss man im Jenkins angeben, wann diese Abfrage getätigt werden soll. Es ist natürlich möglich als zeitliche Regel eine ständige Abfrage zu konfigurieren (Jenkins-Regel: '* * * * *'). Hierdurch würde der Jenkins-Job jede Minute das Git Repository prüfen, ob in dem definierten Branch eine Änderung stattgefunden hat.

Die Art der Abfrage kostet eine Menge Rechenleistung und ist somit keine mögliche Lösung.

Die Lösung:

Anstelle der Anfrage des Jenkins-Jobs an das Git-Repository (Pull-Mechanismus), um eine Veränderung feststellen zu können, ist es sinnvoller den Jenkins über eine Anfrage zu informieren (Push-Mechanismus). Der Jenkins überprüft bei einer Veränderung automatisch das Git- Repository, somit werden die Abfragen auf ein Minimum reduziert.

Um diese Konfiguration möglich zu machen, muss der Build-Auslöser 'Source Code Management System abfragen' ausgewählt werden. Wenn keine Eingabe bei der zeitlichen Regel getätigt wird, dann wird der Build nur auf Zuruf angestoßen. Darüber hinaus muss das 'Git Plugin' [5] im Jenkins aktiviert sein, das automatisch folgenden Endpunkt bereitstellt:

curl http://dein-jenkins.server:8080/git/notifyCommit?url=<URL of the Git repository>[&branches=branch1[,branch2]*][&sha1=<commit ID>]

Schickt man nun mittels curl oder ähnlichem eine HTTP-Anfrage an den Jenkins, wird dieser darüber informiert das Git-Repository nach Veränderungen zu überprüfen.

Der Entwickler kann nun also seine Arbeit in Git per Commit an das Remote-Repository schicken und danach im Browser die oben genannte URL aufrufen, daraufhin wird das Projekt automatisch gebaut. 

Git-Hooks

Damit der gesamte Prozess nun wirklich während eines Commits angestoßen wird und keine weitere Nutzer-Interaktion benötigt, kann der Aufruf des Jenkins-Jobs automatisiert über Git-Hooks getätigt werden.

Git-Hooks sind Ereignis-basiert. Immer dann, wenn bestimmte Git-Befehle ausgeführt werden, überprüft die Software das Hooks-Verzeichnis, um festzustellen, ob für das aktuelle Ereignis ein Hook vorliegt und führt diesen dann aus.

Es gibt zwei Arten von Git-Hooks:

  • ​Client-Seitig
    Die Ereignisse für diesen Hook sind nur auf Client-Seite und können daher nur lokal erkannt sowie verarbeitet werden.
  • Server-Seitig
    Die Ereignisse für diesen Hook sind nur auf Server-Seite und werden daher auf dem Server ausgeführt, auf dem das Git-Repository gehostet wird.

Die verschiedenen Git-Hooks, auf die reagiert werden kann, können der Dokumentation von Git [4] entnommen werden.

In der Regel ist die Verwendung des 'PostReceive'-Hooks die beste Wahl. Hierbei handelt es sich um einen Server-Seitigen Hook, der immer nach der Verarbeitung eines Commits aufgerufen wird. Die Verwendung des Server-Seitigen Hooks bietet sich an, da somit die Konfiguration für das Build-On-Commit nur an einer zentralen Stelle geändert werden muss. Bei einem Client-Seitigen-Hook müsste jeder Entwickler diese Einstellungen manuell in seinem Git-Repository vornehmen.

Um einen Hook zu aktivieren, muss eine Datei mit dem Namen 'post-receive' im Hooks-Verzeichnis des Git-Repositories auf dem Server hinterlegt werden. Dieses Verzeichnis wird automatisch bei der Initialisierung eines Git-Repositories angelegt. Der Inhalt der Datei wird als Shell-Skript geschrieben und kann in unserem Beispiel wie folgt aussehen:
#!/bin/sh
curl -s http://dein-jenkins.server:8080/git/notifyCommit?url=http://dein-git.server/git/project/test.git
echo 'Jenkins-Build notified' 

Durch obige Konfiguration wird im ersten Schritt der Jenkins-Server benachrichtigt und im zweiten Schritte eine Bemerkung ausgegeben. 

Wenn nun ein Commit an das Remote-Repository abgeschickt wird (Push), so wird automatisch dieses Skript nach der Verarbeitung auf dem Server ausgeführt. Der Jenkins-Build wird daraufhin ebenfalls automatisch ausgeführt. 
Bei einem Git-Push sieht dies dann wie folgt aus:

$ git push
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 238 bytes | 0 bytes/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Scheduled polling of test-jenkins-job
remote: No Git consumers using SCM API plugin for: http://dein-git.server/git/project/test.git
remote: Jenkins-Build notified
To http://dein-git.server/git/project/test.git
   ea6b1a7..6cbcee8  master -> master
 

Fazit

Die Kombination des Jenkins Git-Plugin mit Git-Hooks ermöglicht eine schnelle und einfache Konfiguration einer Build-On-Commit Pipeline im Jenkins. Wurden alle Schritte wie oben beschrieben umgesetzt, so steht einer Continuos Integration Plattform nichts mehr im Wege. Der Entwickler bekommt somit sofort Feedback über seinen veröffentlichten Arbeitsstand und kann dementsprechend reagieren.

Durch die Zunahme eines automatisierten Deployments im Jenkins auf die Zielumgebung (z.B. Applicationserver, Web-Container etc.), kann die Software sogar einem Tester sofort nach einem erfolgreichen Build-Prozess zur Verfügung gestellt werden.


Glossar

Continuous Integration
Continuous Integration beschreibt den Prozess des fortlaufenden Zusammenfügens von Komponenten zu einer Anwendung. Das Ziel der kontinuierlichen Integration ist die Steigerung der Softwarequalität.

Git
Eine Software zur verteilten Versionsverwaltung von Dateien. Git ist ein sehr beliebtes Tool innerhalb von Softwareentwicklungsprojekten, da hierdurch sowohl die Versionsverwaltung als auch die parallele Arbeit im Team ermöglicht wird.

Jenkins
Eines der bekanntesten Open-Source Applikationen in Bezug auf automatisierte Builds und Deployments für eine Menge an verschiedenen Projekttypen.

Cron
Ein Werkzeug im Linux-Umfeld, dass zur Automatisierung von Zeit gesteuerten Befehlen verwendet wird.

Links

Bildnachweis

© pixabay.com | gedanken-idee-innovation-phantasie-2123971

0
Die Datenverwaltung mit Business Time
Big Data – Informationen neu gelebt (Teil VIII): H...

Unsere Autoren

Technologie Blogs

Tutorials

4 members

Webentwicklung

3 members

Java

3 members

Archiv | Blog-Beiträge

Login