Blog

So schreibt, testet und veröffentlicht ihr ein individuelles Gradle-Plugin

JUL 14, 2017

Starte deine Plugin-Entwicklung mit einem dokumentierten Beispiel

Thierry Lacour

A Belgian working from Malmö. Thierry has a Bachelor’s degree in Application Development and has been with us since 2015 as an Automation Toolsmith. He enjoys computer and tabletop gaming and is an infinite source of optimism and humor. A good man to have in a crisis.

Ein praxisorientierter Leitfaden zum Schreiben, Testen und Veröffentlichen eigener Gradle-Plugins – inklusive Demo-Repository für einen schnellen Einstieg!

Einführung

Plugins eignen sich hervorragend, um Gradle-Funktionalität bereitzustellen – ob als öffentliche Plugins für Tool-Integrationen wie das Artifactory-Plugin oder als Unternehmens-Plugins, die intern gemeinsame Tasks bereitstellen, wie jene, bei deren Entwicklung wir unsere Kunden unterstützen.

Leider empfand ich die Dokumentation und Beispiele zum Schreiben eines eigenen Gradle-Plugins als etwas verstreut. Deshalb habe ich ein sehr einfaches Gradle-Plugin als Grundlage erstellt, das mir – und jetzt auch euch – einen schnellen Einstieg in die Entwicklung eines eigenen Gradle-Plugins bieten soll. Es enthält Hello, world!-Tasks, Tests und Möglichkeiten zur Veröffentlichung. Parallel dazu habe ich diesen Blog als Dokumentation geschrieben. Viel Spaß!

Das Plugin-Repository

Das Gradle-Plugin-Grundgerüst findet ihr auf GitHub unter Praqma/gradle-plugin-bootstrap. Es ist ein vollständig funktionsfähiges Gradle-Plugin. Ihr könnt es gerne als Ausgangspunkt nutzen: Klont es einfach und passt es an eure Anforderungen an.

Die einzelnen Bestandteile

Ich gehe kurz auf die einzelnen Bestandteile eines Gradle-Plugins ein. Im Repository findet ihr alles Erwähnte, aber hier kann ich es ausführlicher beschreiben, als es in Kommentaren möglich wäre.

Der Einstiegspunkt

Beispiel zu finden in:src/main/groovy/com/praqma/demo/DemoPlugin.groovy

Dies ist das Herzstück des Plugins. Hier findet ihr die Methode apply, da sie org.gradle.api.Plugin implementiert. Gradle ruft diese Methode auf, wenn es euer Plugin auf ein Projekt anwendet. Hier könnt ihr eure Tasks, Extensions und vieles mehr hinzufügen.

Im Beispiel-Plugin habe ich solche Bestandteile in ein separates Modul ausgelagert. Das dient nur dazu, die Hauptklasse des Plugins nicht unnötig groß werden zu lassen. Seht es als Empfehlung, wenn ihr viele unterschiedliche Tasks zu eurem Plugin hinzufügen möchtet.

Den Einstiegspunkt registrieren

Beispiel zu finden in:build.gradle

Bevor Gradle euer Plugin anwenden kann, müsst ihr ihm mitteilen, wo es dieses findet. Wendet dazu das Gradle-Plugin java-gradle-plugin an und konfiguriert es in der Datei build.gradle. Bindet das Plugin über den Block plugins am Anfang eurer Datei build.gradle ein:

plugins {    id 'java-gradle-plugin' }

Konfiguriert euer Plugin im Block gradlePlugin, der von java-gradle-plugin bereitgestellt wird. Im Block plugins könnt ihr für jedes Plugin im Projekt einen Eintrag hinzufügen. Wir halten es einfach und haben nur ein Plugin. Deshalb fügen wir darunter einen beliebig benannten Block hinzu, um unser Plugin zu konfigurieren und zwei Eigenschaften festzulegen:

  • Die id ist die Kennung eures Plugins und wird verwendet, um es anzuwenden:plugins id: 'com.praqma.demo'

  • Die implementationClass verweist auf die Klasse des Einstiegspunkts, damit Gradle sie finden kann. In diesem Fall ist das com.praqma.demo.DemoPlugin.

Die id ist die Kennung eures Plugins und wird verwendet, um es anzuwenden:

plugins {     id: 'com.praqma.demo' }

Die implementationClass verweist auf die Klasse des Einstiegspunkts, damit Gradle sie finden kann. In diesem Fall ist das com.praqma.demo.DemoPlugin.

Die vollständige Konfiguration sieht etwa so aus:

gradlePlugin {    plugins {        demoPlugin {            id = 'com.praqma.demo'            implementationClass = 'com.praqma.demo.DemoPlugin'        }    }}

Tasks hinzufügen

Beispiel zu finden in:src/main/groovy/com/praqma/demo/greeting/GreetingModule.groovy

Hier habe ich zwei Tasks hinzugefügt: Einer zeigt die Verwendung von Eigenschaften aus Projekt-Extensions, der andere die Verwendung von Projekteigenschaften. Das Hinzufügen von Tasks zum Plugin unterscheidet sich kaum davon, Tasks zu einem gewöhnlichen Gradle-Projekt hinzuzufügen – ruft einfach die Methode task für das Projekt auf.

