Jenkins-Tutorial: Entwicklung eines Seed-Jobs - Feature-Bild

Jenkins-Tutorial: Programmierung eines Seed-Jobs

Autor-Bild
von Sven Woltmann – 9. Oktober 2019

Artikelserie: Jenkins-Tutorial

Teil 1: Build- und Release-Jobs

Teil 2: Jenkins Job DSL

Teil 3: Programmierung eines Seed-Jobs

(Melde dich für den HappyCoders-Newsletter an, um sofort über neue Teile informiert zu werden.)

In Teil eins dieser dreiteiligen Artikelserie habe ich dir gezeigt, wie man Jenkins installiert und konfiguriert – und wie man Build- und Release-Jobs für Maven Projekte über das User Interface von Jenkins anlegt. In Teil zwei haben wir die gleichen Jobs programmatisch mit der Jenkins Job DSL erstellt und die Job-Liste durch Views übersichtlicher gestaltet.

In diesem abschließenden dritten Teil erkläre ich dir, wie du duplizierten Groovy-Code in Utility-Klassen extrahierst und wie du für neue Java-Projekte im Git-Monorepo vollautomatisch Jenkins-Jobs erstellen kannst, bzw. bei Änderungen im Job-DSL-Code alle Jenkins-Jobs gleichzeitig anpassen kannst.

Den in der kompletten Artikelserie erstellten Code findest du in meinem GitLab-Repository https://gitlab.com/SvenWoltmann/jenkins-tutorial-demo.

Jenkins Sicherheitssystem: Script Security

Bevor wir mit der Automatisierung weitermachen, ist es wichtig sich mit dem Skript-Sicherheitssystem von Jenkins vertraut zu machen. Bisher konnten wir alle Skripte problemlos ausführen, weil wir sie als Administrator angelegt haben. Jenkins pflegt eine Whitelist von erlaubten Skripten, zu der automatisch all diejenigen Skripte hinzugefügt werden, die ein Admin anlegt. Diese Skripte können später auch von regulären Usern ausgeführt werden.

Dies gilt allerdings nur für Skripte, die ein Admin direkt in einen Job kopiert. Im Zuge der weiteren Automatisierung wollen wir Jenkins so konfigurieren, dass es Skripte aus dem Git-Repository lädt. Heruntergeladene Skripte sind von regulären Usern nicht ausführbar. Dies schließt auch den SYSTEM-User mit ein, mit dessen Rechten ein Job standardmäßig ausgeführt wird – selbst dann, wenn ein Admin diesen startet. Das Sicherheitssystem von Jenkins schützt uns hier vor potentiell gefährlichem Groovy-Code, der durch das Herunterladen aus dem Git-Repository eingeschleust werden könnte.

Wie können wir heruntergeladene Skripte ausführbar machen?

Es gibt verschiedene Arten heruntergeladene Groovy-Skripte ausführbar zu machen:

  • Nicht ausführbare Skripte landen automatisch in einer Validierungsqueue. Ein Admin kann diese unter "Manage Jenkins" → "In-process Script Approval" manuell freigeben. Das müsste er jedoch bei jeder Änderung am Groovy-Code erneut machen. Dies könnte sich – je nach Team-Zusammensetzung – als unpraktikabel erweisen.
  • Das Sicherheitssystem so konfigurieren, dass der Seed-Job immer als Admin ausgeführt wird (standardmäßig wird er durch den User SYSTEM ausgeführt). Dies ist unsicher, da so potentiell gefährlicher Code eingeschleust werden könnte. Außerdem ist es bei dieser Variante nicht möglich duplizierten Code in Utility-Klassen auszulagern.
  • Script Security komplett deaktivieren ("Manage Jenkins""Configure Global Security" Checkbox "Enable script security for Job DSL scripts" deaktivieren). Dies ist noch unsicherer, da dadurch jeder User in jedem Job potentiell gefährliche Skripte ausführen könnte. In der vorherigen Option war dieses Risiko zumindest auf durch den Seed-Job heruntergeladene Groovy-Dateien reduziert.
  • Den Code in der "Groovy Sandbox" ausführen. In der Sandbox ist der Code grundsätzlich ausführbar, wird jedoch auf einen festgelegten Satz an Operationen beschränkt. Diese Option funktioniert nur zusammen mit der Ausführung als Admin-User, ansonsten müssten auch hier die Skripte wieder über die Validierungsqueue freigeschaltet werden. Die Sandbox verhindert in diesem Fall die Ausführung von schädlichem Code. Dies ist die einzige Option, die es ermöglicht Code in Utility-Klassen auszulagern.

