Jenkins-Tutorial: Jobs erstellen mit der Job DSL - Feature-Bild

Jenkins-Tutorial: Jobs erstellen mit der Jenkins Job DSL

Autor-Bild
von Sven Woltmann – 26. September 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.)

Im ersten Teil dieser dreiteiligen Artikelserie habe ich dir gezeigt, wie man Jenkins per Ansible als Docker-Container installiert und grundlegend konfiguriert – und wie man Build- und Release-Jobs für Maven-Projekte über das User Interface von Jenkins anlegt.

In diesem zweiten Teil zeige ich dir, wie du die gleichen Jobs über die Jenkins Job DSL als Programmcode erstellst und in Jenkins importierst. Außerdem zeige ich dir, wie du sogenannte Views für die übersichtliche Gestaltung der Job-Liste manuell und programmatisch erstellst.

Den in diesem Artikel erstellten Code findest du in meinem GitLab-Repository unter https://gitlab.com/SvenWoltmann/jenkins-tutorial-demo.

Einführung in die Jenkins Job DSL

In diesem Kapitel erkläre ich dir, was die Jenkins Job DSL ist. Ich zeige dir, wie du IntelliJ konfigurieren kannst, um das Schreiben eines Jobs durch Autovervollständigung zu erleichtern, wie du einen minimalen Job als Code aufsetzt und wie du diesen in Jenkins importierst.

Was ist die Jenkins Job DSL?

Die Jenkins Job DSL ermöglicht die programmatische Erstellung von Jenkins-Jobs mittels Groovy-Code. Diesen kannst du in deinem Git-Repository mit ablegen und somit Änderungen nachverfolgbar machen und Jenkins-Jobs automatisiert generieren. Die DSL wird über das Job DSL Plugin bereitgestellt und ist im Job DSL API Viewer ausführlich dokumentiert.

Job DSL-Support in IntelliJ IDEA aktivieren

IntelliJ IDEA unterstützt von Haus aus Groovy. Mit einem sogenannten GroovyDSL-Skript können wir darüber hinaus IntelliJ mit konkreten DSLs vertraut machen. Dies ermöglicht Autovervollständigung und eine bessere Syntaxhervorhebung.

Die hierfür unter https://github.com/jenkinsci/job-dsl-plugin/wiki/IDE-Support zur Verfügung gestellte Dokumentation ist nicht sehr verständlich und erfordert eine Installation von Gradle. Ich zeige dir jetzt, wie es auch ohne Gradle geht:

Zunächst legen wir in IntelliJ ein neues Maven-Projekt oder -Modul an. Da ich in meinem Demo-Code bisher mit einem Monorepo gearbeitet habe, lege ich das Projekt im Unterverzeichnis jenkins-jobs/ an. Über die pom.xml definieren wir eine Abhängigkeit zur job-dsl-core Library, die im öffentlichen Jenkins-Repository liegt:

<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>eu.happycoders.jenkins-tutorial-demo</groupId>
  <artifactId>jenkins-jobs</artifactId>
  <version>1.0-SNAPSHOT</version>

  <repositories>
    <repository>
      <id>jenkins</id>
      <url>https://repo.jenkins-ci.org/public/</url>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>org.jenkins-ci.plugins</groupId>
      <artifactId>job-dsl-core</artifactId>
      <version>1.76</version>
    </dependency>
  </dependencies>

</project>Code-Sprache: HTML, XML (xml)

Unter src/main/resources/ legen wir eine Datei idea.gdsl mit folgendem Inhalt an:

def jobPath = /.*/jobs/.*.groovy/

def ctx = context(pathRegexp: jobPath)
contributor(ctx, {
  delegatesTo(findClass('javaposse.jobdsl.dsl.DslFactory'))
})Code-Sprache: Groovy (groovy)

Das durch den Projekt-Generator von IntelliJ erzeugte Verzeichnis src/test/ kannst du löschen, dieses werden wir nicht benötigen.

