Ein Jenkins Plugin schreiben

Ein Jenkins Plugin schreiben

1. Überblick

Jenkins ist ein Open Source Continuous Integration-Server, mit dem Sie ein benutzerdefiniertes Plugin für bestimmte Aufgaben / Umgebungen erstellen können.

In diesem Artikel werden wir den gesamten Prozess des Erstellens einer Erweiterung durchlaufen, die der Build-Ausgabe Statistiken hinzufügt, nämlich die Anzahl der Klassen und Codezeilen.

2. Konfiguration

Das erste, was zu tun ist, ist das Projekt einzurichten. Luckily, Jenkins provides convenient Maven archetypes dafür.

Führen Sie einfach den folgenden Befehl aus einer Shell aus:

mvn archetype:generate -Dfilter=io.jenkins.archetypes:plugin

Wir erhalten die folgende Ausgabe:

[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart
  (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> io.jenkins.archetypes:empty-plugin (Skeleton of
  a Jenkins plugin with a POM and an empty source tree.)
2: remote -> io.jenkins.archetypes:global-configuration-plugin
  (Skeleton of a Jenkins plugin with a POM and an example piece
  of global configuration.)
3: remote -> io.jenkins.archetypes:hello-world-plugin
  (Skeleton of a Jenkins plugin with a POM and an example build step.)

Wählen Sie nun die erste Option und definieren Sie Gruppe / Artefakt / Paket im interaktiven Modus. Danach müssen diepom.xml verfeinert werden, da sie Einträge wie<name>TODO Plugin</name> enthalten.

3. Jenkins Plugin Design

3.1. Erweiterungspunkte

Jenkins provides a number of extension points. Dies sind Schnittstellen oder abstrakte Klassen, die Verträge für bestimmte Anwendungsfälle definieren und es anderen Plugins ermöglichen, diese zu implementieren.

Beispielsweise besteht jeder Build aus einer Anzahl von Schritten, z. “Checkout from VCS”,“Compile”,“Test”,“Assemble”, usw. Jenkins definiert den Erweiterungspunkt vonhudson.tasks.BuildStep, sodass wir ihn implementieren können, um einen benutzerdefinierten Schritt bereitzustellen, der konfiguriert werden kann.

Ein weiteres Beispiel isthudson.tasks.BuildWrapper - dies ermöglicht es uns, Vor- / Nachaktionen zu definieren.

Wir haben auch ein Nicht-Core-PluginEmail Extension, das den Erweiterungspunkthudson.plugins.emailext.plugins.RecipientProviderdefiniert und das Bereitstellen von E-Mail-Empfängern ermöglicht. Eine Beispielimplementierung finden Sie hier:hudson.plugins.emailext.plugins.recipients.UpstreamComitterRecipientProvider.

Hinweis: Es gibt einen Legacy-Ansatz, bei dem die Plugin-Klassehudson.Plugin erweitern muss. it’s now recommended to use extension points instead.

3.2. Plugin-Initialisierung

Es ist notwendig, Jenkins über unsere Erweiterung zu informieren und darüber, wie sie instanziiert werden sollte.

Zuerst definieren wir eine statische innere Klasse innerhalb des Plugins und markieren sie mit der Annotationhudson.Extension:

class MyPlugin extends BuildWrapper {
    @Extension
    public static class DescriptorImpl
      extends BuildWrapperDescriptor {

        @Override
        public boolean isApplicable(AbstractProject item) {
            return true;
        }

        @Override
        public String getDisplayName() {
            return "name to show in UI";
        }
    }
}

Zweitens müssen wir einen Konstruktor definieren, der für die Objektinstanziierung des Plugins verwendet werden soll, und ihn durch die Annotationorg.kohsuke.stapler.DataBoundConstructormarkieren.

Es ist möglich, Parameter dafür zu verwenden. Sie werden in der Benutzeroberfläche angezeigt und automatisch von Jenkins bereitgestellt.

E.g. Betrachten Sie dieMaven plugin:

@DataBoundConstructor
public Maven(
  String targets,
  String name,
  String pom,
  String properties,
  String jvmOptions,
  boolean usePrivateRepository,
  SettingsProvider settings,
  GlobalSettingsProvider globalSettings,
  boolean injectBuildVariables) { ... }

Es ist der folgenden Benutzeroberfläche zugeordnet:

image

Es ist auch möglich, die Annotation vonorg.kohsuke.stapler.DataBoundSettermit Setzern zu verwenden.

4. Plugin-Implementierung

Wir beabsichtigen, grundlegende Projektstatistiken während eines Builds zu sammeln, daher isthudson.tasks.BuildWrapper der richtige Weg, um hierher zu gelangen.

Implementieren wir es:

class ProjectStatsBuildWrapper extends BuildWrapper {

    @DataBoundConstructor
    public ProjectStatsBuildWrapper() {}

    @Override
    public Environment setUp(
      AbstractBuild build,
      Launcher launcher,
      BuildListener listener) {}

    @Extension
    public static class DescriptorImpl extends BuildWrapperDescriptor {

        @Override
        public boolean isApplicable(AbstractProject item) {
            return true;
        }

        @Nonnull
        @Override
        public String getDisplayName() {
            return "Construct project stats during build";
        }

    }
}

Ok, jetzt müssen wir die eigentliche Funktionalität implementieren.

Definieren wir eine Domänenklasse für die Projektstatistiken:

class ProjectStats {

    private int classesNumber;
    private int linesNumber;

    // standard constructors/getters
}

Und schreiben Sie den Code, der die Daten erstellt:

private ProjectStats buildStats(FilePath root)
  throws IOException, InterruptedException {

    int classesNumber = 0;
    int linesNumber = 0;
    Stack toProcess = new Stack<>();
    toProcess.push(root);
    while (!toProcess.isEmpty()) {
        FilePath path = toProcess.pop();
        if (path.isDirectory()) {
            toProcess.addAll(path.list());
        } else if (path.getName().endsWith(".java")) {
            classesNumber++;
            linesNumber += countLines(path);
        }
    }
    return new ProjectStats(classesNumber, linesNumber);
}

Schließlich müssen wir den Endbenutzern die Statistiken anzeigen. Erstellen wir dafür eine HTML-Vorlage:




    
    $PROJECT_NAME$


Project $PROJECT_NAME$:
Classes number Lines number
$CLASSES_NUMBER$ $LINES_NUMBER$

Und füllen Sie es während des Builds:

public class ProjectStatsBuildWrapper extends BuildWrapper {
    @Override
    public Environment setUp(
      AbstractBuild build,
      Launcher launcher,
      BuildListener listener) {
        return new Environment() {

            @Override
            public boolean tearDown(
              AbstractBuild build, BuildListener listener)
              throws IOException, InterruptedException {

                ProjectStats stats = buildStats(build.getWorkspace());
                String report = generateReport(
                  build.getProject().getDisplayName(),
                  stats);
                File artifactsDir = build.getArtifactsDir();
                String path = artifactsDir.getCanonicalPath() + REPORT_TEMPLATE_PATH;
                File reportFile = new File("path");
                // write report's text to the report's file
            }
        };
    }
}

5. Verwendungszweck

Es ist Zeit, alles, was wir bisher geschaffen haben, zu kombinieren - und in Aktion zu sehen.

Es wird davon ausgegangen, dass Jenkins in der lokalen Umgebung aktiv ist. Bitte beziehen Sie sich ansonsten aufinstallation details.

5.1. Fügen Sie das Plugin zu Jenkins hinzu

Jetzt erstellen wir unser Plugin:

mvn install

Dadurch wird eine*.hpi-Datei imtarget-Verzeichnis erstellt. Wir müssen es in das Jenkins-Plugins-Verzeichnis kopieren (~/.jenkins/plugin standardmäßig):

cp ./target/jenkins-hello-world.hpi ~/.jenkins/plugins/

Zuletzt starten wir den Server neu und stellen sicher, dass das Plugin angewendet wird:

  1. Öffnen Sie das CI-Dashboard beihttp://localhost:8080

  2. Navigieren Sie zuManage Jenkins | Manage Plugins | Installed

  3. Finden Sie unser Plugin

image

5.2. Konfigurieren Sie Jenkins Job

Erstellen wir einen neuen Job für ein Open-Source-Projekt von Apache commons-lang und konfigurieren Sie dort den Pfad zu seinem Git-Repo:

image

Dafür müssen wir auch unser Plugin aktivieren:

image

5.3. Überprüfen Sie die Ergebnisse

Wir sind jetzt fertig. Lassen Sie uns überprüfen, wie es funktioniert.

Wir können das Projekt erstellen und zu den Ergebnissen navigieren. Wir können sehen, dass einestats.html-Datei hier verfügbar ist:

image

Lass es uns öffnen:

image

Das haben wir erwartet - eine einzelne Klasse mit drei Codezeilen.

6. Fazit

In diesem Tutorial haben wir einJenkins-Plugin von Grund auf neu erstellt und sichergestellt, dass es funktioniert.

Natürlich haben wir nicht alle Aspekte der Entwicklung von CI-Erweiterungen behandelt, sondern lediglich einen grundlegenden Überblick, Entwurfsideen und eine erste Einrichtung bereitgestellt.

Und wie immer kann der Quellcodeover on GitHub gefunden werden.