Meiner Meinung nach ist die "Groovy Sandbox" in Kombination mit der Ausführung des Jobs als Admin-User die sinnvollste Option. Im folgenden zeige ich dir, wie du das Sicherheitssystem entsprechend konfigurierst.

Globale Konfiguration der Jenkins-Skript-Sicherheit

Als erstes müssen wir die Möglichkeit aktivieren für einzelne Jobs die Ausführungsrechte zu konfigurieren. Dazu öffnen wir die globalen Sicherheitseinstellungen über "Manage Jenkins" → "Configure Global Security". Dort müssen wir unter "Access Control for Builds" die "Per-project configurable Build Authorization" hinzufügen:

Jenkins Access Control konfigurieren – Schritt 1
Jenkins Access Control konfigurieren – Schritt 1

Bei den sich nun öffnenden Checkboxen sollte die Strategie "Run as User who Triggered Build" bereits aktiviert sein. Hier müssen wir zusätzlich die Option "Run as a specific user" erlauben, um später die Möglichkeit zu haben einen Job so zu konfigurieren, dass dieser immer durch einen Admin-User ausgeführt wird.

Jenkins Access Control konfigurieren – Schritt 2
Jenkins Access Control konfigurieren – Schritt 2

Über den "Save"-Button speichern wir die Änderungen. Alle weiteren Einstellungen tätigen wir später direkt im Jenkins-Job.

Fehlermeldungen des Jenkins-Skript-Sicherheitssystems

Im Folgenden liste ich die Fehlermeldungen auf, die du durch das Sicherheitssystem angezeigt bekommen könntest. Ich gebe außerdem die jeweiligen Ursachen und Lösungsmöglichkeit dazu an. Diesen Abschnitt kannst du erst einmal überspringen. Du kannst hierher zurückkehren, wenn im weiteren Verlauf des Tutorials sicherheitsrelevante Fehlermeldungen auftreten.

ERROR: script not yet approved for use

Jenkins zeigt diese Fehlermeldung an, wenn ein Job versucht ein Skript auszuführen, das nicht durch einen Admin auf die Whitelist gesetzt wurde (entweder explizit, oder in dem der Admin das Skript manuell in einen Job kopiert hat).

Lösungsmöglichkeiten:

  • Jenkins legt das Skript automatisch in die Validierungsqueue. Ein Admin kann es unter "Manage Jenkins""In-process Script Approval" manuell freigeben.
  • Den Job als Admin-User ausführen: Job-Seite → "Authorization""Configure Build Authorization" aktivieren → "Authorize Strategy" auf "Run as Specific User" oder “Run as User who Triggered Build” setzen und entsprechend einen Admin-User hinterlegen oder den Job als Admin-User starten.

ERROR: startup failed: […] unable to resolve class […]

Diese Fehlermeldung bekommst du, wenn du versuchst aus einem Groovy-Script, das nicht in der Groovy Sandbox läuft, auf eine andere Groovy-Klasse auf dem Dateisystem zuzugreifen.

Lösung:

  • Die einzige Möglichkeit, um auf andere Klassen zugreifen zu können, ist es den Code in der Groovy Sandbox auszuführen: Job-Konfiguration → "Process Job DSLs""Use Groovy Sandbox" aktivieren.

ERROR: You must configure the DSL job to run as a specific user in order to use the Groovy sandbox