Auch hier gilt: Tasks in Module aufzuteilen, ist keineswegs erforderlich. Es ist lediglich meine Gewohnheit, damit die Plugin-Klasse nicht zu einem Monstrum mit zweitausend Zeilen ausartet.

Task-Typen hinzufügen

Beispiel zu finden in:src/main/groovy/com/praqma/demo/greeting/GreetingTask.groovysrc/main/groovy/com/praqma/demo/greeting/GreetingModule.groovy

Wenn ihr einen benutzerdefinierten Task-Typ erstellt, können Plugin-Nutzer ihre eigenen Tasks darauf aufbauen – ähnlich wie wir unsere Tasks auf den Tasks Zip und Copy aufbauen. Um diese bereitzustellen, fügt ihr die Task-Klassen einfach eurem Plugin hinzu. Allein dadurch können Nutzer Tasks dieses Typs über den vollständig qualifizierten Namen definieren. Zum Beispiel:

import com.praqma.demo.greeting.GreetingTasktask myGreetingTask(type:GreetingTask) {    message = "Howdy"}

Damit eure Nutzer den Task nicht importieren müssen, fügt die Task-Klasse beim Anwenden des Plugins zur ExtraPropertiesExtension des Projekts hinzu. Mit diesem Trick können eure Nutzer über den hier angegebenen Namen auf euren Task zugreifen, ohne ihn importieren zu müssen.

project.ext.GreetingTask = com.praqma.demo.greeting.GreetingTask

Extensions hinzufügen

Beispiel zu finden in:src/main/groovy/com/praqma/demo/greeting/GreetingExtension.groovysrc/main/groovy/com/praqma/demo/greeting/GreetingModule.groovy

Extensions stellen Eigenschaften bereit, die Plugin-Nutzer festlegen können. Sie eignen sich hervorragend, damit Nutzer Werte konfigurieren können, auf die ihr in den Tasks angewiesen seid. Im Demo-Plugin können Nutzer die Begrüßung für den Task helloWorld konfigurieren, indem sie in ihrer Datei build.gradle die Eigenschaft greeting.message festlegen.

Um eurem Plugin eigene Extensions hinzuzufügen, erstellt eine einfache Klasse mit einigen Eigenschaften und fügt sie in der Methode apply eures Plugins als Projekt-Extension hinzu:

project.extensions.create('greeting', GreetingExtension)

Auf diese Eigenschaften könnt ihr über die Extensions des Projekts zugreifen, zum Beispiel:

project.extensions.<extensionName>.<propertyName>

Das Plugin testen

Auf Unit-Tests für das Gradle-Plugin gehe ich nicht näher ein, da sie sich nicht von Unit-Tests in einem gewöhnlichen Groovy-Projekt unterscheiden und es dazu viele gute Ressourcen gibt (siehe den Ressourcenbereich weiter unten). Ich behandle jedoch funktionale Tests und zeige, wie ihr euer Plugin mit einer lokalen Veröffentlichung testet.

Funktionale Tests

Beispiel zu finden in:src/test/groovy/com/praqma/demo/greeting/GreetingModuleTest.groovy

Dank GradleRunner ist das Schreiben funktionaler Tests für ein Gradle-Plugin kinderleicht. Mit den Annotationen @Rule und @Before von JUnit könnt ihr für jeden Test ganz einfach ein temporäres Verzeichnis mit einer Datei build.gradle einrichten, die euer Plugin anwendet. Der GradleRunner fügt euer Plugin zum Klassenpfad des temporären Projekts hinzu, sodass es das zu testende Plugin tatsächlich anwenden kann. Wenn ihr eure Tasks über den GradleRunner ausführt, erhaltet ihr Zugriff auf das Build-Ergebnis und die Textausgabe. Zusammen mit dem Zugriff auf das temporäre Verzeichnis könnt ihr so überprüfen und sicherstellen, dass alles wie erwartet ausgeführt wurde.

Veröffentlichen und Testen auf eurem lokalen Rechner

Ihr könnt euer Plugin im lokalen Maven-Repository eures Rechners veröffentlichen. Dadurch könnt ihr eure lokale Testveröffentlichung auf ein Gradle-Projekt auf eurem Rechner anwenden und Änderungen testen, ohne sie in einem gemeinsamen Repository zu veröffentlichen. Dafür ist etwas Konfiguration nötig, aber der Aufwand hält sich in Grenzen.

Im lokalen Maven-Repository veröffentlichen

Wendet das Plugin maven-publish an, das den Task publishToMavenLocal enthält. Dieser Task veröffentlicht alle definierten Veröffentlichungen in eurem lokalen Repository. Richten wir also eine Veröffentlichung für unser Plugin ein:

publishing {    publications {        pluginPublication (MavenPublication) {            from    components.java            groupId    project.group            artifactId    "demo"            version    project.version        }    }}

