Tutoriel d'introduction à la gestion de dépendances, de versions, et de l'intégration continue.
- Comprendre le fonctionnement de maven, configurer un projet de développement, utiliser les artifacts, et générer des rapports
- Utiliser Git pour sauvegarder et collaborer sur le code source de votre projet, au travers des forges telles que Github et Gitlab
- Utiliser un système d’intégration continue tel que Sonar, Jenkins et Gitlab CI
Vérifiez que vous avez installé sur votre machine de développement :
- Java (JDK) 11+, e.g. Oracle JSE (avec la variable d'environnement JAVA_HOME bien configurée)
- Maven
- Git
- un IDE Java tel qu'Eclipse, IntelliJ IDEA, NetBeans, VS Code, Xcode, etc.
Les examples de commandes indiquées dans ce tutorial sont données pour être exécutées sous Linux. Toutefois, elles trouvent normalement leurs contre parties sous Windows. Vous pouvez également utiliser PowerShell ou une machine virtuelle (e.g., VirtualBox).
- Site de Maven : http://maven.apache.org/
- FAQ MAVEN developpez.com : http://java.developpez.com/faq/maven/
Maven est essentiellement un outil de gestion et de compréhension de projet. Maven offre des fonctionnalités de : construction, compilation ; documentation ; rapport ; gestion des dépendances ; gestion des sources ; mise à jour de projet ; déploiement.
Utiliser Maven consiste à définir dans chaque projet à gérer un script Maven appelé POM : pom.xml. Nous allons voir dans ce TP qu'un POM permet de définir des dépendances, des configurations pour notamment construire, tester, mettre en paquet des artefacts logiciels (exécutables, tests, documentations, archives, etc.). Pour cela, Maven récupère sur des dépôts maven les outils dont il a besoin pour exécuter le POM. Utiliser Maven requière donc : une (bonne) connexion à Internet car il télécharge beaucoup de choses ; de l'espace disque pour la même raison. Les artefacts qu'il télécharge sont originellement stockés dans le dossier .m2 dans votre home-dir. Ce dossier contient également le fichier de configuration Maven : settings.xml.
Pour configurer Maven de manière à changer l'endroit où les artefacts téléchargés seront stockés (e.g., afin d'éviter des problèmes d'espace disque), vous pouvez modifier le fichier settings.xml de la manière suivante :
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<localRepository>/tmp/mavenrepository</localRepository>
<offline>false</offline>
</settings>
Création d’une application basique : pour initialiser un projet Java, vous pouvez utiliser l’archetype maven maven-archetype-quickstart. Vous avez juste à fournir un groupId et un artefactId.
Dans Eclipse:
new -> other -> maven -> maven project. Vous devrez sélectionner l’archetype, l’artifactId et le groupId
En ligne de commande (non nécessaire si vous l’avez fait depuis Eclipse):
mvn archetype:generate \
-DgroupId=[your project's group id] \
-DartifactId=[your project's artifact id] \
-DarchetypeArtifactId=maven-archetype-quickstart
Ou simplement :
mvn archetype:generate \
-DgroupId=[your project's group id] \
-DartifactId=[your project's artifact id]
Vous obtenez la structure de projet jointe
|-- src
| |-- main
| | `-- java
| | `-- [your project's package]
| | `-- App.java
| `-- test
| `-- java
| `-- [your project's package]
| `-- AppTest.java
`-- pom.xml
Par exemple si vous exécutez la commande
mvn archetype:generate \
-DgroupId=fr.esir.omd.ci \
-DartifactId=tpmaven
Vous obtiendrez l’architecture suivante :
|-- src
| |-- main
| | `-- java
| | `-- fr
| | `-- esir
| | `-- omd
| | `-- ci
| | `-- tpmaven
| | `-- App.java
| `-- test
| `-- java
| `-- fr
| `-- esir
| `-- omd
| `--ci
| `-- tpmaven
| `-- AppTest.java
`-- pom.xml
Le fichier pom.xml est le fichier de configuration maven du projet. Il décrit les caractéristiques du projet (son nom, sa famille, sa version, etc.), ainsi que les processus (les « builds ») à exécuter (la compilation, l'exécution des tests, la création d'archive, etc.).
Il existe différentes tâches Maven de base, i.e. fournies par Maven. Les principales sont :
mvn clean
: supprimer le dossier target. Le dossier target d'un projet maven contient toutes les données produites par maven (classes compilées, jar produits, rapports, etc.) ;mvn compile
: lance la compilation du code source du projet Maven (mais pas la compilation des tests) ;mvn test
: mvn compile + lance la compilation et l'exécution des tests ;mvn package
: mvn test + lance les opérations de packaging (exemple : la création de fichiers jar) ;mvn install
: mvn package + installe les jar produits dans le dossier .m2 de l'utilisateur pour une utilisation dans les autres projets en local.
Chaque plugin configuré et utilisé dans un pom peut fournir des tâches spécifiques.
Depuis Eclipse 4.X, le support de maven s’est amélioré. Pour importer votre projet (ne pas faire si vous avez créé votre projet maven depuis eclipse, ce sera par contre à faire après avoir récupéré un projet depuis un serveur git) :
File -> import -> maven -> existing maven project.
Votre projet est configuré.
Intégrer à votre projet l'application FirstPDF.
Vous verrez que le code ne compile pas car il manque une dépendance. Intégrez maintenant la dépendance (<dependencies>...<dependencies>) à itext dans le fichier pom.xml.
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.1</version>
</dependency>
Votre IDE va downloader la dépendance et la mettre automatiquement dans votre classpath. Dans ce sens, cela permet de ne mettre dans votre gestionnaire de source que le code source et le descripteur de projet (pom.xml).
Imaginons que vous souhaitiez ajouter une tâche dans le processus de build. Par exemple, compilez votre code source avec la version Java 11. Ajoutez la section suivante à votre fichier pom.xml (essayez en changant de version) :
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- or whatever version you use -->
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
Vous pouvez ajouter de nombreux plugins dans cette section. Prenez le temps d'aller regarder ici.
Ajoutez des commentaires à votre code, puis ajoutez le code suivant dans la section build du pom.xml de votre projet.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.9.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.1.1</version>
</plugin>
Puis dans la section reporting:
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
</plugin>
</plugins>
</reporting>
Puis lancez en ligne de commande (au même niveau que le fichier pom.xml) : mvn site. Cette tâche crée un site Web pour votre projet. Par défaut, les goals maven générant des fichiers travaillent dans le dossier target se trouvant au même niveau que le fichier pom.xml. Allez dans le dossier target/site et ouvrez le fichier index.html. Vous pouvez regarder la Javadoc générée en cliquant sur Project reports.
Eclipse permet de lancer cette commande maven sans passer par la ligne de commande (voir menu run d'Eclipse).
Ajoutez à la section <plugins> dans <reporting> le plugin checkstyle :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.1</version>
</plugin>
Lancez mvn clean site (le goal clean vide le dossier target). Sur la page Web générée par « mvn site », une nouvelle section Checkstyle a été ajouté dans Project reports.
Quelle est la norme de codage à laquelle se réfère le rapport par défaut ? Comment imposer la norme de codage de Google? Le fichier de configuration de Google est inclus dans checkstyle vous devez juste indiquer dans la configuration que vous souhaitez l’utiliser (cf. https://maven.apache.org/plugins/maven-checkstyle-plugin/examples/custom-checker-config.html). Modifiez votre classe du projet tp1 de façon à diminuer le nombre d'erreurs.
- Site de l'outil CheckStyle : http://checkstyle.sourceforge.net/
- Site du plugin Maven : http://maven.apache.org/plugins/maven-checkstyle-plugin/
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</reporting>
Lien utile : http://maven.apache.org/plugins/maven-jxr-plugin/ Quelle est la valeur ajoutée de ce plugin ? En particulier, montrez sa complémentarité avec CheckStyle. Désormais vous pouvez passer du rapport CheckStyle au code source en cliquant sur le numéro de ligne associé au commentaire CheckStyle.
A quel point les développeurs ont réalisé des tests unitaires ? Quelles parties de l'application n'ont pas été testées ? Ecrivez quelques tests en JUnit (/tpmaven/src/test/java/fr/esir/omd/ci/tpmaven/FirstPdfTest.java), et voyez quelle couverture de code vous obtenez.
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution> <!-- attached to Maven test phase -->
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<outputDirectory>target/jacoco-report</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<reportSets>
<reportSet>
<reports>
<!-- select non-aggregate reports -->
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
Lien utile : https://www.eclemma.org/jacoco/
Ajoutez volontairement du code mort à votre code (e.g., une méthode non utilisée) et identifiez le code mort (e.g., variables ou paramètres non utilisés) et la duplication de code (e.g., code copié/collé = possible bug copié/collé, code 'compliqué', e.g., trop de if...else ...).
Ajoutez à la section <reporting> le plugin PMD :
<project>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.14.0</version>
</plugin>
</plugins>
</reporting>
</project>
Quels sont les deux nouveaux rapports générés ? Qu'est ce que le rapport 'CPD Report' ? Qu'est ce que le rapport 'PMD Report' ?
Combien et quels fichiers ont été modifiés par un développeur ? Commiter votre projet sur GitHub ou sur le GitLab de l’istic.
Ajoutez à la section <reporting> le plugin changelog, et définissez la section <scm> en fonction de votre application et gestionnaire de version :
<project>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-changelog-plugin</artifactId>
</plugin>
</plugins>
</reporting>
<scm>
<connection>scm:git:git://IPSERVEUR/repository1/monappli</connection>
<url>http://IPSERVEUR/svn/monappli-web</url>
</scm>
</project>
ipserveur doit être remplacé par l’IP de votre repo svn ou git.
Lancez mvn site. Que s'est t'il passé ?
Le répertoire /target/site situé dans votre projet contient maintenant trois rapports d'activité :
- changelog : rapport indiquant toutes les activités sur le SCM.
- dev-activity : rapport indiquant par développeur le nombre de commits, de fichiers modifiés.
- file-activity : rapport indiquant les fichiers qui ont été révisés.
Pour cette partie, vous pouvez utiliser à votre convenance soit la forge Gitlab fournie par la plate-forme ISTIC ESIR, soit GitHub. Mettez votre code sur GitHub (ou GitLab):
-
créez un nouveau repository via l'interface GitHub/GitLab
-
liez le dépôt local au distant :
git remote add origin https://github.com/login/nomRepo.git
-
mettez votre code sur ce dépôt :
git push origin master (en cas de non-fast-forward : git pull origin master)
Initiez-vous aux principales commandes Git (en ligne de commande, et au travers de votre IDE) :
- créez deux branches, passez de l'une à l'autre dans votre répo local (git checkout), et faites des modifications (dont certaines en conflit entre les deux branches) dans chacune d'entre elles (e.g., rajoutez des commentaires, des tests, etc.)
- mergez successivement les deux branches avec la branche master. Le merge de la deuxème branche devrait soulever des conflits que vous devez résoudre manuellement. Résolvez le conflit en éditant le fichier en conflit et en enlevant manuellement les <<<<>>>> etc. Ces lignes représentent les lignes en conflits entre vos 2 versions (des deux branches).
- poussez ces modifications sur le repository.
Si vous êtes sur GitHub, vous pouvez aussi vous initier à la création de pull request, à l'utilisation de bots (see here), etc.
Vous pouvez annuler tous les commits précédents (git revert ...) avant de poursuivre le TP.
Vous allez dans la suite étudier les outils d'intégration continue Sonar et Jenkins/GitLab CI. La différence entre ces deux outils est simple : Sonar est un outil d'assurance qualité tandis que Jenkins est un outil de « release engineering ». Les deux sont complémentaires.
Téléchargez Sonar, décompressez-le dans /tmp, puis lancez Sonar :
sh ./bin/linux-x86-64/sonar.sh start
Au niveau du pom de votre projet, lancez la commande mvn sonar:sonar puis allez à l'adresse http://localhost:9000/. Loguez-vous avec le login admin et le mot-de-passe admin. Allez dans Quality Profiles et changez les règles de qualités utilisées puis relancez mvn sonar:sonar. Baladez-vous dans Sonar pour explorer ces différentes fonctionnalités.
Vous pourrez ensuite arrêter Sonar avec
sh ./bin/linux-x86-64/sonar.sh stop
Sur http://jenkins-ci.org/, prenez la version LTS Java Web Archive (.war) pour la mettre dans /tmp. Il faut déplacer l'endroit où la configuration Jenkins sera stockée :
export JENKINS_HOME=/tmp/.jenkins
Démarrez Jenkins :
java -jar jenkins.war --httpPort=9900
Allez dans votre navigateur : http://localhost:9900/.
Configurez Jenkins :
- Allez dans le menu « Jenkins → Manage Jenkins → Global Tool Configuration » Cliquez sur « Add JDK ». Saisissez un nom quelconque permettant d'identifier la JDK. Cochez « I agree... ». Ignorez le message d'erreur « requires Oracle account »
- Cliquez sur « Add Maven ». Saisissez un nom quelconque permettant d'identifier cette version de Maven.
- Cliquez sur « Save ». Le but de ces configurations est de pouvoir installer, si on le souhaite, plusieurs Maven ou plusieurs JDK (par exemple, certains projets peuvent nécessiter Java 6 et d'autres Java 8).
- Installez le module git pour Jenkins : « Jenkins → Manage Plugins → Available → « GIT plugin » et « Maven integration plugin » → Download now and install after restart → Restart Jenkins ». Ces plugins peuvent être déjà installés.
Ensuite créez un « job » en cliquant sur « create new job -> Maven Project ». Donnez un nom à votre projet. Définissez les sources en indiquant l'url du repository git que vous avez préalablement créé sur GitHub (i.e. https://github.com/login/nomRepo.git) et enfin définissez les goals maven pour le build (« Add build step » → « Invoke top-level Maven... ») : pour commencer clean package. Si le pom n’est pas à la racine de votre projet, cliquez sur « Advanced... » → remplissez le champ POM. Lancer un build.
Dans l'historique des builds, une icône bleue doit apparaître à la fin de la construction pour désigner la construction correcte de l'artefact (bleu car le développeur de Jenkins est Japonais et au Japon le bleu équivaut au vert chez nous, d'ailleurs un plugin Jenkins existe pour afficher des icônes vertes et non bleues...). Cliquez ensuite sur le lien sous « Module builds », les artefacts créés par Jenkins en utilisant le POM du projet sont visibles dont un jar. Ouvrez ce dernier, vous verrez que le manifest est vide. Dans les étapes suivantes vous allez compléter le POM pour obtenir un vrai jar exécutable.
Les artefacts logiciels peuvent être produits soit en utilisant maven en ligne de commande (ou au travers de l'IDE), soit en utilisant un outil d'intégration continue tel que Jenkins. Dans cette partie, nous allons étudier différents plugins maven permettant de réaliser des actions liées à la construction d'artefacts logiciels, et qu'il faudra automatiser à l'aide de Jenkins.
Pour construire des artefacts vous allez ajouter un bloc <build> dans le bloc <project> de votre POM. Générez un jar exécutable grâce au plugin maven-jar-plugin qui vous permettra de définir un manifest : http://maven.apache.org/plugins/maven-jar-plugin/ (regardez les exemples « creating an executable JAR file »).
Lancez mvn clean install et exécutez le nouveau jar généré se trouvant dans le dossier target. Commitez et pushez vos changements, relancez le build Jenkins, allez dans le « last build » et cliquez sur le « Module Builds » listé : la liste des éléments produits doit être visible et téléchargeable.
Utilisez le plugin maven-surefire-plugin pour exécuter les tests du projet lors de la commande mvn clean install, cf.: http://maven.apache.org/surefire/maven-surefire-plugin/ Commitez le POM sur GitHub (avec quelques tests) et relancez un build sur Jenkins afin d'observer les évolutions apportées.
Le plugin maven-assembly-plugin permet de créer des archives. Ce plugin est notamment très utile pour créer des archives des sources ou des fichiers exécutables, cf : http://maven.apache.org/plugins/maven-assembly-plugin/ (voir aussi: https://medium.com/@kasunpdh/using-the-maven-assembly-plugin-to-build-a-zip-distribution-5cbca2a3b052)
Étudiez et adaptez l'utilisation de ce plugin dans le projet suivant : https://github.com/latexdraw/latexdraw/blob/master/pom.xml pour l'utiliser dans votre projet afin de créer un zip des sources et un autre contenant le jar exécutable.
Commitez les modifications sur GitHub et relancez un build sur Jenkins afin d'observer les évolutions apportées.
- https://plugins.jenkins.io/sonar/
- https://plugins.jenkins.io/jacoco/
- https://plugins.jenkins.io/warnings-ng/
Attention pour Cobertura, vous avez besoin de définir le format de sortie en xml. Pour cela, il existe deux solutions:
- la première consiste à ajouter une option dans la définition du build maven: “-Dcobertura.report.format=xml”
- la deuxième consiste à modifier la configuration dans votre pom et d’ajouter l’option de configuration appropriée (voir sur la page de Cobertura plugin)
Configurer vos builds Jenkins pour qu'ils se construisent automatiquement à 1h du matin tous les jours.
Pour cette partie, votre projet devra hébergé sur GitLab (e.g., https://gitlab.istic.univ-rennes1.fr), avec la permission d'exécuter des pipelines (Settings / General / Permissions / Pipelines).
Quelques définitions préliminaires des concepts de GitLab CI :
- pipeline : un ensemble de jobs (quoi faire?), chacun à réaliser lors d'un stage (quand le faire?).
- runner : GitLab utilise des runners sur différents serveurs pour exécuter les jobs dans un pipeline. GitLab fournit des runners à utiliser, mais vous pouvez aussi utiliser vos propres serveurs comme runners.
- jobs : tâche à exécuter dans un pipeline.
- stage : un groupe de jobs connexes à exécuter dans un pipeline.
Pour installer votre runner, veillez vous référer à la documentation. Vous pouvez l'installer en local, ou via un conteneur (e.g. docker). Vous aurez besoin des informations présentes sur votre projet dans GitLab : Settings / CI/CD / Specific Runners, Setup a specific Runner manually.
- Pour l'URL copiez coller ce que vous trouverez à l’adresse précédente
- Pour le token copiez coller ce que vous trouverez à l’adresse précédente
- Pour le executor : docker ou shell en fonction
sudo curl --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64"
gitlab-runner register
gitlab-runner install
gitlab-runner start
Installer un runner docker sur votre pc en suivant les instructions : https://docs.gitlab.com/runner/install/docker.html
Lancer votre Runner à l’aide de la commande sudo docker run -d --name gitlab-runner --restart always -v /srv/gitlab-runner/config:/etc/gitlab-runner -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest
Si vous ne possédez pas docker, veuillez suivre les instructions d’installation de Docker pour votre système d’exploitation https://docs.docker.com/get-docker/. Installer ensuite le gitlab Runner , puis faire la partie register runner https://docs.gitlab.com/runner/register/index.html#docker .
Une fois l'installation de votre runner terminée, vous devriez le voir apparaître dans votre projet sur Gitlab : Settings / CI/CD / Runners.
Retourner dans Settings / CI/CD / Specific Runners, Setup a specific Runner manually, et configurer votre Runner pour accepter les jobs non tagger : Cliquer sur le crayon pour éditer votre runner, puis cocher la case Run untagged jobs. Voilà, votre runner est maintenant prêt!
Vous pouvez explorer le menu Settings / Integration et ajouter l’envoi d’un email automatique à votre adresse mail lorsque vous effectuez un push sur le repository, et lorsque votre pipeline de CI/CD obtient une erreur.
GitLab utilise le fichier ".gitlab-ci.yml" pour faire fonctionner le pipeline de l'Intégration Continue pour chaque projet. Le fichier ".gitlab-ci.yml" doit se trouver dans le répertoire racine de votre projet.
Créez un fichier ".gitlab-ci.yml" avec le contenu suivant, et explorez votre pipeline dans le menu CI/CD / Pipelines.
job1:
stage: build
script:
- echo "foo"
job2:
stage: test
script:
- echo "bar"
job3:
stage: deploy
script:
- echo "foobar"
Voici également un autre exemple. Aidez vous de cet exemple et de la documentation pour définir un pipeline similaire à celui que vous avez défini sur Jenkins dans la partie précédente.