Diese Fehlermeldung erscheint, wenn du für einen Job die Groovy Sandbox aktiviert hast, das Sicherheitssystem aber nicht so konfiguriert habt, das der Job als ein spezifischer User ausgeführt wird. Standardmäßig wird ein Job immer durch den User "SYSTEM" ausgeführt.

Lösung:

  • Den Job als ein spezifischer User ausführen: Job-Seite → "Authorization""Configure Build Authorization" aktivieren → "Authorize Strategy" auf "Run as Specific User" oder “Run as User who Triggered Build” setzen und entsprechend einen User hinterlegen. Der ausgewählte User sollte wiederum Admin-Rechte haben, ansonsten wirst du die Fehlermeldung "script not yet approved for use" bekommen, solange das Skript nicht explizit von einem Admin freigeschaltet wurde.

ERROR: Scripts not permitted to use method [...] / staticMethod [...]

Diese Fehlermeldung weist uns darauf hin, dass der Aufruf der entsprechenden Methode in der Groovy-Sandbox nicht gestattet ist.

Lösung:

  • Jenkins legt die Methodensignatur automatisch in die Validierungsqueue. Ein Admin muss sie unter "Manage Jenkins""In-process Script Approval" manuell freigeben.

ERROR: […] No signature of method: […] is applicable for argument types: […]

Dieser Fehler tritt auf, wenn man versucht den Sparse Checkout innerhalb der Groovy Sandbox über die oben gezeigte configure()-Methode zu konfigurieren.

Lösung:

  • Anstatt mit der configure()-Methode müssen die Extensions (wie oben gezeigt) über den NodeBuilder konfiguriert werden.

Automatisierung der Job-Erstellung

Im vorherigen Artikel haben wir Jenkins-Jobs als Code geschrieben. Dieser liegt in den Dateien BuildJobApplication1.groovy, BuildJobLibrary1.groovy, ReleaseJobApplication1.groovy, ReleaseJobLibrary1.groovy im Verzeichnis jenkins-jobs/src/jobs/ unseres Monorepos. (Falls du nicht mit einem Monorepo arbeitest, kannst du für die Jenkins-Jobs auch ein separates Repository verwenden.)

Um die Jobs in Jenkins zu importieren, haben wir manuell Creator-Jobs angelegt, jeweils einen Build Step "Process Job DSLs" hinzugefügt, den Groovy-Code hineinkopiert und den Job ausgeführt. Wenn wir in einem Job etwas ändern wollen, müssen wir den Code erneut in den jeweiligen Creator-Job kopieren. Bei vier Jobs ist das noch machbar – bei einigen hundert ist es nicht mehr praktikabel. Das bisherige Verfahren hat außerdem den Nachteil, dass wir mehrfach benötigten Code duplizieren mussten, anstatt diesen in Helfer-Klassen auslagern zu können.

Laden des Jenkins Job DSL-Codes aus dem Projekt-Repository

Zunächst einmal wollen wir den Code nicht manuell in die Creator-Jobs kopieren, sondern sogenannte Loader-Jobs den Code aus dem Git-Repository laden lassen. Das lässt sich ziemlich einfach einrichten.

Ich zeige hier am Beispiel des "Jenkins Tutorial Demo - Library 1" Jobs, wie das geht. Für die drei anderen Jobs kann das Vorgehen eins zu eins übernommen werden.

Wir erstelle auf der Jenkins-Startseite über "New Item" einen neuen Job. Als Namen vergeben wir "Jenkins Tutorial Demo - Library 1 (DSL) Loader", als Typ wählen wir wieder "Freestyle project". Unter "Build" fügen wir einen "Process Job DSLs"-Schritt hinzu. Soweit ist der Vorgang identisch zum Erstellen der Creator-Jobs (daher verzichte ich bis hierhin auf Screenshots).