Stattdessen legen wir ein neues Verzeichnis src/jobs/ an. In diesem Verzeichnis werden wir die Jenkins-Jobs speichern. Zum Test des Setups erstelle ich eine Groovy-Datei Test.groovy und gebe "maven" ein. Die Autovervollständigung zeigt mir zwei mavenJob()-Methoden aus der Klasse MavenJob des Job DSL-Plugins an; somit ist das Setup erfolgreich abgeschlossen.

Jenkins Job DSL-Integration in IntelliJ IDEA
Jenkins Job DSL-Integration in IntelliJ IDEA

Grundstruktur des Codes für einen Jenkins-Jobs

Zunächst zeige ich an einem minimalen Beispiel, wie du einen Job schreibst und in Jenkins importierst. In meinem im vorherigen Abschnitt angelegten jobs/-Verzeichnis erstellen wir dafür eine Datei SimpleDSLJob.groovy mit folgendem Inhalt:

mavenJob('Jenkins Tutorial Demo - Simple DSL Job') {
  description 'A very simple demo for the Jenkins Job DSL'

}Code-Sprache: Groovy (groovy)

Die Methode mavenJob() ist hierbei der Einstiegspunkt, durch den der Jenkins-Job generiert wird. Wer Groovy nicht kennt, hier eine kurze Erklärung der Syntax: Der Code in geschweiften Klammern ist eine Closure (das was in Java ein Lambda ist), also eine Funktion, die als Parameter übergeben wird. Wohin wird diese Methode übergeben? Jetzt wird es etwas verrückt: Die Closure ist neben dem Job-Namen der zweite Parameter der mavenJob()-Methode. Wenn der letzte Parameter eines Methodenaufrufs eine Closure ist, kann diese außerhalb der Klammern übergeben werden. Klammern können ebenso komplett weggelassen werden, wie es bei der description()-Methode der Fall ist. Der Code oben ist also gleichbedeutend mit:

mavenJob('Jenkins Tutorial Demo - Simple DSL Job', {
  description('A very simple demo for the Jenkins Job DSL')

})Code-Sprache: Groovy (groovy)

Die Java-Entsprechung dieses Codes wäre:

mavenJob("Jenkins Tutorial Demo - Simple DSL Job", () -> {
  description("A very simple demo for the Jenkins Job DSL");

});Code-Sprache: Groovy (groovy)

Soweit dieser kleine Groovy-Exkurs. Wenn du am Ende dieses Tutorials überlegst, wie der bis dahin entstandene Groovy-Code in Java aussehen würde, wirst du feststellen, dass die Groovy-Darstellung deutlich lesbarer ist.

Import eines mit der Job DSL programmierten Jobs in Jenkins

Prüft bitte vorab, ob du die Jenkins-Plugins "Job DSL" und "Authorize Project" installiert hast und hol dies ggf. nach. Ich hatte diese Plugins im ersten Teil des Artikels ursprünglich nicht aufgelistet und sie erst nachträglich hinzugefügt.

Um den Job in Maven zu importieren, klickst du zunächst auf der Startseite auf "New Item":

DSL-Job in Jenkins importieren – Schritt 1: Job anlegen
DSL-Job in Jenkins importieren – Schritt 1: Job anlegen

Wichtig ist nun zu wissen, dass du nicht den eigentlichen Job anlegst, sondern einen Job, der den in der DSL geschriebenen Job importiert. Als Namen geben wir "Project Jenkins Tutorial Demo - Simple DSL Job Creator" ein. Als Projekttyp wählen wir "Freestyle project" und klicken auf "OK":

DSL-Job in Jenkins importieren – Schritt 2: Job anlegen
DSL-Job in Jenkins importieren – Schritt 2: Job anlegen

Auf der folgenden Seite scrollen wir hinunter zu "Build" (oder klicken auf den entsprechenden Tab) und wählen dort aus dem "Add build step"-Dropdown den Punkt "Process Job DSLs" aus:

