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.
Edit la conclusion est fausse. Si l'on veut que le module A soit présent au runtime comme au compile-time, il faudra l'ajouter explicitement.
Les <optional>true</optional>
ne sont utiles que si l'on se sert de frameworks comme Spring qui passent leur temps à faire de la détection de chargement au runtime.
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>
Trouver quels jars sont en conflits dans votre build Maven en une ligne : mvn dependency:tree -Dverbose
Si votre variable JAVA_HOME
est égale à GRAALVM_HOME
alors cette configuration Maven suffit :
<plugin>
<groupId>com.oracle.substratevm</groupId>
<artifactId>native-image-maven-plugin</artifactId>
<version>1.0.0-rc14</version>
<executions>
<execution>
<goals>
<goal>native-image</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
Bon, je vais résumer ici les différents scopes de Maven parce que l'outil évolue !
compile :
Si A déclare B en scope "compile" alors A tire B ainsi que toutes ses dépendances transitives au moment de la compilation, des tests, du packaging et du run.
test :
Si A déclare B en scope "test" alors A tire B ainsi que toutes ses dépendances transitives mais uniquement au moment de l'exécution des tests.
provided :
Si A déclare B en scope "provided" alors A tire B ainsi que toutes ses dépendances transitives au moment de la compilation, des tests, mais pas du packaging. Aussi, il faudra que B et ses dépendances transitives soient présentes sur l'environnement de run pour que tout fonctionne (typiquement, c'est le cas avec un Tomcat).
system :
Fait la même chose que "provided" mais avec les API native du système d'exploitation (eg. C / Rust, etc). Ce scope est à utiliser principalement lorsque l'on fait du JNI (Java Native Interface).
runtime :
Fait la même chose que "provided" mais pour les micro-services et non les serveurs d'applications. Dit autrement, la dépendance n'est pas accessible au moment de la compilation, donc il n'est pas possible de l'importer explicitement dans votre code, cependant cette dépendance sera incluse dans le package généré et copiée avec vos JAR ; contrairement aux dépendances en scope "provided" qui elles ne sont pas copiées dans le package générée puisqu'elles sont fournies par le serveur d'application.
import :
Si A déclare B en scope "import" alors A inclura la section <dependencyManagement>
de B. A noter que ce scope ne peut être affecté à une dépendance que si celle-ci est déclarée dans la section <dependencyManagement>
de A.
Voici ma configuration (voir la ligne <Xlint>
) :
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<configuration>
<!-- Je n'ai pas d'interception dans le jar courant, on l'ignore ici -->
<!-- Remarque : les éléments sont séparés par des virgules -->
<Xlint>adviceDidNotMatch=ignore</Xlint>
<complianceLevel>${jvm.target}</complianceLevel>
<deprecation>true</deprecation>
<encoding>${project.encoding}</encoding>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<verbose>true</verbose>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${version.aspectj}</version>
</dependency>
</dependencies>
</plugin>
Je découvrir un type de packaging que Maven est capable de produire : bundle.
Je copie-colle ci-dessous la définition :
This kind of artifact is an OSGi bundle, typically produced using the maven-bundle-plugin which is part of Apache Felix.
The plugin treats all the classes available to your module (the module's own classes, classes provided by dependencies, other classes on the classpath) as one giant set, then lets you select a subset of those classes to bundle into a jar. It also creates the necessary manifest information to make the artifact an OSGi bundle.
So the artifact you're pulling in by using this dependency is a jar, however it's a jar built by choosing a subset from a much larger set of classes, not just the classes that were defined inside the abdera-core module.
If you have a look at the pom for the abdera project you'll see the maven-bundle-plugin configuration which will give you an idea of which classes have been added to the bundle, and which have been held back.
Bref, l'artifact org.mockito:mockito-core:2.28.2 est maintenant de type bundle.