Anstatt nun "Use the provided DSL script" auszuwählen, belassen wir es bei der Standard-Auswahl "Look on Filesystem". Um die Job-Datei im Filesystem verfügbar zu machen, müssen wir diese zunächst aus GitLab auschecken. Wir scrollen also noch einmal hoch zu "Source Code Management", wähle "Git" aus und gebe die Repository-URL "[email protected]:SvenWoltmann/jenkins-tutorial-demo.git" an:

Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 1
Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 1

Unter "Additional Behaviours" fügen wir "Sparse Checkout Paths" hinzu und tragen dort jenkins-jobs/ ein, so dass nur dieses Verzeichnis ausgecheckt wird. (Wenn du die Jenkins-Jobs in einem separaten Repository hast, kannst du diesen Schritt weglassen.)

Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 2
Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 2

Nun scrollen wir wieder runter zu "Build" und tragen bei "DSL Scripts" den Pfad der Groovy-Datei, jenkins-jobs/src/jobs/BuildJobLibrary1.groovy, ein. Außerdem aktivieren wir die Option "Use Groovy Sandbox" (wie im vorangegangenen Kapitel erläutert):

Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 3
Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 3

Wir speichern den Job über "Save". Bevor wir ihn ausführen können, müssen wir noch festlegen, dass der Job als Admin-User ausgeführt wird (ebenfalls im vorangegangenen Kapitel erläutert). Dazu klicken wir auf der Job-Seite auf "Authorization". (Wenn du diesen Punkt nicht siehst, dann prüf bitte, ob du die Schritte im Abschnitt Globale Konfiguration der Jenkins-Skript-Sicherheit korrekt durchgeführt hast.)

Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 4
Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 4

Auf der "Authorization"-Seite aktivieren wir "Configure Build Authorization" und wählen als Strategie entweder "Run as User who Triggered Build" oder "Run as Specific User" aus. Der Unterschied ist folgender:

  • Run as User who Triggered Build: Der Job muss letztendlich von einem Admin-User gestartet werden, um den heruntergeladenen Groovy-Code ausführen zu können (es sei denn dieser wurde bereits durch einen Admin auf die Whitelist gesetzt).
  • Run as Specific User: Bei dieser Option kann man einen User angeben, mit dessen Rechten – unabhängig vom eingeloggten User – der Job ausgeführt wird. Wenn man hier also einen Admin-User einträgt, kann jeder eingeloggte User den Job starten.

Da ich auf meinem Jenkins-Server der einzige User bin, wähle ich die Option "Run as User who Triggered Build":

Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 5
Laden von Jenkins-Jobs aus dem Git-Repository – Schritt 5

Wir speichern die Änderung mit "Save" und starten den Job über "Build Now". Der Job bricht trotz aller sicherheitsrelevanten Einstellungen, die wir vorgenommen haben, zunächst mit einer Fehlermeldung ab:

Ausführen von Jenkins-Jobs aus dem Git-Repository – Groovy-Sandbox-Fehlermeldung
Ausführen von Jenkins-Jobs aus dem Git-Repository – Groovy-Sandbox-Fehlermeldung

Diese Fehlermeldung können wir nicht vermeiden. Sie besagt, dass der Aufruf der angegebene Methode innerhalb der Groovy-Sandbox nicht erlaubt ist. Gleichzeit hat Jenkins die Methodensignatur in die Validierungsqueue geschrieben, in der wir sie wie folgt freigeben. Wir erreichen das Approval-Formular über "Manage Jenkins""In-process Script Approval":

Freigabe von Methoden-Signaturen für die Groovy-Sandbox
Freigabe von Methoden-Signaturen für die Groovy-Sandbox

Wir klicken auf "Approve" und sehen daraufhin die Methodensignatur in der Liste der freigegebenen Signaturen.

Die Schritte "Job ausführen" und "Methodensignatur freigeben" müssen wir noch dreimal wiederholen, so dass die Liste der freigegebenen Signaturen schließlich vier Einträge enthält:

Freigegebene Methoden-Signaturen für die Groovy-Sandbox
Freigegebene Methoden-Signaturen für die Groovy-Sandbox