DSL-Job in Jenkins importieren – Schritt 3: Build-Schritt hinzufügen
DSL-Job in Jenkins importieren – Schritt 3: Build-Schritt hinzufügen

In dem sich öffnenden Formular wählen wir "Use the provided DSL script" und kopieren das kurze Stück Code in das entsprechende Feld. Vielleicht wunderst du dich, dass das Feld so klein ist. Dies liegt daran, dass wir DSL-Jobs eigentlich nicht auf diese Art importieren, sondern automatisiert, wie ich es im weiteren Verlauf des Tutorials beschreiben werde. An dieser Stelle wollen wir erstmal nur sehen, dass wir unseren minimalen Job zum Laufen bringen.

DSL-Job in Jenkins importieren – Schritt 4: DSL-Code eintragen
DSL-Job in Jenkins importieren – Schritt 4: DSL-Code eintragen

Wir klicken auf "Save" und danach – auf der Job-Seite – auf "Build Now", um den Job zu starten:

DSL-Job in Jenkins importieren – Schritt 5: Creator-Job ausführen
DSL-Job in Jenkins importieren – Schritt 5: Creator-Job ausführen

Nachdem der Job durchgelaufen ist, können wir links unten in der Build History den Job anklicken:

DSL-Job in Jenkins importieren – Schritt 6: Build-Ergebnis ansehen
DSL-Job in Jenkins importieren – Schritt 6: Build-Ergebnis ansehen

Wir sehen nun den Status des Creator-Jobs: wann er ausgeführt wurde, wie lange er gebraucht hat, und wir bekommen unter "Generated Items" einen Link zum neu erstellten Job angezeigt:

DSL-Job in Jenkins importieren – Schritt 7: Build-Ergebnis
DSL-Job in Jenkins importieren – Schritt 7: Build-Ergebnis

Wir folgen den Link zum neu erstellen Job und sehen hier Titel und Beschreibung, wie wir sie im Programmcode festgelegt haben. Wir klicken auf "Configure", um uns die Konfiguration anzusehen:

DSL-Job in Jenkins importieren – Schritt 8: Neu erstellter Job
DSL-Job in Jenkins importieren – Schritt 8: Neu erstellter Job

In der Konfiguration sehen wir außer Namen und Beschreibung erstmal nichts weiter. Mehr haben wir im DSL-Code ja auch nicht definiert:

DSL-Job in Jenkins importieren – Schritt 9: Konfiguration prüfen
DSL-Job in Jenkins importieren – Schritt 9: Konfiguration prüfen

Auf der Jenkins-Startseite sollten jetzt neben den Jobs aus dem ersten Artikel sowohl der Creator-Job als auch der erstellte "Simple DSL Job" angezeigt werden:

Übersicht aller bisher erstellen Jobs
Übersicht aller bisher erstellen Jobs

Job-Konfiguration per Jenkins Job DSL für Maven-Projekte

In diesem Kapitel zeige ich dir, wie du die im ersten Teil manuell erstellten Build- und Release-Jobs eins zu eins in Jenkins Job DSL Programmcode überführst.

Jenkins Build-Job per Job DSL programmieren

Für das Maven-Projekt "library1" erstellen wir im jobs/-Verzeichnis eine Datei BuildJobLibrary1.groovy mit folgendem initialen Inhalt, wie du sie aus dem vorherigen Kapitel kennst.

mavenJob('Jenkins Tutorial Demo - Library 1 (DSL)') {
  description 'Build job for Jenkins Tutorial / Library 1'

}Code-Sprache: Groovy (groovy)

Mit folgendem Code fügen wir die Einstellung hinzu, dass nur die letzten fünf Builds aufgehoben werden:

logRotator {
  numToKeep 5
}Code-Sprache: Groovy (groovy)

