Mon premier plugin Gradle

Olivier Gauthier
BeTomorrow
Published in
8 min readNov 15, 2017

N’ayez pas peur, ce n’est pas très compliqué.

Avant de commencer on pourrait se demander “Mais pourquoi donc ai-je besoin de faire un plugin ? C’est justement pas pour ça qu’on utilise Gradle et plus Maven ou Ant ? 😕”

Il est vrai que Gradle arrive avec une promesse assez sympa. Être souple (façon Ant), apporter l’écosystème Maven (gestion des dépendances, dépôts, conventions, …), tout ça dans un langage plus commode comme Groovy. Fini donc les fichiers de description à rallonge en XML 😛.

Dans les faits c’est un peu différent. Avec le temps nous rajoutons de la logique dans les scripts de build comme par exemple la partie déploiement sur nos serveurs de développement, l’activation des tests d’intégration par rapport à nos propres conventions, etc. Et nous le dupliquons encore et encore à chaque nouveau projet. Et c’est là que ça devient intéressant d’en faire un plugin (notamment pour éviter de retourner 15 ans en arrière à la belle époque de Ant).

Il existe plusieurs façons de faire des plugins avec Gradle, celle qui nous intéresse le plus est la version dite “binaire” qui permet de réutiliser le plugin facilement.

Nous allons donc voir ensemble à travers cet article :

  • la création d’un plugin Gradle et son utilisation ;
  • la configuration ;
  • pour finir, la mise à disposition pour tout le monde.

Côté environnement de développement, vous aurez besoin d’un simple IDE supportant Groovy comme IntelliJ Community par exemple.

Hello World !

Mais avant de commencer, faisons le traditionnel HelloWorld pour poser les bases 😉. C’est parti !

On commence par créer un nouveau projet de type Groovy que l’on appellera par exemple “my-first-gradle-plugin” et on ajoute les dépendances nécessaires au développement d’un plugin Gradle.

On crée ensuite la classe principale de notre plugin que l’on va mettre dans le répertoire src/main/groovy/com/betomorrow/gradle/sample:

Ici on définit une classe qui implémente Plugin<Project>. C’est le point d’entrée de notre plugin. Lorsqu’on va appliquer notre plugin avec apply plugin: 'com.betomorrow.my-first-gradle-plugin' tout ce code sera exécuté. Avant de vérifier si ça fonctionne il reste une petite chose à faire. Pour que Gradle trouve notre plugin il va falloir ajouter un fichier pour lui dire en quelque sorte où se trouve le point d’entrée. Donc dans le répertoire src/main/resources/META-INF/gradle-plugins on va ajouter un fichier com.betomorrow.my-first-gradle-plugin.properties avec le contenu suivant :

Il ne reste plus qu’à installer le plugin localement puis à l’utiliser. Pour cela on va utiliser le plugin “maven-publish”. On modifie donc notre build.gradle comme ceci :

Pour finir on appelle la tâche de publication locale :

./gradle publishToMavenLocal

Voilà, notre premier plugin est prêt, il ne reste plus qu’à l’utiliser. Pour cela rien de très compliqué, il suffit de créer un fichier build.gradle dans le dossier sample par exemple, avec le contenu suivant :

Ici on définit une partie buildscript pour dire à Gradle où trouver notre plugin. Et on applique notre plugin en utilisant son identifiant “com.betomorrow.my-first-gradle-plugin”. On notera que l’identifiant correspond au nom du fichier com.betomorrow.my-first-gradle-plugin.properties sans le “.properties” que nous avons créé précédemment. Ensuite on peut appeler par exemple gradle tasks. Le résultat devrait ressembler à ceci :

Regardez bien le début en dessous de la ligne “> Configure project :”. Vous le reconnaissez ? C’est bien notre plugin qui vient d’être appelé et qui a affiché cette ligne “Hello World !”

Voilà ! Vous avez les bases. On va maintenant pouvoir passer au vrai plugin.

THE plugin !

Nous allons donc faire un plugin qui va permettre de générer un fichier au format JSON à la racine du projet avec quelques infos de build comme le numéro de version, le nom du projet ou le hash du commit et le changelog. Le plugin n’a pas grand intérêt mais il permettra de voir un peu tous les aspects.

On va commencer par déplacer notre HelloWorld dans une tâche pour qu’il ne soit pas appelé directement lors du chargement du build.gradle. Voici à quoi cela ressemble :

Comme vous pouvez le voir nous avons juste déplacé le println("HelloWorld !") dans une tâche à part. On notera que la tâche se déclare de la même façon que si nous l’avions fait directement dans le build.gradle. Installez cette version du plugin et faites gradle tasks dans le répertoire sample comme précédemment. “Hello World !” aura disparu et à la place une tâche “info” sera listée dans le groupe “Other tasks”.

On va maintenant modifier la tâche pour générer un fichier avec quelques informations.

Si on teste cette version du plugin, on va voir que la tâche “info” génère un fichier info.json à la racine du projet de test contenant les informations suivantes :

{
"metadata": {
"name": "my-sample",
"version": "1.0-SNAPSHOT",
"buildDate": "2017-10-26T16:04:27.672Z"
}
}

Rajoutons les informations de révision et un petit changelog. Pour cela nous allons avoir besoin de la librairie grgit. Il faut donc modifier notre build.gradle comme ceci :

grgit n’est pas dans le dépôt maven central mais sur jcenter (mis à disposition par Bintray). Nous avons donc ajouté jcenter dans la déclaration des dépôts ainsi que la dépendance à grgit. On modifie ensuite la classe du plugin comme ceci :