Um das Plugin in seinem aktuellen Zustand in eurem lokalen Repository zu veröffentlichen, führt gradle publishToMavenLocal aus.

Ein lokal veröffentlichtes Plugin anwenden

Um ein lokal veröffentlichtes Plugin auf euer Projekt anzuwenden, müsst ihr das lokale Maven-Repository als vertrauenswürdiges Repository in eurem Gradle-Projekt hinzufügen. Glücklicherweise erlaubt Gradle dies, indem ihr mavenLocal() im Closure repository eurer Datei build.gradle aufruft. Zum Beispiel:

buildscript {    repositories {        mavenLocal()    }    dependencies {        classpath "com.praqma:demo:1.0.0"    }}apply plugin: 'com.praqma.demo.DemoPlugin'

Nun könnt ihr eure Gradle-Builds ausführen, und euer Projekt verwendet die lokale Distribution des Plugins.

Das Plugin bereitstellen

Ich erläutere sowohl die Veröffentlichung im öffentlichen Gradle-Plugin-Repository als auch auf einem beliebigen Artifactory-Server. Das Projekt enthält die erforderliche Konfiguration für beides. Löscht einfach die Konfiguration, die ihr nicht verwendet, und passt die andere entsprechend an.

Über das Gradle-Plugin-Repository

Konfiguration

Beispiel zu finden in:build.gradle

Wendet das Plugin com.gradle.plugin-publish auf euer Plugin-Projekt an. Konfiguriert es über den Closure-Block pluginBundle, in dem ihr nützliche Informationen zu eurem Plugin angebt, etwa den Repository-Speicherort, eine Beschreibung und relevante Tags.

pluginBundle {    website = 'https://github.com/Praqma/gradle-plugin-bootstrap'    vcsUrl = 'scm:git@github.com:Praqma/gradle-plugin-bootstrap.git'    tags = ['demo', 'example', 'quickstart']    plugins {        demoPlugin {            id = 'com.praqma.demo.DemoPlugin'            displayName = 'Gradle Multi Git plugin'            description = 'Demo plugin to use as a starting point for custom plugin development'        }    }}

Veröffentlichung

Das Plugin com.gradle.plugin-publish stellt die erforderlichen Tasks bereit, um euer Plugin im Plugin-Portal zu veröffentlichen.

Führt gradle login in eurem Plugin-Repository aus und folgt den Anweisungen, um den Rechner zum Veröffentlichen eures Plugins zu autorisieren.

Führt gradle publishPlugins aus, um euer Plugin im Portal zu veröffentlichen.

Anwendung

Sobald das Plugin veröffentlicht ist, kann es über den Closure-Block plugins in anderen Gradle-Projekten angewendet werden – mithilfe der ID und Version des Plugins. Zum Beispiel:

plugins {    id 'com.praqma.demo.DemoPlugin', version '1.0.0'}

Über Artifactory

Konfiguration

Beispiel zu finden in:build.gradle

Wendet das Plugin com.jfrog.artifactory an und konfiguriert es. Im Closure-Block artifactory konfiguriert ihr alles, was das Plugin benötigt, um das Plugin auf eurem Artifactory-Server zu veröffentlichen. Zum Beispiel:

artifactory {    contextUrl = "http://devops.acmeindustries.com/artifactory"    publish {        repository {            repoKey     = 'plugins-release'            username    = 'joe'            password    = 's3cr3t-p4ss'            maven       = true        }        defaults{            publications("pluginPublication")    // Publication defined below        }    }}

Wir müssen noch festlegen, was wir veröffentlichen. Wendet dazu das Plugin maven-publish an. Damit könnt ihr eine MavenPublication deklarieren, die unser Plugin enthält. Zum Beispiel:

publishing {    publications {        pluginPublication (MavenPublication) {            from    components.java            groupId    project.group            artifactId    "demo"            version    project.version        }    }}

Veröffentlichung

Veröffentlicht euer Plugin mit gradle artifactoryPublish.

Anwendung

Um das Plugin anzuwenden, müsst ihr es zunächst abrufen. Registriert euren Artifactory-Server als Repository und fügt euer Plugin als Abhängigkeit hinzu. Anschließend wendet ihr es wie jedes andere Plugin an. Hier ist eine Beispielkonfiguration aus einem Projekt, das unser Plugin anwendet:

buildscript {    repositories {        maven {            url = "http://devops.acmeindustries.com/artifactory/plugins-release"            credentials {                username = "joe"                password = "s3cr3t-p4ss"            }        }    }    dependencies {        classpath "com.praqma:demo:1.0.0"    }}apply plugin: "com.praqma.demo.DemoPlugin" // Apply with the plugin id

Abschließende Anmerkungen

Damit sollten die Grundlagen abgedeckt sein, um ein individuelles Gradle-Plugin zum Laufen zu bringen. Ich hoffe, das hilft euch dabei, eigene großartige Gradle-Plugins zu schreiben und zu verteilen. Gedanken, Fragen und Vorschläge sind jederzeit willkommen – hinterlasst sie einfach im Kommentarbereich unten!

Referenzen

  • DevOps

Subscribe to our newsletter