Als nächstes fügen wir die Abfrage des Parameters für den Git-Branch hinzu. Die User-Interface-Einstellungen, für die im letzten Artikel zwei Screenshots nötig waren, lassen sich mit nur fünf Code-Zeilen darstellen:

parameters {
  gitParam('Branch') {
    description 'The Git branch to checkout'
    type 'BRANCH'
    defaultValue 'origin/master'
  }
}Code-Sprache: Groovy (groovy)

Als nächstes müssen wir den Sparse-Checkout implementieren für das Repository "[email protected]:SvenWoltmann/jenkins-tutorial-demo.git" auf den Branch "$Branch" und für das Verzeichnis "library1/". Dieses mal wird der Code etwas länger; das liegt daran, dass wir den Sparse Checkout Path und den Polling-Filter über sogenannte "Extensions" konfigurieren müssen. Die Jenkins Job DSL unterstützt diese Features nicht nativ, da sie durch das Git-Plugin hinzugefügten wurden. Wenn du kein Multirepo verwendest und das komplette Repository auschecken willst, dann kannst du den mit "// Add extensions..." markierten Block weglassen.

scm {
  git {
    remote {
      url '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'
    }

    branch '$Branch'

    // Add extensions 'SparseCheckoutPaths' and 'PathRestriction'
    def nodeBuilder = NodeBuilder.newInstance()
    def sparseCheckout = nodeBuilder.createNode(
          'hudson.plugins.git.extensions.impl.SparseCheckoutPaths')
    sparseCheckout
          .appendNode('sparseCheckoutPaths')
          .appendNode('hudson.plugins.git.extensions.impl.SparseCheckoutPath')
          .appendNode('path', 'library1/')
    def pathRestrictions = nodeBuilder.createNode(
          'hudson.plugins.git.extensions.impl.PathRestriction')
    pathRestrictions.appendNode('includedRegions', 'library1/.*')
    extensions {
      extensions << sparseCheckout
      extensions << pathRestrictions
    }
  }
}Code-Sprache: Groovy (groovy)

Für die Konfiguration der Extensions gäbe es auch eine elegantere und besser lesbare Variante:

configure {
  it / 'extensions' / 'hudson.plugins.git.extensions.impl.SparseCheckoutPaths' / 'sparseCheckoutPaths' {
    'hudson.plugins.git.extensions.impl.SparseCheckoutPath' {
      path 'library1/'
    }
  }
  it / 'extensions' / 'hudson.plugins.git.extensions.impl.PathRestriction' {
    includedRegions 'library1/.*'
  }
}Code-Sprache: Groovy (groovy)

Diese funktioniert allerdings nicht innerhalb der sogenannten "Groovy Sandbox", die wir im späteren Verlauf des Tutorials benötigen werden.

Es folgt der Build-Trigger, der alle 15 Minuten prüft, ob sich der Code in GitLab geändert hat und das Projekt ggf. neu baut:

triggers {
  scm 'H/15 * * * *'
}Code-Sprache: Groovy (groovy)

Schließlich legen wir noch den Pfad der Root-POM und die Maven-Build-Parameter fest:

rootPOM 'library1/pom.xml'
goals 'clean install'Code-Sprache: Elixir (elixir)

Damit ist der Job fertig programmiert. Hier ist noch einmal der vollständige Code (den du auch im GitLab-Repository findest):