Wir starten den Job ein fünftes Mal. Jetzt läuft er erfolgreich durch:

Laden von Jenkins-Jobs aus dem Git-Repository – Erfolgreich ausgeführt
Laden von Jenkins-Jobs aus dem Git-Repository – Erfolgreich ausgeführt

Auch wenn wir das Freischalten der Methoden-Signaturen außer Acht lassen (dies müssen wir nicht wiederholen), war das immer noch ein ziemlich hoher manueller Aufwand. Wenn wir das jetzt noch für die Release-Jobs und das "application1"-Projekt machen müssten, hätten wir gegenüber der vorherigen, komplett manuellen, Lösung nicht viel gewonnen. Wie es einfacher geht, zeige ich im nächsten Abschnitt.

Erstellen von mehreren Jenkins-Jobs mit einem einzigen Seed-Job

Um die drei restlichen Jobs (also den Build-Job für das "application1"-Projekt und die zwei Release-Jobs) und die View zu generieren, müssen wir keine zusätzlichen Loader-Jobs anlegen. Stattdessen können wir einfach in dem zuvor erstellten Loader-Job unter "DSL Scripts" die vier weiteren Groovy-Dateien hinzuzufügen:

Erstellen von mehreren Jenkins-Jobs mit einem einzigen Seed-Job – Konfiguration
Erstellen von mehreren Jenkins-Jobs mit einem einzigen Seed-Job – Konfiguration

Da der Loader-Job nun nicht mehr nur den Build-Job für das "library1"-Projekt erzeugt, können wir ihn umbenennen von "Jenkins Tutorial Demo - Library 1 (DSL) Loader" in "Jenkins Tutorial Demo - Seed Job". Wir führen ihn erneut aus und sehen folgende Statusmeldung:

Erstellen von mehreren Jenkins-Jobs mit einem einzigen Seed-Job – Ergebnis
Erstellen von mehreren Jenkins-Jobs mit einem einzigen Seed-Job – Ergebnis

Es wurden mit einem einzigen Job erfolgreich vier Jenkins-Jobs und eine View angelegt. Das ging jetzt wirklich schnell!

Was passiert eigentlich mit Jobs, die bereits existieren? Sofern sich am Groovy-Code nichts verändert hat, bleibt auch der Job unverändert. Bei Änderungen im Groovy-Code wird der Job angepasst. Der Workspace und die Build History des Jobs bleiben dabei erhalten.

Gemeinsam verwendeten Code extrahieren

Beim letzten Zwischenfazit habe ich bemängelt, dass wir einiges an Code dupliziert haben (z. B. den Code-Block für den Sparse Checkout). Als wir im vorherigen Artikel den Code manuell in Jenkins importiert haben, hatten wir keine Möglichkeit gemeinsame Funktionalität auszulagern. Jetzt, wo der Code im Dateisystem liegt, können wir sehr wohl wiederholt vorkommenden Code extrahieren, in dem wir Utility-Klassen anlegen, die von allen Jobs verwendet werden.

Wir erstellen im Unterverzeichnis util/ drei Klassen ScmUtils.groovy, ReleaseUtils.groovy und ViewUtils.groovy und extrahieren dorthin einen Großteil des duplizierten Codes. Die bisherigen Groovy-Dateien lassen wir unverändert und legen folgende vier Job-Dateien und eine View-Datei zusätzlich an:

jenkins-jobs/src/jobs/BuildJobLibrary1WithUtils.groovy
jenkins-jobs/src/jobs/BuildJobApplication1WithUtils.groovy
jenkins-jobs/src/jobs/ReleaseJobLibrary1WithUtils.groovy
jenkins-jobs/src/jobs/ReleaseJobApplication1WithUtils.groovy
jenkins-jobs/src/jobs/ViewDSLJobsWithUtils.groovy

