Een Jenkins-plug-in schrijven

1. Overzicht

Jenkins is een open-source Continuous Integration-server, waarmee u een aangepaste plug-in kunt maken voor een bepaalde taak / omgeving.

In dit artikel gaan we door het hele proces van het maken van een extensie die statistieken toevoegt aan de build-uitvoer, namelijk het aantal klassen en regels code.

2. Installatie

Het eerste dat u moet doen, is het opzetten van het project. Gelukkig biedt Jenkins handige Maven-archetypen daarom.

Voer gewoon de onderstaande opdracht uit vanuit een shell:

mvn-archetype: genereer -Dfilter = io.jenkins.archetypes: plug-in

We krijgen de volgende uitvoer:

[INFO] Project genereren in interactieve modus [INFO] Geen archetype gedefinieerd. Met behulp van maven-archetype-quickstart (org.apache.maven.archetypes: maven-archetype-quickstart: 1.0) Kies archetype: 1: remote -> io.jenkins.archetypes: empty-plugin (skelet van een Jenkins-plug-in met een POM en een lege source tree.) 2: remote -> io.jenkins.archetypes: global-configuration-plugin (skelet van een Jenkins-plug-in met een POM en een voorbeeld van een globale configuratie.) 3: remote -> io.jenkins.archetypes : hello-world-plugin (skelet van een Jenkins-plug-in met een POM en een voorbeeld build-stap.)

Kies nu de eerste optie en definieer groep / artefact / pakket in de interactieve modus. Daarna is het nodig om de pom.xml - omdat het items bevat zoals TODO-plug-in.

3. Ontwerp van Jenkins-plug-in

3.1. Uitbreidingspunten

Jenkins biedt een aantal uitbreidingspunten. Dit zijn interfaces of abstracte klassen die contracten definiëren voor bepaalde gebruikssituaties en die andere plug-ins in staat stellen deze te implementeren.

Elke build bestaat bijvoorbeeld uit een aantal stappen, b.v. "Afrekenen bij VCS", "Compileren", "Test","Monteren", etc. Jenkins definieert hudson.tasks.BuildStep uitbreidingspunt, zodat we het kunnen implementeren om een ‚Äč‚Äčaangepaste stap te bieden die kan worden geconfigureerd.

Een ander voorbeeld is hudson.tasks.BuildWrapper - dit stelt ons in staat om pre / post acties te definiëren.

We hebben ook een niet-core e-mailextensie-plug-in die de hudson.plugins.emailext.plugins.RecipientProvider uitbreidingspunt, waarmee e-mailontvangers kunnen worden verstrekt. Een voorbeeldimplementatie is hier beschikbaar: hudson.plugins.emailext.plugins.recipients.UpstreamComitterRecipientProvider.

Opmerking: er is een legacy-benadering waarbij de plugin-klasse moet worden uitgebreid hudson.Plugin. Echter, het wordt nu aanbevolen om in plaats daarvan uitbreidingspunten te gebruiken.

3.2. Initialisatie van plug-in

Het is noodzakelijk om Jenkins te vertellen over onze extensie en hoe deze moet worden geïnstantieerd.

Eerst definiëren we een statische innerlijke klasse binnen de plug-in en markeren deze met de hudson. extensie annotatie:

klasse MyPlugin breidt BuildWrapper uit {@Extension openbare statische klasse DescriptorImpl breidt BuildWrapperDescriptor uit {@Override openbare boolean isApplicable (item AbstractProject) {return true; } @Override public String getDisplayName () {retourneer "naam die moet worden weergegeven in gebruikersinterface"; }}}

Ten tweede moeten we een constructor definiëren die moet worden gebruikt voor het instantiëren van objecten van de plug-in en deze markeren met de org.kohsuke.stapler.DataBoundConstructor annotatie.

Het is mogelijk om er parameters voor te gebruiken. Ze worden weergegeven in de gebruikersinterface en worden automatisch geleverd door Jenkins.

Bijv. overweeg de Maven-plug-in:

@DataBoundConstructor public Maven (String-doelen, String-naam, String pom, String-eigenschappen, String jvmOptions, boolean usePrivateRepository, SettingsProvider-instellingen, GlobalSettingsProvider globalSettings, boolean injectBuildVariables) {...}

Het is toegewezen aan de volgende gebruikersinterface:

Het is ook mogelijk om org.kohsuke.stapler.DataBoundSetter annotatie met setters.