mavenJob('Jenkins Tutorial Demo - Library 1 (DSL)') {
  description 'Build job for Jenkins Tutorial / Library 1'

  logRotator {
    numToKeep 5
  }

  parameters {
    gitParam('Branch') {
      description 'The Git branch to checkout'
      type 'BRANCH'
      defaultValue 'origin/master'
    }
  }

  scm {
    git {
      remote {
        url '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'
      }

      branch '$Branch'

      // Add extensions 'SparseCheckoutPaths' and 'PathRestriction'
      def nodeBuilder = NodeBuilder.newInstance()
      def sparseCheckout = nodeBuilder.createNode(
            'hudson.plugins.git.extensions.impl.SparseCheckoutPaths')
      sparseCheckout
            .appendNode('sparseCheckoutPaths')
            .appendNode('hudson.plugins.git.extensions.impl.SparseCheckoutPath')
            .appendNode('path', 'library1/')
      def pathRestrictions = nodeBuilder.createNode(
            'hudson.plugins.git.extensions.impl.PathRestriction')
      pathRestrictions.appendNode('includedRegions', 'library1/.*')
      extensions {
        extensions << sparseCheckout
        extensions << pathRestrictions
      }
    }
  }

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

  rootPOM 'library1/pom.xml'
  goals 'clean install'
}Code-Sprache: Groovy (groovy)

Hier auch noch ein Screenshot aus meiner IDE. Wie du siehst, bietet IntelliJ ein Syntax-Highlighting und zeigt bei Methodenaufrufen die Variablennamen an (was hier allerdings oft überflüssig ist, da viele Parameter genauso heißen wie die Methoden):

Jenkins Build Job in IntelliJ IDEA
Jenkins Build Job in IntelliJ IDEA

Den Code importieren wir exakt auf die gleiche Art und Weise, wie ich es im Abschnitt Import eines mit der Job DSL programmierten Jobs in Jenkins gezeigt habe. Den Creator-Job nennen wir "Jenkins Tutorial Demo - Library 1 (DSL) Creator". Wir führen ihn aus und erhalten den neuen Job "Jenkins Tutorial Demo - Library 1 (DSL)". Auf dem Screenshot siehst du an dem blauen Punkt, dass dieser bereits erfolgreich ausgeführt wurde (über den 15-Minuten-Trigger):

DSL-Build-Job und Creator-Job
DSL-Build-Job und Creator-Job

Wenn du nun zwei Browser-Fenster öffnest, eines mit dem manuell erstellten Job und eines mit dem automatisch erstellten, und in beiden auf "Configure" klickt, kannst du die Jobs Zeile für Zeile vergleichen und werdet feststellen, dass diese identisch sind:

Links der manuell erstellte Build-Job – rechts der als Code erstellte Job
Links der manuell erstellte Build-Job – rechts der als Code erstellte Job

Analog erstellen wir für das Maven-Projekt "application1" eine Datei BuildJobApplication1.groovy, in dem wir die eben erstellte Datei BuildJobLibrary1.groovy kopieren und im Code "library" durch "application" ersetzen. So brauchen wir deutlich weniger Zeit, um den Job für die Application zu erstellen, als es bei der manuellen Konfiguration der Fall war.

Jenkins Release-Job per Job DSL programmieren

Da der Release-Job viele Ähnlichkeiten mit dem Build-Job hat, kopieren wir die Datei BuildJobLibrary1.groovy nach ReleaseJobLibrary1.groovy. Wir passen Job-Namen und Description an. Da Releases nur vom Master-Branch erstellt werden, entfernen wir den parameters-Block und ersetzen in der Methode branch() den Parameter-Wert $Branch durch origin/maste'. Da Releases immer manuell gestartet werden, entfernen wir den trigger-Block. Der Code sieht nun so aus:

mavenJob('Jenkins Tutorial Demo - Library 1 - Release (DSL)') {
  description 'Release job for Jenkins Tutorial / Library 1'

  logRotator {
    numToKeep 5
  }

  scm {
    git {
      remote {
        url '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'
      }

      branch 'origin/master'

      // Add extensions 'SparseCheckoutPaths' and 'PathRestriction'
      def nodeBuilder = NodeBuilder.newInstance()
      def sparseCheckout = nodeBuilder.createNode(
            'hudson.plugins.git.extensions.impl.SparseCheckoutPaths')
      sparseCheckout
            .appendNode('sparseCheckoutPaths')
            .appendNode('hudson.plugins.git.extensions.impl.SparseCheckoutPath')
            .appendNode('path', 'library1/')
      def pathRestrictions = nodeBuilder.createNode(
            'hudson.plugins.git.extensions.impl.PathRestriction')
      pathRestrictions.appendNode('includedRegions', 'library1/.*')
      extensions {
        extensions << sparseCheckout
        extensions << pathRestrictions
      }
    }
  }

  rootPOM 'library1/pom.xml'
  goals 'clean install'
}Code-Sprache: Groovy (groovy)