Die neuen Job-/View-Dateien sowie die Utility-Klassen findest du im GitLab-Repository. Sie hier aufzulisten würde den Artikel unnötig aufblähen. Ich habe oben die kompletten Pfade angegeben, so dass wir diese Liste direkt in den Seed-Job kopieren und diesen starten können. Der Job läuft erfolgreich durch und hat vier neue Jobs und eine neue View erzeugt:

Erstellen von Jenkins-Jobs mit Hilfe von Utility-Klassen
Erstellen von Jenkins-Jobs mit Hilfe von Utility-Klassen

Wir können nun für ein neues Java-Projekt ziemlich schnell Build- und Release-Jobs erstellen und diese über den Seed-Job in Jenkins importieren. Einen Großteil der Funktionalität können wir jetzt zentral in den Utility-Klassen anpassen. Nach einer Änderung müssen wir lediglich den Seed-Job starten.

Erweiterung des Seed-Jobs

Wenn wir ein neues Projekt hinzufügen, müssen wir allerdings noch immer a) zwei Groovy-Dateien kopieren und anpassen, b) diese in den Seed-Job eintragen und c) diesen starten.

Die Punkte b) und c) lassen sich recht einfach ändern: statt im Seed-Job konkrete Groovy-Dateien aufzulisten, können wir auch Wildcards verwenden; und um den Seed-job nicht manuell starten zu müssen, können wir beispielsweise einen zeitbasierten Trigger definieren.

Zunächst ersetzen wir die Liste der konkreten Dateinamen durch folgende drei Wildcard-Einträge:

jenkins-jobs/src/jobs/BuildJob*.groovy
jenkins-jobs/src/jobs/ReleaseJob*.groovy
jenkins-jobs/src/jobs/View*.groovy 

