Encore une très bonne nouvelle pour Kotlin ! Le fait d'avoir une fondation à part, disposant d'un modèle économique clair et qui assure au langage sa survit et mieux encore son évolution est ce qu'il fallait faire.
Le parfait contre-exemple que je pourrais donner est ce qu'a fait la fondation Apache avec Maven dans le sens où Apache étant "anti-argent", la fondation a toujours refusé que des contributeurs majeurs de Maven mettent en place un modèle économique de financement de leurs contributions. 15 ans plus tard, le projet n'a qu'une mise à jour tous les 18 mois... Heureusement, des forks sont apparus comme Maven Daemon mais cela fragmente le marché.
Bref, je suis très contente pour Kotlin.
Incroyable, dès que j'ai un problème @Philou arrive avec une solution. Ici Maven Daemon qui propose ce que propose Gradle mais avec Maven (en rapport à mon post précédent).
Je teste ça dans la journée :D
Ça doit faire 4-5 ans que je me remets régulièrement à Gradle dans l'espoir de trouver une solution à un problème pourtant trivial : comment, avec Gradle, mutualiser un processus de build qui doit s'appliquer sur plusieurs projets qui n'ont rien à voir ?
Gradle confond la notion de projets et de modules, donc je dois clarifier ces deux définitions pour la suite :
- Un module, c'est ce qui va produire un jar au sein de votre projet Git et votre projet est composé de modules.
- Donc yn projet, c'est un répo Git et qui en général va produire une application et les modules la composent.
Dans toutes les docs de Gradle, un projet est un module, donc pas besoin d'héritage puisque le build.gradle à la racine va injecter sa conf dans les build.gradle des modules positionnés dans les sous-répertoires.
Sauf que ce genre de bidouilles, ça marche bien dans une startup ne concevant qu'un seul produit, mais imaginez que vous deviez proposer un socle de build gérant Java, Kotlin, Groovy et Scala et qui soit utilisé par 2000 personnes, sur plus de 100 applications et 200 libs, comment faites-vous avec Gradle ?
Permettez-moi de détailler, le processus de build standard inclut :
- La compilation en mode dev (sans optim) et en mode prod (avec optim).
- Les analyses de sécurité.
- Les analyses de code.
- La signature des jars.
- La production des jars des docs et des sources.
- Les analyses des dépendances.
- Etc.
Tout ce processus qui est agnostique du code lui-même DOIT être factorisé pour devenir un standard de l'entreprise et se pose en tant que quality-gate/build-breaker en cas de manquement.
Avec Maven facile, il suffit que le pom parent de votre projet hérite du pom sachant faire tout ça, mais comment faire avec Gradle ? Ce build.gradle "commun" ne peut pas être hérité car Gradle ne permet pas l'héritage et il ne peut pas lancer lui-même le build des 300 répos dont les produits ont des cycles de vie totalement indépendants.
C'est une vraie question car j'aimerai vraiment abandonner Maven (puisqu'il est beaucoup trop lent) au profit de Gradle, mais sans la capacité de mutualiser le processus de build entre deux projets indépendants, cela fait de Gradle un outil pour "amateurs", or sa percée sur le marché me pousse à croire qu'il doit bien exister une astuce mais que je n'ai encore jamais vue. Bref si quelqu'un a l'info...
Comme j'oublie toujours la commande, je me la note ici :
mvn versions:set -DnewVersion=1.2.3
Pour citer l'article.
mvn versions:display-dependency-updates
Scans a project’s dependencies and produces a report of those dependencies which have newer versions available.mvn versions:display-plugin-updates
Scans a project’s plugins and produces a report of those plugins which have newer versions available, taking care of Maven version prerequisites.mvn versions:display-property-updates
Scans a project and produces a report of those properties which are used to control artifact versions and which properties have newer versions available.
Créer un Maven Assembly de référence. Pratique si vous devez gérer plein de choses hors JVM comme des scripts shell.
Tu peux aussi ajouter cette property directement dans le pom.xml de Maven :
<properties>
<maven.test.skip>true</maven.test.skip>
</properties>
Et ça supprimera les tests de façon permanente.
Utiliser le layering Docker via un plugin Maven. Pour @Philou de la part de @Ercou.
Bon, je suis en plein débat avec @Philou et @CCous pour savoir si oui ou non il faut commiter les changements de version de la balise <version>
de nos pom.xml
.
En quoi y a-t-il débat ?
C'est assez simple, je suis partisane du fait qu'il soit nécessaire de commiter le numéro de version des pom.xml et de tagger le commit qui contient cet incrément. Je ne crois pas qu'il faille le faire pour tout, mais la chose est indispensable a minima pour les libs.
Mon argument est que Maven doit être indépendant du gestionnaire de version qui dans l'histoire a changé de nombreuses fois. Pour info, j'ai eu la "chance" de coder sur un projet qui est passé dans cet ordre de Synergy à SVN puis à Git en moins de deux ans ! #TropBien
Bref, le script pour la conf de build sont des sources et il est normal de les tagger avec le code d'autant qu'il s'agit du comportement de base de l'outil.
L'argument que m'oppose @Philou (en dehors du el famoso c'est le progrès faut faire avec son temps), c'est qu'une release consomme énormément de temps de build au niveau des serveurs d'intégration continue, d'autant plus qu'une release exécutée avec le maven-release-plugin
impose de rebuilder deux fois la même chose.
Et mon problème c'est que je suis à la fois d'accord avec @Philou mais je suis aussi d'accord avec moi-même ! #SaletéDeSchizophrénie
Donc j'ai décidé de creuser la question afin d'avoir le meilleur des deux mondes qui consisterait :
- À préserver un numéro de version unique, commun à tous les outils et parfaitement tenu à jour.
- À ne pas augmenter inutilement la charge des serveurs d'intégration continue qui sont souvent à la limite de la rupture. (Pour info, les jobs de build de @Philou durent plus de 6h... Juste un job oui... Donc l'argument du "faut pas rebuilder" est indiscutable)
Solutions possibles
La solution de @Philou et @CCous consiste à ne plus toucher aux versions des POM, mais qu'au moment de la publication dans Nexus ou Artifactory, le haché du commit soit récupéré ainsi que la date du jour et le nom de la branche pour construire un numéro de version unique à base d'une grosse concaténation afin d'estampiller ce dernier dans le répo d’artefacts.
J'oppose à cette bidouille une toute autre bidouille : lors de la release on incrémente le numéro de version mais on ne rebuild pas le code, à ce moment on se contente de récupérer le jar du commit précédent pour le republier sous un nouveau nom. De cette façon le numéro de version de Maven est bien incrémenté et les temps de build du CI restent courts y compris pendant la release (j'en ai déjà parlé avec @Kysofer et il va peut-être nous pondre un plugin Maven).
NDR : soyons clairs, tout cela relève de la grande bidouille mais quand il faut y aller il faut y aller.
En quoi consiste le lien ?
Simplement pour notifier tout le monde que nous ne sommes pas les seuls faisant face à cette problématique et que depuis Maven 3.5.0 trois properties ont été ajoutées afin de faciliter le travail :
- ${revision}
- ${sha1}
- ${changelist}
Ce qui va nous intéresser c'est la property ${revision}
, en effet un POM et son enfant ressembleront à ceci mais en contrepartie il faut alimenter le numéro de version dans la ligne de commande en écrivant :
mvn -Drevision=1.0.0-SNAPSHOT clean install
De cette façon, un pom.xml
ayant le bon numéro de version sera bien généré par Maven, en parallèle le numéro de version ne requiert plus une procédure de commit redondante qui rallongerait les temps de build, le numéro de version peut devenir dynamique et injectable via le C ; et puisque l'on s'inscrit dans ce que propose Maven alors si un développeur voit ${revision}
, il saura que le numéro de version est dorénavant injecté au build et donc qu'on ne tord plus le coup à l'outil pour produire des révisions fictives dans un référentiel de binaires au détriment de la cohérence.
Je dirais que la contrepartie c'est de systématiquement publier le jar des sources avec le jar exécutable afin de préserver la corrélation entre version des sources et version du pom.
Je conclurais en affirmant que les problématiques de build sont pénibles et s'apparentent à de la botanique ! (c) @Chlouchloutte
Comment récupérer une conf Gradle distante pour l'inclure dans son build Gradle (et donc reproduire le mécanisme d'héritage des plugins de Maven avec Gradle) :
apply from: 'http://server-url/nexus/service/local/artifact/maven/redirect?r=repository-name&g=group-name&a=build-common&e=gradle&v=LATEST'
Voilà la solution :
<plugin>
<groupId>org.graalvm.nativeimage</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>19.3.0</version>
<executions>
<execution>
<goals><goal>native-image</goal></goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<skip>false</skip>
<buildArgs> --no-fallback </buildArgs>
</configuration>
</plugin>
La semaine reprend le lundi voyez-vous (en réalité elle ne s'arrête vraiment mais bon) et je ne suis pas toujours motivée comme ce matin par exemple. Et puis mon cher @Philou me sort des p'tits liens qui me font plaisir parce que voilà.
Ici, le fait que le maven-jar-plugin puisse enfin produire des JARs de manière reproductibles ! #Enjoy
Plus d'infos ici et merci @Philou.
Pour obtenir un rapport HTML détaillant quels JAR sont utilisés, inutilisés ou en conflits il faut utiliser la commande :
mvn dependency:analyze-report
Pour un rapport directement dans la console c'est par ici.
Merci à @Philou pour l'astuce.
Une réponse de @Philou sur ce post où je parle des fatjar, de Docker et de leur inefficacité.
Très intéressant.
Imaginez, vous êtes en prestation pour une grosse boite et la cellule d'architecture de votre client active tout un tas de plugins Maven sans comprendre comment fonctionnent ces plugins et ni Maven d'une manière générale.
Vous souhaitez donc bloquer l'héritage de la configuration de ces plugins pour ne laisser que les vôtres. Évidemment vous êtes quand même obligé d'hériter du pom parent provenant de la cellule d'architecture pour récupérer quelques properties bien utiles.
Solution :
1) Vous créez un pom abstrait à la racine de votre projet.
2) Dans ce pom vous ajoutez plugin par plugin (ici avec ANT) la désactivation <inherited>false</inherited>
:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.2</version>
<inherited>false</inherited>
...
</plugin>
</plugins>
</build>
...
</project>
3) Vous créez un second pom abstrait, enfant du premier et duquel héritera tous vos modules.
Et voilà, les modules n'auront plus connaissance des plugins du super-parent.
L'idée n'est pas mal : vous ajoutez des annotations dans votre code Java et un processeur d'annotations piloté par un plugin Maven va générer pour vous la configuration Kubernetes de votre application. Etant donné que je n'aime pas trop m'embêter avec Kubernetes (je n'arrive plus à apprendre toutes les technos de la terre, surtout les technos ops, elles sont en train de me tuer), j'apprécie d'autant plus cette initiative.
Via Riduidel.
Je recherchais un moyen de reproduire avec Maven le comportement du implementation
de Gradle, j'en parlais ici. Pour ceux qui ne connaîtraient pas l'idée est la suivante :
- Un module B tire en tant que dépendance un module A.
- Si un module C tire à son tour le module B en tant que dépendance, et puisque tout est transitif par défaut dans Maven, alors les classes de C pourront importer les classes de A
Ceci est dangereux car rien ne lie explicitement C à A et il devient impossible de supprimer B en tant que dépendance dans le pom de C.
La solution, déclarer le module A en tant que dépendance optionnelle de B de cette manière :
<dependency>
<groupId>com.a</groupId>
<artifactId>module-a</artifactId>
<version>1.0.0</version>
<optional>true</optional>
</dependency>
Si C tire B, alors les classes de A ne seront plus visiblent par C dans le classpath mais elles resteront présentent au runtime.
Mes amis.. à vos pom !
Excellent. Marche même sous Linux ! Je teste ça ce soir et je l'intègre à ma toolbox si ça marche bien.
Via quelqu'un sur Libox River.
Il est très difficile de créer des builds reproductibles en Java via Maven / Gradle & Co. Cette très courte présentation explique bien pourquoi.
Heureusement, il existe un plugin maven permettant de virer les timestamps et les méta-data pour rendre le build reproductible à l'octet près.
Encore une fois, merci à @Philou pour l'info.
Je résume, si vos tests unitaires sont deux à deux distincts, s'ils n'engendre pas de conflits lors de leur exécution en parallèle (donc il faut dissocier les TU des TI qui quant à eux s'appuient sur l'injection de dépendances) alors vous pouvez dire à Surefire d'exécuter en parallèle autant de TEST que vous avez de Thread CPU de disponibles.
Après un benchmark succinct, j'ai divisé par 5 le temps d'exécution de mes tests sur un 4 cœurs physiques et 8 cœurs logiques via la technologique HT (Hyper-Threading chez Intel / Hyper-Transport chez AMD).
Voici comment faire :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M3</version>
<configuration>
<forkCount>1C</forkCount> <!-- C'est la même commande que pour Maven, 1 thread / CPU -->
<reuseForks>true</reuseForks>
</plugin>