Anstelle des entfernten Branch-Parameters fügen wir die Parameter releaseVersion und nextSnapshotVersion hinzu (unter logRotator):

parameters {
  stringParam('releaseVersion', '',
        'The release version for the artifact. If you leave this empty, ' +
              'the current SNAPSHOT version will be used with the ' +
              '"-SNAPSHOT" suffix removed (example: if the current version ' +
              'is "1.0-SNAPSHOT", the release version will be "1.0").')
  stringParam('nextSnapshotVersion', '',
        'The snapshot version to be used after the release. If you leave ' +
              'this empty, the minor version of the release will be ' +
              'incremented by one (example: if the release is "1.0", the ' +
              'next snapshot version will be "1.1-SNAPSHOT").')
}Code-Sprache: Groovy (groovy)

Der folgende Code fügt die Source Code Management Action "Check out to specific local branch" mit dem Branch-Namen "master" hinzu. Ohne die würde das Maven Goal scm:checkin fehlschlagen. Der Code-Block wird am Ende des Blocks git, also direkt hinter configure eingefügt:

extensions {
  localBranch('master')
}Code-Sprache: Groovy (groovy)

Nun fügen wir den Pre-Build Step vom Typ "Execute system Groovy script" ein, der die Variablen releaseVersion und nextSnapshotVersion setzt, sofern der User diese beim Start des Jobs nicht ausgefüllt hat. Den folgende Code fügen wir hinter dem scm-Block ein:

preBuildSteps {
  systemGroovyCommand '''
    import hudson.model.StringParameterValue
    import hudson.model.ParametersAction

    def env = build.getEnvironment(listener)
    String releaseVersion = env.get('releaseVersion')
    String nextSnapshotVersion = env.get('nextSnapshotVersion')

    if (!releaseVersion) {
      String pomPath = build.workspace.toString() + '/library1/pom.xml'
      def pom = new XmlSlurper().parse(new File(pomPath))
      releaseVersion = pom.version.toString().replace('-SNAPSHOT', '')
      println "releaseVersion (calculated) = $releaseVersion"
      def param = new StringParameterValue('releaseVersion', releaseVersion)
      build.replaceAction(new ParametersAction(param))
    }

    if (!nextSnapshotVersion) {
      def tokens = releaseVersion.split('\\.')
      nextSnapshotVersion =
            tokens[0] + '.' + (Integer.parseInt(tokens[1]) + 1) + '-SNAPSHOT'
      println "nextSnapshotVersion (calculated) = $nextSnapshotVersion"
      def param1 = new StringParameterValue('releaseVersion', releaseVersion)
      def param2 = new StringParameterValue('nextSnapshotVersion',
            nextSnapshotVersion)
      build.replaceAction(new ParametersAction(param1, param2))
    }
    '''.stripIndent()
}Code-Sprache: Groovy (groovy)

Es folgen (innerhalb des preBuildSteps-Blocks im Anschluss an systemGroovyCommand) die drei Pre-Build Steps zum Setzen der Versionsnummer, Umstellen auf Release-Dependencies und Prüfung auf SNAPSHOT-Versionen in der pom.xml:

maven {
  mavenInstallation 'Latest'
  goals 'versions:set ' +
        '-DnewVersion=${releaseVersion} ' +
        '-DgenerateBackupPoms=false'
  rootPOM "library1/pom.xml"
}