Wir könnten hier auch jenkins-jobs/src/jobs/*.groovy angeben, doch im jobs/-Verzeichnis liegt auch noch die Beispieldatei SimpleDSLJob.groovy und ich möchte hier eine gewisse Kontrolle über die auszuführenden Dateien behalten.

Verwendung von Wildcards in DSL-Skript-Dateinamen des Seed-Jobs
Verwendung von Wildcards in DSL-Skript-Dateinamen des Seed-Jobs

Um den Seed-Job automatisch auszuführen, fügen wir einen Trigger ein, der das Git-Repository alle 15 Minuten auf Änderungen prüft:

Automatisches Ausführen des Seed-Jobs durch Build-Trigger
Automatisches Ausführen des Seed-Jobs durch Build-Trigger

Damit der Trigger funktioniert, müssen wir die "Authorize Strategy" für den Seed-Job ändern. Diese ist bisher auf "Run as User who Triggered Build" eingestellt. Der Build-Trigger führt den Job als User "SYSTEM" aus. Dieser User hat nicht die Berechtigung Code in der Groovy-Sandbox auszuführen. Daher müssen wir die Strategie umstellen auf "Run as Specific User" und einen User mit Admin-Rechten eintragen. Ich hinterlege hier der Einfachheit halber mich selbst, du kannst aber auch einen separaten User mit Admin-Rechten hierfür erstellen.

Umstellen der Authorize Strategie des Seed-Jobs auf "Run as Specific User"
Umstellen der Authorize Strategie des Seed-Jobs auf "Run as Specific User"

Das einzige, was wir jetzt noch tun müssen, um einen neuen Job anzulegen, ist die entsprechenden Groovy-Dateien im Git-Repository zu erstellen. Alles weitere geschieht dann automatisch. Auch diesen letzten manuellen Schritt können wir noch automatisieren. Wie das geht, zeige ich dir im nächsten – und gleichzeitig letzten Kapitel dieser Artikelserie.

Vollautomatische Generierung von Build- und Release-Jobs für neue Projekte

Es steht nirgends geschrieben, dass Groovy-Dateien eins-zu-eins auf Jenkins-Jobs abgebildet werden müssen. Letztendlich enthält eine Groovy-Datei ein ausführbares Programm, das in den bisherigen Beispielen einmalig die Methode mavenJob() aufruft. Solch ein Programm können wir – wie jedes andere auch – um Schleifen und Verzweigungen erweitern und damit beliebig komplexe Logik implementieren.

Im Folgenden erstellen wir zwei generische Job-Generatoren: einen für Build-Jobs und einen für Release-Jobs. Die Generatoren suchen im Monorepo nach pom.xml-Dateien und erstellen für jede (sofern es sich nicht um ein Modul eines Multimodulprojekts handelt) einen Jenkins-Job. Diese Möglichkeit ist ein großer Vorteil des Monorepos. Wenn wir diese Logik für Projekte implementieren wollten, die über mehrere Repositories verteilt sind, müssten wir dafür über eine API des Repository-Servers die vorhandenen Repositories auslesen und diese auschecken.

Der generische Build-Job in der Datei BuildJobGeneric.groovy:

import javaposse.jobdsl.dsl.jobs.MavenJob
import util.ProjectUtils
import util.ScmUtils

final String repoUrl = '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'

List<String> projectPaths = ProjectUtils.findProjectPaths(__FILE__)

projectPaths.each { projectPath ->
  MavenJob job = mavenJob('Jenkins Tutorial Demo - Build ' +
        ProjectUtils.removeTrailingSlash(projectPath)) {
    description "Automatically generated build job for Maven project " +
          "detected in folder $projectPath."

    logRotator {
      numToKeep 5
    }

    triggers {
      scm 'H/15 * * * *'
      snapshotDependencies true
    }

    rootPOM "${projectPath}pom.xml"
    goals 'clean install'
  }

  ScmUtils.setupGit(job, true, repoUrl, projectPath)
}Code-Sprache: Groovy (groovy)

Der Code ruft zunächst die Funktion ProjectUtils.findProjectPaths() auf und übergibt den Dateinamen des aktuell ausgeführten Skripts, der durch Jenkins in der Variable __FILE__ bereitgestellt wird. Die Methode gibt eine Liste der Verzeichnisse aller Maven-Projekte im Repository zurück. Über diese Liste wird nun iteriert und für jedes Projekt ein Jenkins-Job erstellt. Der Code innerhalb der Schleife entspricht dem Code, den wir im Abschnitt Gemeinsam verwendeten Code extrahieren erstellt haben (BuildJobLibrary1WithUtils.groovy und BuildJobApplication1WithUtils.groovy), mit dem einzigen Unterschied, dass Job-Name und -Beschreibung aus dem Projektpfad generiert werden.

Der generische Release-Job, ReleaseJobGeneric.groovy sieht wie folgt aus. Auch hier entspricht der Code innerhalb der Schleife dem zuvor erstellten Code in ReleaseJobLibrary1WithUtils.groovy und ReleaseJobApplication1WithUtils.groovy.

import javaposse.jobdsl.dsl.jobs.MavenJob
import util.ProjectUtils
import util.ReleaseUtils
import util.ScmUtils

final String repoUrl = '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'

List<String> projectPaths = ProjectUtils.findProjectPaths(__FILE__)

projectPaths.each { projectPath ->
  MavenJob job = mavenJob('Jenkins Tutorial Demo - Release ' +
        ProjectUtils.removeTrailingSlash(projectPath)) {
    description "Automatically generated release job for Maven project " +
          "detected in folder $projectPath."

    logRotator {
      numToKeep 5
    }

    rootPOM "${projectPath}pom.xml"
    goals 'clean install'
  }

  ScmUtils.setupGit(job, false, repoUrl, projectPath)

  ReleaseUtils.setupReleaseParams(job)
  ReleaseUtils.setupPreBuildSteps(job, projectPath)
  ReleaseUtils.setupPostBuildSteps(job, projectPath, repoUrl)
}Code-Sprache: Groovy (groovy)

Die Klasse ProjectUtils mit den Methoden findProjectPaths() und removeTrailingSlash() findest du, ebenso wie die anderen Utility-Klassen auch, im GitLab-Repository. Die Methoden sind dokumentiert, so dass deren Funktionsweise gut verständlich sein sollte.

Zusätzlich erstellen wir eine Datei ViewGeneric.groovy, die eine View für alle generisch erstellten Jobs erzeugt:

import javaposse.jobdsl.dsl.View
import util.ViewUtils

View view = listView('Part III - Generic') {
  jobs {
    regex '(Jenkins Tutorial Demo - Build |Jenkins Tutorial Demo - Release ).*'
  }
}

ViewUtils.addDefaultColumns(view)Code-Sprache: Groovy (groovy)

Bevor du den Seed-Job aufrufen kannst, musst du noch den "Sparse Checkout" wieder deaktivieren, so dass die findProjectPaths()-Methode die library1/- und application1/-Verzeichnisse findet. (Falls du die Jenkins-Jobs in einem separaten Repository hast, musst du stattdessen zusätzlich das Repository – bzw. die Repositories – mit dem Java-Code auschecken).

Sparse Checkout im Seed-Job deaktivieren
Sparse Checkout im Seed-Job deaktivieren

Wenn du den Seed-Job ausführst, wirst du zunächst wieder Fehlermeldungen bekommen, da der Code Methoden verwendet, die für die Groovy-Sandbox noch nicht freigeschaltet sind. Diese musst du wieder – eine nach der anderen – über "Manage Jenkins" → "In-process Script Approval" freigeben. Wenn du damit fertig bist, sollten folgende Methodensignaturen in der Whitelist enthalten sein:

Freigegebene Methoden-Signaturen für die Ausführung des Seed-Jobs in der Groovy-Sandbox
Freigegebene Methoden-Signaturen für die Ausführung des Seed-Jobs in der Groovy-Sandbox

Der Seed-Job sollte nun die View "Part III - Generic" mit folgenden vier Jobs angelegt haben:

Vollautomatisch angelegte Build- und Release-Jobs
Vollautomatisch angelegte Build- und Release-Jobs

Unser Groovy-Code hat nun also vollautomatisch Build- und Release-Jobs für alle im Monorepo vorhandenen Java-Projekte angelegt. Probiert doch zum Abschluss mal folgendes aus: erstellt im Monorepo ein neues, simples Maven-Projekt. Kopiert beispielsweise das "library1"-Projekt in ein "library2"-Projekt. Daraufhin sollten für dieses automatisch Build- und Release-Jobs in Jenkins erscheinen.

Fazit

In dieser Artikelserie habe ich dir mit Build- und Release-Jobs für Maven-Projekte, sowie mit der Jenkins Job DSL und den sogenannten "Seed Jobs" einige der wichtigsten Funktionen von Jenkins gezeigt. Es gibt zahllose Erweiterungsmöglichkeiten, hier ein paar Beispiele:

  • Du kannst den Seed-Job automatisch Build-Jobs für Feature-Branches anlegen lassen.
  • Du kannst deine Release-Jobs Docker-Container bauen und diese in eine Docker-Registry pushen lassen (mit dem Fabric8 Maven Plugin).
  • Du kannst Web-Applikationen per Blue-Green Deployment deployen.
  • Du kannst Microservices in ein Kubernetes-Cluster deployen (ebenfalls mit dem Fabric8 Maven Plugin).

Letztendlich kannst du durch Shell-Scripte in den verschiedenen Build-Schritten und eine umfangreiche Auswahl an Plugins beliebig komplexe Build- und Deploy-Logik implementieren.

Hast du Probleme beim Nachvollziehen der einzelnen Schritte? Kommst du irgendwo nicht weiter? Dann schreib gerne einen Kommentar unter den Artikel; ich werde mein Bestes geben dir weiterzuhelfen.

Wenn dir der Artikel gefällt, teile ihn gerne über einen der Share-Buttons am Ende. Möchtest du informiert werden, wenn neue Artikel auf HappyCoders.eu veröffentlicht werden? Dann klick hier, um dich für den HappyCoders.eu Newsletter anzumelden.