4. Implementatie van plug-ins

We zijn van plan om tijdens een build basisprojectstatistieken te verzamelen, dus hudson.tasks.BuildWrapper is de juiste manier om hier naartoe te gaan.

Laten we het implementeren:

class ProjectStatsBuildWrapper breidt BuildWrapper uit {@DataBoundConstructor public ProjectStatsBuildWrapper () {} @Override public Environment setUp (AbstractBuild build, Launcher launcher, BuildListener listener) {} @Extension public static class DescriptorImpl breidt BuildWrapperDescriptor uit {@OooleProanRide is public). waar; } @Nonnull @Override public String getDisplayName () {return "Bouw projectstatistieken tijdens het bouwen"; }}}

Ok, nu moeten we de daadwerkelijke functionaliteit implementeren.

Laten we een domeinklasse definiëren voor de projectstatistieken:

class ProjectStats {private int classesNumber; privé int linesNumber; // standard constructors / getters}

En schrijf de code die de gegevens opbouwt:

private ProjectStats buildStats (FilePath root) gooit IOException, InterruptedException {int classesNumber = 0; int linesNumber = 0; Stack toProcess = nieuwe Stack (); toProcess.push (root); while (! toProcess.isEmpty ()) {FilePath-pad = toProcess.pop (); if (path.isDirectory ()) {toProcess.addAll (path.list ()); } else if (path.getName (). endsWith (". java")) {classesNumber ++; linesNumber + = countLines (pad); }} retourneer nieuwe ProjectStats (classesNumber, linesNumber); }

Ten slotte moeten we de statistieken aan eindgebruikers laten zien. Laten we daarvoor een HTML-sjabloon maken:

    $ PROJECT_NAME $ Project $ PROJECT_NAME $: 
Klassen nummerLijnen nummer
$ CLASSES_NUMBER $$ LINES_NUMBER $

En vul het tijdens het bouwen:

public class ProjectStatsBuildWrapper breidt BuildWrapper {@Override public Environment setUp (AbstractBuild build, Launcher launcher, BuildListener listener) {return new Environment () {@Override public boolean tearDown (AbstractBuild build, BuildListener listener) gooit IOException, InterruptedException = build {ProjectStats (stats) build.getWorkspace ()); String report = GenereerReport (build.getProject (). GetDisplayName (), stats); Bestand artifactsDir = build.getArtifactsDir (); String path = artifactsDir.getCanonicalPath () + REPORT_TEMPLATE_PATH; File reportFile = nieuw bestand ("pad"); // schrijf de tekst van het rapport naar het bestand van het rapport}}; }}

5. Gebruik

Het is tijd om alles wat we tot nu toe hebben gemaakt te combineren - en het in actie te zien.

Aangenomen wordt dat Jenkins actief is in de lokale omgeving. Raadpleeg anders de installatiedetails.

5.1. Voeg de plug-in toe aan Jenkins

Laten we nu onze plug-in bouwen:

mvn installeren

Dit zal een * .hpi bestand in het doelwit directory. We moeten het kopiëren naar de Jenkins plug-ins directory (~ / .jenkins / plugin standaard):

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

Laten we tot slot de server opnieuw opstarten en ervoor zorgen dat de plug-in wordt toegepast:

  1. Open het CI-dashboard op // localhost: 8080
  2. Navigeren naar Beheer Jenkins | Beheer plug-ins | Geïnstalleerd
  3. Vind onze plug-in

5.2. Configureer Jenkins Job

Laten we een nieuwe job maken voor een open-source Apache commons-lang-project en het pad naar zijn Git-opslagplaats daar configureren:

Daarvoor moeten we ook onze plug-in inschakelen:

5.3. Bekijk de resultaten

We zijn nu helemaal klaar, laten we eens kijken hoe het werkt.

We kunnen het project bouwen en naar de resultaten navigeren. We kunnen zien dat a stats.html bestand is hier beschikbaar:

Laten we het openen:

Dat is wat we hadden verwacht - een enkele klasse met drie regels code.

6. Conclusie

In deze tutorial hebben we een Jenkins plug-in helemaal opnieuw en zorgde ervoor dat het werkt.

Natuurlijk hebben we niet alle aspecten van de ontwikkeling van CI-extensies behandeld, we hebben alleen een basisoverzicht, ontwerpideeën en een eerste opzet gegeven.

En, zoals altijd, is de broncode te vinden op GitHub.