maven {
  mavenInstallation 'Latest'
  goals 'versions:use-releases ' +
        '-DgenerateBackupPoms=false ' +
        '-DprocessDependencyManagement=true'
  rootPOM "library1/pom.xml"
}

shell '''
      if find library1/ -name 'pom.xml' | xargs grep -n "SNAPSHOT"; then
        echo 'SNAPSHOT versions not allowed in a release'
        exit 1
      fi
      '''.stripIndent()Code-Sprache: Groovy (groovy)

Zuletzt müssen noch die vier Post-Build Steps angelegt werden: Commit der Release-Version in Git, Tagging der Release-Version, Ändern der Versionsnummer auf die nächste Snapshot-Version und Commit der Snapshot-Version in Git. Die Posts-Build Steps fügen wir ganz am Ende der Datei, hinter goals, ein:

postBuildSteps {
  maven {
    mavenInstallation 'Latest'
    goals 'scm:checkin ' +
          '-Dmessage="Release version ' +
                '${project.artifactId}:${releaseVersion}" ' +
          '-DdeveloperConnectionUrl=scm:git:' +
                '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'
    rootPOM "library1/pom.xml"
  }

  maven {
    mavenInstallation 'Latest'
    goals 'scm:tag ' +
          '-Dtag=${project.artifactId}-${releaseVersion} ' +
          '-DdeveloperConnectionUrl=scm:git:' +
                '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'
    rootPOM "library1/pom.xml"
  }

  maven {
    mavenInstallation 'Latest'
    goals 'versions:set ' +
          '-DnewVersion=${nextSnapshotVersion} ' +
          '-DgenerateBackupPoms=false'
    rootPOM "library1/pom.xml"
  }

  maven {
    mavenInstallation 'Latest'
    goals 'scm:checkin ' +
          '-Dmessage="Switch to next snapshot version: ' +
                '${project.artifactId}:${nextSnapshotVersion}" ' +
          '-DdeveloperConnectionUrl=scm:git:' +
                '[email protected]:SvenWoltmann/jenkins-tutorial-demo.git'
    rootPOM "library1/pom.xml"
  }
}Code-Sprache: Groovy (groovy)

Da der fertige Code der ReleaseJobLibrary1.groovy ziemlich lang ist, verzichte ich an dieser Stelle darauf ihn anzuzeigen. Du findest ihn in meinem GitLab-Repository. Der Code wird analog zu den vorherigen Projekten importiert und ausgeführt, so dass Jenkins schließlich einen neuen Job "Jenkins Tutorial Demo - Library 1 - Release (DSL)" erstellt:

DSL-Release-Job und Creator-Job
DSL-Release-Job und Creator-Job

Wir legen uns noch einmal zwei Browser-Fenster nebeneinander und vergleichen den manuell mit dem automatisiert erstellen Job. Wir stellen zufrieden fest, dass die Jobs nahezu identisch sind. Lediglich die "Additional Behaviours" im Source Code Management haben eine andere Reihenfolge, was aber keine Bedeutung hat.

Links der manuell erstellte Release-Job – rechts der als Code erstellte Job
Links der manuell erstellte Release-Job – rechts der als Code erstellte Job

Analog erstellen wir wieder eine Datei ReleaseJobApplication1.groovy für das "application1"-Projekt, in dem wir die eben erstellte Datei kopieren und im Code "library" durch "application" ersetzen. Insgesamt brauche ich knapp eine Minute, um den Release-Job für die Applikation zu erstellen, in Jenkins zu importieren und ihn auszuführen.

Zwischenfazit nach der Erstellung der Jenkins-Jobs als Code

Wir haben insgesamt vier Groovy-Dateien erstellt. Allerdings fällt auf, dass wir eine ganze Menge Code dupliziert haben. Eine Tatsache, die gegen das "Don't Repeat Yourself"-Grundprinzip der Programmierung verstößt. Wie können wir das verhindern? Mit der bisherigen Art und Weise, wie wir den Code in Jenkins importiert haben, geht das nicht. Von dieser Methode wollen wir uns jetzt auch verabschieden – sie war nur ein Zwischenziel.