Ici on a utilisé grgit pour récupérer le dernier hash de commit avecgrgit.log().first().getAbbreviatedId() et les 10 derniers messages de commit grâce à grgit.log(maxCommits:10).collect { it.fullMessage } .

Avant de tester notre plugin il nous faut maintenant un historique Git. Pour gagner un peu de temps je vous propose de cloner le dépôt suivant et de mettre votre build.gradle de test dedans

Si on installe et teste le plugin, nous aurons une erreur. En effet, rappelez-vous, nous utilisons une nouvelle dépendance qui ne se trouve pas dans le dépôt maven central. Il faut donc ajouter “jcenter” dans la liste des dépôts du script de build comme ceci.

Le nouveau fichier info.json généré ressemble donc maintenant à ça :

{
"metadata": {
"name": "my-sample",
"version": "1.0-SNAPSHOT",
"buildDate": "2017-10-26T16:04:27.672Z",
"revision": "6b6a1ee"
},
"changelog": [
"Ajout de la tache\n",
"empty project\n"
]
}

Convention over configuration

Bon c’est pas mal tout ça mais si on regarde d’un peu plus près le code du plugin on peut voir qu’on a mis en dur le nom du fichier où mettre les informations (“info.json”) et le nombre de commits max dans le changelog (10). Je ne sais pas vous, mais si j’étais amené à utiliser ce plugin, je voudrais surement pouvoir configurer ce genre de choses. Pour faire ça on va introduire la notion de DSL (Domain Specific Language). On en a déjà utilisé au début, par exemple, pour installer notre plugin localement via le DSL fournit par le plugin “maven-publish”.

publishing {
publications {
gradlePlugin(MavenPublication) {
from components.java
pom.withXml {
asNode().appendNode('description',
'My First Gradle Plugin')
}
}
}
}

Comme son nom l’indique, c’est en gros un nouveau langage dédié au plugin qui va faciliter sa configuration. Par convention, notre plugin utilisera les valeurs précédentes mais permettra de les configurer en utilisant le DSL suivant :

info {
filename = "package.json"
logSize = 5
}

Pour cela on va utiliser ce qui s’appelle des extensions. On ajoute donc une nouvelle classe à notre projet que l’on va appeler InfoExtension.groovy avec le code suivant :

C’est une simple classe Groovy standard qui déclare 2 propriétés filename et logSize initialisées avec nos valeurs par défaut (“info.json” et “10”). Ces valeurs vont pouvoir être redéfinies dans le fichier build.gradle final.

On va utiliser cette extension dans notre plugin comme ceci :

Nous créons ici une extension avec le mot clé “info”, c’est le point d’entrée de notre DSL. Les propriétés définies dans la classe d’extension correspondent à la suite du DSL. Nous avons ensuite remplacé nos valeurs par celles de l’extension info.logSize et info.filename .

Il n’y a plus qu’à la tester en ajoutant par exemple ceci dans le build.gradle de test.

info {
filename = "package.json"
logSize = 1
}

La tâche “info” génère maintenant un fichier package.json avec une seule ligne de log.

Le déploiement

Maintenant que nous avons un plugin qui fonctionne il serait bien de le mettre à la disposition de tous. Pour cela nous allons le déployer sur le dépôt officiel des plugins Gradle https://plugins.gradle.org/. Cette partie est déjà très bien documentée sur le site officiel mais on va faire une petite passe rapide dessus tant que nous y sommes.

On commence donc par créer un compte sur https://plugins.gradle.org/, une fois sur la page on récupère nos clés API et on les met dans les paramètres globaux de Gradle qui se trouvent dans $HOME/.gradle/gradle.properties .

Ensuite on modifie notre build.gradle pour utiliser le plugin de publication Gradle comme ceci :

Par rapport à la version précédente, nous avons ajouté la déclaration du bloc buildScript pour importer le plugin de publication, ensuite nous avons appliqué le plugin avec apply plugin: ‘com.gradle.plugin-publish’ et enfin nous avons ajouté la partie pluginBundle. La dernière étape permet de définir des metadonnées pour par exemple facilement le retrouver à partir de tags ou bien afficher une description sur le site des plugins de la communauté. Pour finir, on utilise la tâche publishPlugins pour déployer notre plugin sur le dépôt officiel.

Déployer son plugin sur le dépôt officiel a plusieurs avantages. Tout d’abord ça permet de participer à la communauté 😛. Ensuite ça permet d’utiliser son plugin plus facilement, pas besoin d’avoir accès au dépôt de l’entreprise via un VPN ou autres accès restreints. Si par exemple on doit livrer les sources d’une application utilisant un plugin custom à un client, il n’y a pas besoin de trouver un moyen pour qu’il puisse l’utiliser, il y aura forcément accès depuis les dépôts publics. Et surtout ça permet d’être encore plus concis dans l’utilisation du plugin. En effet, pour les plugins publiés sur le dépôt officiel on peut utiliser le “nouveau” DSL comme ceci sans avoir à ajouter la partie buildScript.

Il y a cependant quelques petits prérequis pour pouvoir l’utiliser de cette façon :

  • l’id du plugin doit être de format groupid.artifactid ;
  • la version ne doit pas être en SNAPSHOT ;
  • toutes les librairies dont on dépend doivent être déployées sur les dépôts jcenter de Bintray. Dans notre cas grgit est bien sur jcenter donc nous n’avons pas eu de problème.

Voilà c’est terminé, si besoin vous pouvez retrouver le code du plugin fait dans cet article ici.

Au final vous avez créé et publié votre premier plugin. Il ne restera plus qu’à trouver le moyen de tester/debugger tout ça, organiser le code d’un projet de plugin un peu plus complexe et aussi regarder du côté des bonnes pratiques. Mais tout ça fera l’objet d’un second article prochainement.

--

--