Blog

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

JUL 14, 2017

Startet eure 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 praxisnaher Leitfaden zum Schreiben, Testen und Veröffentlichen eigener Gradle-Plugins – inklusive Demo-Repository für einen erfolgreichen Start!

Einleitung

Plugins sind eine gute Möglichkeit, Gradle-Funktionen bereitzustellen – ob als öffentliche Plugins mit Tool-Integrationen wie das Artifactory-Plugin oder als unternehmensinterne Plugins, die gemeinsame Aufgaben bereitstellen, wie diejenigen, deren Entwicklung wir unsere Kunden unterstützen.

Leider fand ich die Dokumentation und Beispiele zum Schreiben eines eigenen Gradle-Plugins etwas verstreut. Deshalb habe ich ein sehr grundlegendes Gradle-Plugin als Ausgangspunkt erstellt, das mir – und jetzt auch euch – einen erfolgreichen Start bei der Entwicklung eines eigenen Gradle-Plugins ermöglichen soll. Es enthält Hello, world!-Tasks, Tests und Möglichkeiten zur Veröffentlichung. Diesen Blog habe ich begleitend als eine Art Dokumentation geschrieben. Viel Spaß!

Das Plugin-Repository

Den Ausgangspunkt für das Gradle-Plugin findet ihr auf GitHub unter Praqma/gradle-plugin-bootstrap. Es handelt sich um ein vollständig funktionsfähiges Gradle-Plugin. Ihr könnt es gern 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. Alles Erwähnte findet ihr im Repository, aber hier kann ich es ausführlicher beschreiben als in den Kommentaren.

Der Einstiegspunkt

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

Das 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 euer Plugin auf ein Projekt angewendet wird. Hier könnt ihr Tasks, Erweiterungen und mehr hinzufügen.

Im Beispiel-Plugin habe ich solche Elemente in ein separates Modul ausgelagert. Das dient nur dazu, zu verhindern, dass die Hauptklasse des Plugins zu groß wird. Betrachtet es als Empfehlung, wenn ihr viele verschiedene 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 angeben, wo es dieses findet. Dafür wendet ihr das Gradle-Plugin java-gradle-plugin an und konfiguriert es in der Datei build.gradle. Wendet das Plugin über den Closure-Block plugins am Anfang eurer Datei build.gradle an:

plugins {    id 'java-gradle-plugin' }

Konfiguriert euer Plugin im Closure-Block gradlePlugin, der von java-gradle-plugin bereitgestellt wird. Im Closure-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 Closure-Block hinzu, um unser Plugin zu konfigurieren und zwei Eigenschaften festzulegen:

  • Die id ist der Bezeichner eures Plugins, der verwendet wird, um euer Plugin 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 der Bezeichner eures Plugins, der verwendet wird, um euer Plugin 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

Ich habe hier zwei Tasks hinzugefügt: Einer zeigt die Verwendung von Eigenschaften aus Projekterweiterungen, der andere die Verwendung von Projekteigenschaften. Das Hinzufügen von Tasks zum Plugin unterscheidet sich kaum vom Hinzufügen von Tasks in einem normalen Gradle-Projekt: 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 zweitausend Zeilen langen Ungetüm wird.

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 eigenen Task-Typ erstellt, können Nutzer des Plugins ihre eigenen Tasks auf euren aufbauen – ähnlich wie unsere Tasks auf den Tasks Zip und Copy basieren. Um diese bereitzustellen, müssen die Task-Klassen lediglich Teil eures Plugins sein. Dann können Nutzer Tasks dieses Typs über ihren 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 praktischen Trick können eure Nutzer über den hier festgelegten Namen auf euren Task zugreifen, ohne ihn importieren zu müssen.

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

Erweiterungen hinzufügen

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

Erweiterungen stellen Eigenschaften bereit, die Plugin-Nutzer festlegen können. Sie eignen sich hervorragend, damit Nutzer Werte konfigurieren können, auf die ihr in euren Tasks angewiesen seid. Im Demo-Plugin können Nutzer die Begrüßung konfigurieren, die im Task helloWorld verwendet wird. Dazu setzen sie die Eigenschaft greeting.message in ihrer Datei build.gradle.

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

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

Auf diese Eigenschaften könnt ihr über die Erweiterungen 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 Ressourcenabschnitt 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 es kinderleicht, funktionale Tests für ein Gradle-Plugin zu schreiben. Mit den Annotationen @Rule und @Before von JUnit könnt ihr für jeden Test mühelos ein temporäres Verzeichnis mit einer Datei build.gradle einrichten, die euer Plugin anwendet. 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 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 prüfen und sicherstellen, dass alles wie erwartet ausgeführt wurde.

Auf eurem lokalen Rechner veröffentlichen und testen

Ihr könnt euer Plugin im lokalen Maven-Repository eures Rechners veröffentlichen. So könnt ihr eure lokale Testveröffentlichung auf ein Gradle-Projekt auf eurem Rechner anwenden und Änderungen testen, ohne sie in einem gemeinsamen Repository veröffentlichen zu müssen. Das erfordert etwas Konfiguration, ist aber recht unkompliziert.

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. Richtet daher 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 im aktuellen Zustand in eurem lokalen Repository zu veröffentlichen, ruft gradle publishToMavenLocal auf.

Ein lokal veröffentlichtes Plugin anwenden

Um ein lokal veröffentlichtes Plugin auf euer Projekt anzuwenden, müsst ihr euer lokales Maven-Repository in eurem Gradle-Projekt als vertrauenswürdiges Repository hinzufügen. Glücklicherweise ermöglicht Gradle dies durch den Aufruf von mavenLocal() im Closure repository eurer Datei build.gradle. Zum Beispiel:

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

Jetzt 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 beide Optionen. Löscht einfach die Konfiguration, die ihr nicht verwenden werdet, 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 pluginBundle, in dem ihr nützliche Informationen zu eurem Plugin angebt, etwa den Speicherort des Repositorys, 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 notwendigen 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 für die Veröffentlichung eures Plugins zu autorisieren.

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

Anwenden

Sobald das Plugin veröffentlicht ist, kann es über den Closure plugins auf andere Gradle-Projekte 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 artifactory konfiguriert ihr alles, was das Plugin benötigt, um euer 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 daher 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.

Anwenden

Um das Plugin anzuwenden, müsst ihr es zuerst abrufen. Registriert euren Artifactory-Server als Repository und fügt euer Plugin als Abhängigkeit hinzu. Wendet es anschließend 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 für die Entwicklung eines eigenen Gradle-Plugins abgedeckt sein. Ich hoffe, das hilft euch dabei, eure eigenen coolen Gradle-Plugins zu schreiben und zu verteilen. Gedanken, Fragen und Vorschläge sind jederzeit willkommen – schreibt sie einfach in den Kommentarbereich unten!

Referenzen

  • DevOps

Subscribe to our newsletter