In den folgenden Kapiteln zeige ich dir, wie wir Jenkins dazu bringen den Code aus dem Git-Repository zu laden und wie wir gemeinsam verwendeten Code extrahieren können.

Jenkins-Jobs in Views organisieren

Die Job-Liste ist mittlerweile ziemlich lang und unübersichtlich geworden. Glücklicherweise bietet Jenkins die Möglichkeit die Jobs in sogenannten "Views" zu organisieren. Diese Views werden als Tabs am oberen Rand der Job-Liste angezeigt.

Jenkins-View manuell erstellen

Damit wir es einmal per Hand gemacht haben, legen wir für die vier Jobs aus dem ersten Artikel der Serie manuell eine View an. Dazu klicken wir auf das Plus-Zeichen neben dem "All"-Tab:

Jenkins-View manuell hinzufügen – Schritt 1
Jenkins-View manuell hinzufügen – Schritt 1

Als Name tragen wir "Part I" ein und als Typ wählen wir "List View". Mit "OK" gelangen wir zu nächsten Schritt.

Jenkins-View manuell hinzufügen – Schritt 2
Jenkins-View manuell hinzufügen – Schritt 2

Wir wählen die vier im ersten Artikel manuell erstellten Jobs aus und klicken auf "OK":

Jenkins-View manuell hinzufügen – Schritt 3
Jenkins-View manuell hinzufügen – Schritt 3

Damit ist die erste View "Part I" fertig:

Jenkins-View manuell hinzufügen – Schritt 4
Jenkins-View manuell hinzufügen – Schritt 4

Jenkins-View per Code erstellen

Die View für die in diesem Artikel über die Job DSL angelegten Jobs erstellen wir selbstverständlich auch als Code. Wir erstellen hierfür, wieder im jobs/-Verzeichnis des Git-Repositories die Datei ViewDSLJobs.groovy mit folgendem Inhalt:

listView('Part II - DSL') {
  jobs {
    regex '.+\(DSL\).*'
  }

  columns {
    status()
    weather()
    name()
    lastSuccess()
    lastFailure()
    lastDuration()
    buildButton()
  }
}Code-Sprache: Groovy (groovy)

In dieser View geben wir keine konkreten Jobs an. Stattdessen verwenden wir einen regulären Ausdruck, der alle Jobs filtert, die den String "(DSL)" enthalten. Über die columns()-Funktion wird angegeben, welche Spalten in der View angezeigt werden sollen. Lässt man diese Funktion weg, wird nicht etwa eine Standardauswahl an Spalten angezeigt, sondern gar keine. Wir erstellen wieder einen Creator-Job, nennen ihn "View Creator (DSL)", kopieren den DSL-Code hinein und führen ihn aus. Mit wenigen Zeilen Code und ein paar Klicks ist die neue View "Part II - DSL" fertig:

Jenkins-View per Code hinzugefügt
Jenkins-View per Code hinzugefügt

Fazit und Ausblick

In diesem Teil haben wir die Build- und Release-Jobs, die wir im ersten Teil manuell angelegt haben, programmatisch mit der Jenkins Job DSL erzeugt und die Liste der Jobs durch – zunächst manuell und später programmatisch erstellte – Views übersichtlicher gestaltet.

Im dritten und letzten Teil werde ich dir zeigen, wie du mehrfach verwendeten Groovy-Code in Utility-Klassen extrahieren kannst (dazu muss das Skript-Sicherheitssystem konfiguriert werden) und wie du einen sogenannten Seed-Job erstellst, der vollautomatisch Jenkins-Jobs für alle Java-Projekte im Git-Repository (also auch neu hinzukommende) generiert.

Wenn dir dieser Artikel gefallen hat, hinterlasse mir gerne einen Kommentar und teile ihn ü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.