4. Gestion d'un projet avec Git, Jenkins, Sonar et Nexus

Cette partie du TP va montrer la gestion d'un projet avec le gestionnaire de sources Git, tout en intégrant au fur et à mesure les développements avec Jenkins. Jenkins fonctionnera en se servant de Maven pour “builder” le projet et lancer les tests unitaires, puis analysera la qualité du code, et pour finir déploiera sur un serveur Nexus le binaire produit.

Quelques mots sur Git avant de commencer. Git est un gestionnaire de sources décentralisé, il ne nécessite pas un serveur centrale pour que les développeurs puissent partager leur travail. Chaque développeur possède en local l'intégralité du dépôt (les fichiers sources, leurs différentes versions, les branches, les tags). Il est toutefois pratique d'avoir un serveur central comme point d'entrée, à jour avec tous les développements, permettant à d'autres développeurs ou utilisateurs de venir récupérer les sources.

La documentation officielle est disponible en français ici.

4.1 Dépôt Git du TP

Lors de ce TP nous nous appuierons sur un dépôt Git hébergé sur la plateforme SourceSup. Ce dépôt sera lié à un projet SourceSup afin de gérer les droits d'accès.

Chaque stagiaire se crée un projet sur la forge afin de pouvoir effectuer certaines commandes du TP :

  • Se rendre sur la page d'accueil de la forge
  • S'authentifier via son établissement ou un compte CRU
  • Cliquer sur l'onglet “Ma page”
  • Cliquer sur le menu “Enregistrer un projet”
  • Remplir les champs du formulaire avec les valeurs suivantes :
Nom complet du projet : form-git-XXX
Etablissement : GIP RENATER 
Description publique du projet : projet support du TP de la formation Git
Nom Unix du projet : form-git-XXX 
Code source : choisir git
  • Valider
  • Votre projet est maintenant créé
  • Le dépôt va être prochainement initialisé avec un petit délai
  • Activons les plugins qui vont nous servir :
    • Cliquer sur l'onglet “Administration” de votre projet
    • cliquer sur le sous onglet “Outils”
    • Cocher la case “intégration continue”
    • Cocher la case Nuxéo

La plateforme SourceSup permet d'interagir avec les dépôts qu'elle héberge seulement via le protocole SSH pour gérer l'identification des développeurs. Les stagiaires vont donc devoir se créer une clé SSH sur les machines locale, puis l'uploader sur la forge.

  • Dans un premier temps, il va falloir générer une clé SSH au format RSA
# ssh-keygen -t rsa
 
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Si vous configurez une passphrase, ne l'oubliez pas car elle vous sera demandée lors des interactions avec la plateforme SourceSup.
  • Après avoir entré une passphrase, les fichiers ~/.ssh/id_rsa et ~/.ssh/id_rsa.pub seront générés.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
b8:2f:11:e5:66:6f:7e:a8:30:22:ad:9b:2c:b0:89:8c 
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|        .        |
|       o         |
|      ..+        |
|      .+S.       |
|.  .  ..  o      |
|=o. o +. o .     |
|Eo + ..+  o .    |
| .=.   .o. .     |
+-----------------+

4.2 Objectif de cette partie du TP

Le but est d'être capable d'utiliser le client “ligne de commande” Git et d'interagir sur les sources du projet, comprendre le fonctionnement des branches, de leur fusion, ainsi que la gestion des conflits. Nous verrons aussi comment créer des jobs, les lancer, faire des tests unitaires avec JUnit.

4.3 Installation d'un client Git

Sur CentOS, un paquet Git existe permettant d'installer un client afin d'utiliser Git en lignes de commandes.

  • Installé le client git et wget :
# yum install git wget

4.4 Configurer son dépôt Git

Maintenant que Git est installé sur votre poste, vous pouvez personnalisé son environnement. Pour cela, il existe des fichiers regroupant la configuration nécessaire à l'utilisation du client Git. Ces fichiers sont situés :

  • /etc/gitconfig : Contient les valeurs pour tous les utilisateurs et tous les dépôts du système. Si vous passez l'option –system à git config, il lit et écrit ce fichier spécifiquement.
  • ~/.gitconfig : Spécifique à votre utilisateur. Vous pouvez forcer Git à lire et écrire ce fichier en passant l'option –global.
  • .git/config : Le fichier config dans le répertoire Git du dépôt. Précise des paramètres pour le dépôt en question. Il est possible de surcharger la valeur d'un paramètre au niveau d'un dépôt si celui-ci a déjà une valeur au niveau système. Les valeurs dans .git/config surchargent celles de /etc/gitconfig.

Il est important de configurer le pseudo ainsi que le mail de l'utilisateur car ces informations seront utilisées par Git. Remplacer USERNAME ET USERMAIL par vos valeurs dans les commandes ci dessous.

# git config --global user.name USERNAME
# git config --global user.email USERMAIL
# more ~/.gitconfig

D'autres configuration peuvent être ajoutées, comme des alias de commande Git, pour cela éditer le fichier de configuration :

# emacs ~/.gitconfig
  • ajouter par exemple ces alias à la fin du fichier
~/.gitconfig
[alias]
        ci = commit
        co = checkout
        st = status
        br = branch
  • afin de vérifier la configuration de git sur notre machine locale, exécutons la commande git config –list
# git config --list
 
user.name=USERNAME
user.email=USERMAIL
alias.ci=commit
alias.co=checkout
alias.st=status
alias.br=branch
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
remote.origin.url=git+ssh://git@git.renater.fr:2222/form-git-XXX.git
branch.master.remote=origin
branch.master.merge=refs/heads/master

4.5 Intégration Continue avec Jenkins

4.5.1 Introduction

Jenkins est un serveur d'intégration continue et de déploiement continu. Ce serveur est open-source et multi-plateforme. Jenkins va permettre de “builder” des projets, effectuer des tests sur le code, et de déployer les sources sur un serveur.

L'intégration continue est un processus dans lequel les développements sont intégrés les uns aux autres le plus tôt possible, ainsi que de générer des artéfacts et tester les sources du projet.

4.5.2 Pré requis

Pour que le processus d'intégration continue soit efficace, certains pré-requis doivent être mis en place :

  • Les sources de l'application doivent être hébergées sur un SCM (SVN/Git)
  • Les développeurs doivent régulièrement propager leurs modifications sur un dépôt
  • Des tests automatisés doivent être écris par les équipes de développement

4.5.3 Avantages

Les bénéfices qu'une équipe peut retirer de la mise en place d'un processus d'intégration continue :

  • La phase de build et de lancement des tests est automatisée et lancée régulièrement, elle n'est plus dépendante d'une intervention humaine
  • Les problèmes de builds sont découvert rapidement
  • Les tests unitaires peuvent découvrir au plus tôt certains soucis de programmation
  • La dernière version stable de l'application est connue grâce à l'historique des builds

4.5.4 Compte utilisateur et authentification

Jenkins est un serveur applicatif, qui possède sa propre gestion de comptes utilisateurs pour gérer l'authentification ainsi que les permissions sur les jobs. Sa gestion a été simplifiée afin que l'utilisateur de la forge n'ait pas besoin de créer de compte :

  • Si l'utilisateur est authentifié sur la forge via la plateforme de son établissement ou bien un compte CRU, il sera aussi authentifié sur Jenkins
  • Lorsqu'un utilisateur de SourceSup va créer un job depuis le plugin de la forge, les permissions seront automatiquement positionnées pour donner l'accès sur ce job aux membres du projet.

Le principe est le même pour les serveurs Sonar et Nexus, les utilisateurs de la forge n'ont pas besoin de créer/gérer de compte utilisateur sur ces plateformes.

4.5.5 Création d'un job

Pour créer un job sur la forge, un administrateur d'un projet doit se rendre sur l'interface de son projet sur SourceSup, puis :

  • Cliquer sur l'onglet “intégration continue”
  • Cliquer sur le sous onglet “Administration de Jenkins” (réservé aux administrateurs du projet)

  • Entrer le nom d'un job dans le champ : “form-git-XXX”
  • Cliquer sur “Créer”
  • Un nouveau “job” lié au projet SourceSup apparait sur l'écran
  • L'écran “Administration de Jenkins” permet de voir tous les jobs liés au projet, et éventuellement de le supprimer (ce qui n'est pas réversible)

  • Des boutons sont présents pour lancer une exécution du job manuellement depuis la forge, ainsi que pour supprimer le job.
  • Un clic sur le nom du job affiche directement la page de gestion du job dans Jenkins
Il est toutefois possible de créer un job directement depuis le serveur Jenkins, mais cette méthode n'est pas recommandée car
  • les permissions ne seront PAS positionnées et seul le créateur du job aura accès à celui-ci. Ce n'est donc pas pratique pour la gestion des utilisateurs.
  • Le job ne sera pas listé sur la page des jobs liés au projet SourceSup de l'utilisateur.
  • Cliquer sur le nom du job pour arriver dans Jenkins

Cette page vous présente les différents menus qu'un job offre.

  • Il est possible de lancer manuellement l'exécution d'un job
  • de visualiser toutes les opérations effectuées lors du build grâce à la console d'erreur. Très pratique pour comprendre pourquoi un build a échoué.
  • la page workspace vous permet de naviguer dans l'espace de travail du job, celui-ci contiendra les sources du projet récupérées sur le gestionnaire de source.
  • L'historique des derniers builds est affiché afin de donner une idée sur l'état du projet

4.5.6 Configuration d'un job

En utilisant le plugin de SourceSup, votre job a été créé avec quelques paramètres pré-configurés, notamment :

  • la liste des utilisateurs ayant accès au job

  • Le SCM utilisé, ainsi que l'adresse du dépôt sur SourceSup

Les sources du dépôt du projet seront téléchargées par Jenkins afin que celui-ci puisse exécuter les différentes actions qui peuvent être ajoutées dans la configuration du job, comme packager ses sources, générer des artéfacts, etc. Jenkins réserve un espace de travail pour chaque job, dans lequel il stockera les sources et effectuera les traitements. Suite à la création du job, l'espace de travail est pour l'instant vide.

4.5.7 Maven

Maven est un gestionnaire de build très répandu, permettant de gérer les dépendances de son projet, de packager les sources, de lancer les tests unitaires, etc.

Jenkins fonctionne très bien avec cet outil de build. Il est possible de rajouter des actions pré-build afin de demander à Maven de lancer des actions, comme de packager vos sources.

Nous allons l'utiliser pour notre TP. Ajoutons à notre job une action pre-build, nommée “invoquer des cibles maven de haut niveau”

Cette action requiert quelques paramètres, notamment les cibles Maven à invoquer. Indiquer dans le champ “cibles” :

clean package

L'action “clean” va nettoyer l'espace de travail du job, afin de pouvoir re-packager notre code, avec l'action “package”.

Cette action lancera la commande “mvn clean package” sur l'espace de travail du job.

4.5.8 Interaction avec SonarQube

Il existe deux méthodes pour faire appel à une analyse SonarQube depuis un job Jenkins :

  • Si Maven est utilisé dans le projet, alors il suffit d'ajouter une action post-build “SonarQube” :

  • Utiliser le Sonar Runner, en ajoutant une action “Lancer une analyse sonar autonome” pre-build à son job. Cette méthode requiert de configurer le lancement de l'analyse Sonar.

Dans ce TP, nous utiliserons le Sonar Runner pour lancer notre analyse Sonar

  • Sur la page de configuration de votre job Jenkins, ajouter une action “Build”: “Lancer une analyse Sonar autonome

  • remplissez le champ “propriétés de l'analyse” avec les valeurs suivantes :
sonar.projectKey=form:git:XXX
sonar.projectName=Projet Form-git-XXX
sonar.projectVersion=1.0
sonar.sources=src
sonar.language=java
sonar.java.binaries=target/
la valeur de la propriété projectVersion est mise par défaut et peut être changé tout au long de l'évolution du projet
  • Lancer l'exécution du job
    • Lors de l'exécution d'un job, il est intéressant de voir ce qui se passe, la console est là pour nous informer des actions qui se déroulent

  • Le lancement de l'analyse sonar est visible dans les logs de la console, tout comme la récupération des sources de l'application
  • Une fois l'exécution du job terminée, se rendre sur l'interface de votre projet sur SourceSup, au niveau du plugin d'intégration continue, cliquer sur l'onglet “Sonar

  • Votre projet devrait apparait sur la droite de l'IHM, cliquer sur son nom
  • Un tableau de bord vous est présenté, avec les différentes métriques importantes et leur valeur pour ce projet

Jenkins lancera les outils dont il connait l'existence, et transmettra les résultats, des fichiers xml, au serveur SonarQube. Celui-ci en fera une synthèse et stockera les données, afin de présenter les différents problèmes relevés à l'utilisateur.

4.5.9 SonarQube et qualité de code

SonarQube est un serveur applicatif fournissant une analyse complète de la qualité du code d'un projet, en se basant sur des outils existant (PMD, Checkstyle, Findbugs, etc.). SonarQube permet de visualiser les différents problèmes soulevés comme :

  • du code dupliqué à plusieurs endroits des sources
  • Voir si les fonctions sont expliquées par des commentaires
  • Le respect des bonnes pratiques de développement des langages utilisés
  • Les bugs évidents, tels qu'appliquer un sur un objet nul
  • Si des tests unitaires ont bien été développés
  • Si le code ne comporte pas de classes trop volumineuses qui pourraient être découpées

SonarQube regroupe toutes ses informations et en présente une vue d'ensemble, à un instant donné ou bien sur la durée grâce à l'historique des analyses précédentes. Cela permet d'avoir une idée de l'évolution de la qualité du projet, une chute brusque de la qualité du projet pourrait par exemple être due à l'arrivée d'une nouvelle personne sur le projet.

4.6 Clonage du dépôt Git

Maintenant que notre projet pourra être analyse au fur et à mesure des développements, revenons à notre dépôt Git, nous allons le cloner en local pour récupérer ses sources.

Le clonage d'un dépôt Git existant consiste à le recopier entièrement en local. Contrairement à SVN où l'utilisateur ne récupère qu'une version spécifique du code source en effectuant un “checkout”, avec Git, le développeur reçois tout le contenu du dépôt. Tous les fichiers sources, leur historique, les branches et tags. Tout cela sera stocké dans le dossier “.git” situé à la racine de la copie de travail créée lors du clonage.

  • Plaçons nous tout d'abord dans le dossier /opt/
# cd /opt
  • Il faut maintenant récupérer le dépôt Git en local. Pour cela nous utiliserons la commande “git clone”.
# git clone git+ssh://git@git.renater.fr:2222/form-git-XXX.git
  • Un dossier form-git-XXX a été créé. Ce répertoire est pour l'instant vide hormis la présence du dossier '.git'. Il est possible d'avoir un aperçu de l'arborescence de la dépôt local git :
# cd form-git-XXX
# ll .git
 
drwxr-xr-x 2 root root 4096 Mar  4 14:40 branches
-rw-r--r-- 1 root root  271 Mar  4 14:40 config
-rw-r--r-- 1 root root   73 Mar  4 14:40 description
-rw-r--r-- 1 root root   23 Mar  4 14:40 HEAD
drwxr-xr-x 2 root root 4096 Mar  4 14:40 hooks
drwxr-xr-x 2 root root 4096 Mar  4 14:40 info
drwxr-xr-x 4 root root 4096 Mar  4 14:40 objects
drwxr-xr-x 4 root root 4096 Mar  4 14:40 refs
Nous pouvons voir la présence de répertoires pour les branches, les références, les hooks, etc. Attention aux modifications effectuées directement dans ce dépôt, cela pourrait le corrompre.

Notre copie de travail est fonctionnelle, nous allons pouvoir développer dessus.

  • Récupérons maintenant un petit programme nous permettant d'illustrer le TP
  • Télécharger l'archive suivante : demo.tar.gz
  • Désarchiver la dans le dépôt de votre projet
# cd /opt/form-git-XXX/
# wget https://services.renater.fr/_media/sourcesup/formation/demo.tar.gz
# tar -xvzf demo.tar.gz
# cd demo
# mv src pom.xml .settings .project ..
# cd ..
# rm -rf demo
  • Editer le fichier pom.xml afin d'avoir un artefact-ID unique
pom.xml
<artifactId>jtetris-XXX</artifactId>

Notre démo est présente dans notre copie de travail git. Nous allons pouvoir l'utiliser pour travailler dessus. Toutefois la seule présence des fichiers sur notre copie de travail n'est pas suffisant pour que git les prennes en compte.

4.7 Ajout des fichiers à la zone d'index

Ces dossiers et fichiers font partie de notre copie de travail, il faut indiquer au client Git qu'il devra les ajouter au dépôt Git local lors du prochain commit. Pour cela nous allons utiliser la commande “git add”.

Etat des fichiers

Sur une copie de travail, chaque fichier peut avoir deux états :

  • Sous suivi de version: tous fichiers qui ont déjà fait parti d'un instantané ou qui viennent d'être indexés. Ils peuvent être inchangés, modifiés ou indexés.
  • Non suivi: tous fichiers n'ayant jamais fait parti d'un instantané et n'ayant pas été indexés
GIT ADD

La commande “git add” est multi-usage, elle permet en effet :

  • d'ajouter des fichiers au suivi de version : lorsqu'un fichier est créé dans la copie de travail, il ne fait pas par défaut parti du suivi de version. C'est à dire qu'il n'a jamais fait parti d'un instantané, et n'a pour l'instant aucune existence dans le dépôt Git local. Il sera aussi automatiquement inclu à la zone d'index.
  • d'ajouter un fichier sous suivi de version à la zone d'index afin de l'inclure au prochain instantané.

Cette commande ajoute les fichiers et dossiers en argument à la zone d'index du dépôt local. Tous les fichiers présents dans cette zone seront ensuite ajoutés dans le prochain instantané :

# git add src pom.xml

Si on ajoute un répertoire à la zone d'index, par défaut tous les fichiers situés dedans sont ajouté automatiquement également. Dans notre cas, tous les fichiers contenus dans le dossier 'src' ont été ajoutés à la zone d'index.

  • la commande “git status” permet de voir l'état des fichiers de la copie de travail :
# git status
 
On branch master
 
Initial commit
 
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
 
	new file:   pom.xml
	new file:   src/main/java/fr/cd/jtetris/Constantes.java
	new file:   src/main/java/fr/cd/jtetris/bean/Bloc.java
	new file:   src/main/java/fr/cd/jtetris/bean/Grille.java
	new file:   src/main/java/fr/cd/jtetris/bean/GrilleListener.java
 
.....
 
Untracked files:
  (use "git add <file>..." to include in what will be committed)
 
	.project
	.settings/
  • Nous avons plusieurs fichiers marqué comme “nouveau fichier”, présent dans la zone d'index, et qui seront inclus au prochain instantané. Nous pouvons voir aussi que 3 petits fichiers n'ont pas été ajouté à la zone d'index, et ont un statut “non suivi
  • La commande nous indique aussi sur quelle branche du dépôt nous travaillons. Pour l'instant nous sommes sur la branche 'master' qui correspond à la branche par défaut.
  • Afin d'avoir une vue simplifiée du résultat de la commande status, l'argument '-s' peut être utilisé :
# git status -s
 
A  pom.xml
A  src/main/java/fr/cd/jtetris/Constantes.java
A  src/main/java/fr/cd/jtetris/bean/Bloc.java
A  src/main/java/fr/cd/jtetris/bean/Grille.java
.....
 
?? .project
?? .settings/
  • Le résultat est plus sommaire et pratique à lire.
Les modifications peuvent être de différentes natures, la commande “status” du client Git permet d'avoir un aperçu rapide des fichiers ayant subit des changements. Voici les principaux status pouvant être retournés par la commande :
'' = unmodified
? = untracked
M = modified
A = added
D = deleted
R = renamed
C = copied
U = updated but unmerged

4.8 Sortir un fichier de la zone d'index

Si vous décidez qu'un fichier ne doit finalement pas faire partie du prochain instantané, il faut le faire sortir de la zone d'index. La commande git rm va répondre à notre besoin, en lui passant l'argument –cached.

Testons sur un fichier que nous venons d'ajouter :

# git rm --cached src/main/java/fr/cd/jtetris/Constantes.java
 
rm src/main/java/fr/cd/jtetris/Constantes.java

Le status du fichier a changé, il n'est plus marqué comme “ajouté à la zone d'index”. Vérifions avec la commande “git status” :

# git status 
 
A  pom.xml
A  src/main/java/fr/cd/jtetris/bean/Bloc.java
A  src/main/java/fr/cd/jtetris/bean/Grille.java
..........
 
?? .project
?? .settings/
?? src/main/java/fr/cd/jtetris/Constantes.java
  • ré-ajoutons le fichier afin de créer notre premier commit sur ce dépôt
# git add src/main/java/fr/cd/jtetris/Constantes.java

4.9 Création d'un instantané

Notre zone d'index contenant les nouveaux répertoires créés, nous allons les valider afin de les inclure dans le dépôt Git.

# git commit -m "ajout de l'arborescence du projet"
 
[master (root-commit) 438e882] ajout de l'arborescence du projet
 24 files changed, 1421 insertions(+)
 create mode 100644 pom.xml
 create mode 100644 src/main/java/fr/cd/jtetris/bean/Bloc.java
 create mode 100644 src/main/java/fr/cd/jtetris/bean/Grille.java
 create mode 100644 src/main/java/fr/cd/jtetris/bean/GrilleListener.java
 create mode 100644 src/main/java/fr/cd/jtetris/factory/BlocFactory.java
.....

Un instantané a été créé, la zone d'index a été vidée, une nouvelle révision a été construite avec ce qu'elle contenait.

Dans le cas où plusieurs fichiers ont été modifiés, et que ces fichiers ont déjà été au moins une fois fait parti d'un instantané (c'est à dire qu'ils ont le status “modifiés” et non pas “non suivi (untracked)”, il est alors possible d'utilisé un raccourci pratique. Celui-ci permet de sauter l'étape “add” et de lancer directement le commit. L'instantané sera créé avec tous les fichiers qui ont subit une modification.
# git commit -a -m "instantané direct"

4.10 Ignorer des fichiers

Il est possible que certains types de fichiers ne doivent pas être ajoutés dans le dépôt Git, comme par exemple des fichiers de configuration d'un IDE. Plutôt que de risquer d'ajouter ces fichiers à la zone d'index et ensuite au dépôt, il est possible d'indiquer dans un fichier tous les patrons de noms des fichiers à ignorer. Ce fichier se nomme .gitignore :

# touch .gitignore
# emacs .gitignore
.gitignore
.settings
.project
  • Relancer la commande git status afin de voir le changement
# git status -s
 
?? .gitignore

4.11 Voir les modifications "staged" et "unstaged"

Afin de visualiser les modifications effectuées sur votre copie de travail, vous pouvez utiliser la commande “git diff”. Celle-ci a un fonctionnement un peu particulier. En effet, utiliser sans argument, elle va renvoyée les modifications qui n'ont pas encore été indexées et laisser de côté les autres.

# emacs src/main/java/fr/cd/jtetris/util/BlocUtil.java
BlocUtil.java
package fr.cd.jtetris.util;
 
/**
 * Utilitaire de gestion des blocs
 * 
 * @author CD
 * @version 1.0.0
 * @since 1.0.0
 */
public final class BlocUtil {
 
        /**
         * Constructeur privé pour classe utilitaire
         * @since 1.0.0
         */
        private BlocUtil() {
        }
 
        /**
         * Renvoie la colonne
         * 
         * ex :
         * Pour le bloc
         * part1 = 0 0 1 0
         * part2 = 0 0 1 0
         * part3 = 0 1 1 0
         * part4 = 0 0 0 0
         * 
         * La colonne 2 a pour valeur binaire : 1 1 1 0
         * La colonne 3 a pour valeur binaire : 0 0 1 0
         * 
         * @param parts Un bloc
         * @param index Index de la colonne recherch<C3><A9>e
         * @return Valeur de la colonne
         * @since 1.0.0
         */
        public static int getColonne(int[] parts, int index, int largeur) {
                final int hauteur = parts.length;
 
                int val = 0;
 
                for (int i=0; i<hauteur; i++) {
                        int part = parts[i];
                        val += (part & 1 << (largeur - index - 1)) == 0 ? 0 : 1 << (hauteur - 1 - i); 
                }
 
                return val;
        }
 
    public static String mafonction() {
        return "Hello";
    }
 
}
# git diff
 
diff --git a/src/main/java/fr/cd/jtetris/util/BlocUtil.java b/src/main/java/fr/cd/jtetris/util/BlocUtil.java
index fa340bc..3b829ce 100644
--- a/src/main/java/fr/cd/jtetris/util/BlocUtil.java
+++ b/src/main/java/fr/cd/jtetris/util/BlocUtil.java
@@ -47,4 +47,8 @@ public final class BlocUtil {
                return val;
        }
 
+    public static String mafonction() {^M
+       return "Hello";^M
+    }^M
+^M
 }

Pour voir le différentiel entre le dernier instantané et les modifications qui font parties de la zone d'index, il faut utiliser la commande “git diff” avec l'argument “–staged”.

# touch src/main/java/fr/cd/jtetris/util/AutreUtil.java
# emacs src/main/java/fr/cd/jtetris/util/AutreUtil.java
AutreUtil.java
package fr.cd.jtetris.util;
 
/**
 * Utilitaire de gestion des blocs
 * 
 * @author CD
 * @version 1.0.0
 * @since 1.0.0
 */
public final class AutreUtil {
 
        /**
         * Constructeur privé pour classe utilitaire
         */
        private AutreUtil() {
        }
 
        /**
         * Renvoie la colonne
         * 
         * 
         * @param parts Un bloc
         * @param index Index de la colonne recherchée
         * @return Valeur de la colonne
         * @since 1.0.0
         */
        public static int getAutreColonne(int[] parts, int index, int largeur) {
                final int hauteur = parts.length;
 
                int val = 0;
 
                for (int i=0; i<hauteur; i++) {
                        int part = parts[i];
                        val += (part & 1 << (largeur - index - 1)) == 0 ? 0 : 1 << (hauteur - 1 - i); 
                }
 
                return val;
        }
 
}
Il est important de noter que “git diff” ne montre pas les changements effectués depuis le dernier commit, mais seulement les modifications qui n'ont pas encore été indexées. Si toutes les modifications ont été indexées, git diff ne renverra aucun changement
L'argument “–staged” de la commande “git diff” est synonyme de “–cached”
# git add src/main/java/fr/cd/jtetris/util/AutreUtil.java
# git diff --staged

4.12 Corriger le dernier commit

Après avoir créé un commit, si vous vous rendez compte qu'un fichier n'a pas été ajouté à la zone de suivi de version, il est possible de corriger cet oublie. Plutôt que de créer un nouveau commit, dont la seule raison d'existence serait de corriger/compléter le commit précédent, il est préférable de modifier le dernier commit afin de garder des commits cohérent.

Nous avons auparavant créer un commit, et depuis nous avons créer un fichier, qui a été ajouté à la zone d'index comme nous l'indique “git status -s

# git status -s

A  src/main/java/fr/cd/jtetris/util/AutreUtil.java
M  src/main/java/fr/cd/jtetris/util/BlocUtil.java

Corriger le commit précédent :

git commit --amend

Il est possible de préciser l'option '-m' à la commande afin de modifier le commentaire précédemment enregistré.

git commit --amend -m "commentaire"

4.13 Supprimer des fichiers

Pour supprimer un fichier qui est en suivi de version, il faut utiliser la commande “git rm”. Celle-ci va supprimer le fichier de la copie locale, et ajouter à la zone d'index la suppression du fichier dans le dépôt.

# git rm src/main/java/fr/cd/jtetris/util/AutreUtil.java
 
rm 'src/main/java/fr/cd/jtetris/util/AutreUtil.java'

La commande “git st” nous montre que cette suppression sera présente dans le prochain commit :

# git st -s
D  src/main/java/fr/cd/jtetris/util/AutreUtil.java
 M src/main/java/fr/cd/jtetris/util/BlocUtil.java
Toutefois si un fichier en suivi de version est supprimé via une commande système classique, la suppression ne sera pas incluse dans la zone d'index. Il faudra informer git de cette suppression via la commande 'git rm'

Un autre besoin pourrait être de supprimé un fichier du suivi de version, mais de le garder dans la copie locale. L'option '–cached' est utilisée dans ce cas avec la commande 'git rm' :

4.14 Pousser des modifications sur un dépôt distant

Nous pouvons maintenant pousser nos commits effectués sur le dépôt distant hébergé sur la forge. En effet jusqu'ici, toutes les modifications que nous avons fait sont restées en local. La commande push va nous permettre de les transférer au dépôt

git push origin master

git push
X11 forwarding request failed on channel 0
Counting objects: 46, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (37/37), done.
Writing objects: 100% (46/46), 20.13 KiB | 0 bytes/s, done.
Total 46 (delta 7), reused 0 (delta 0)
To git+ssh://git@git.renater.fr:2222/form-git-XXX/form-git-XXX-2.git
 * [new branch]      master -> master

Le dépôt git sur le serveur Git a été mis à jour avec les dernières modifications, les autres utilisateurs du projet pourront récupérer ces changements.

4.15 Exécution du job Jenkins

Maintenant que nous avons pousser du code sur le dépôt distant, nous pouvons lancer l'exécution de notre Job. Rendez vous sur l'interface de Jenkins, cliquer sur le nom de votre job et lancer son exécution.

Durant l'exécution, un build du projet puis une analyse du code sont réalisés. Une fois le job fini, vous pouvez vous rendre sur l'interface Sonar depuis l'interface de SourceSup, ou en cliquant ici

4.16 Création de notre branche pour les tests unitaires

Nous n'avons maintenant plus de travail en cour, nous allons pouvoir créer notre branche, et ensuite switcher sur cette nouvelle branche.

Toutefois, notre copie de travail comporte encore des modifications dans la zone d'index que nous n'avons pas commiter. Partons du principe que ces modifications ne doivent pas être commitées tout de suite, mais nous ne souhaitons pas les perdre.

Git possède une fonctionnalité répondant à ce besoin, la notion de remisage. Git va mettre de côté les modifications inclues dans la zone d'index, et vous pourrez les récupérer plus tard.

git st -s

D  src/main/java/fr/cd/jtetris/util/AutreUtil.java
 M src/main/java/fr/cd/jtetris/util/BlocUtil.java
  • Mettons de côté nos modifications
# git stash

Saved working directory and index state WIP on master: 0b98764 ajout de l'arborescence du projet
HEAD is now at 0b98764 ajout de l'arborescence du projet
git st -s

Nous n'avons maintenant plus de travail en cour, nous allons pouvoir créer notre branche, et ensuite switcher sur cette nouvelle branche.

# git branch testunit
# git co testunit

Switched to branch 'testunit'

La création d'une branche ne fait que créer un nouveau pointeur dans le dépôt git, pour changer la copie de travail avec le code de la branche, il faut récupérer la version désigner par ce pointeur.

La branche master est identique aux autres branches, ce nom de branche est très répandu et présent dans presque tous les dépôts, car la commande “git init” l'utilise pour initialiser un dépôt. Il peut toutefois être modifié.

4.17 Ajout de tests unitaires

En Java, mais aussi dans d'autres langages comme C, PHP, etc., il existe des framework permettant d'écrire facilement des tests unitaires puis de les exécuter. Dans notre exemple, nous utiliserons la bibliothèque JUnit pour développer nos tests unitaires.

Pour cela nous aurons besoin d'ajouter la dépendance JUnit à notre fichier pom.xml afin d'indiquer à Maven qu'il devra télécharger cette archive.

  • Editer le fichier pom.xml
...
<name>JTetris</name>

<dependencies>
                <dependency>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                        <version>4.11</version>
                        <scope>test</scope>
                </dependency>
        </dependencies>
...
  • créons les fichiers de tests unitaires :
# cd /opt/form-git-XXX/
# mkdir src/test
  • créer toute cette arborescence : src/test/java/fr/cd/jtetris/util
  • ainsi que ce répertoire :src/test/resources
  • Maintenant créons nos fichiers de tests :
    • src/test/java/fr/cd/jtetris/bean/BlocTest.java
package fr.cd.jtetris.bean;
 
import static org.junit.Assert.*;
 
import org.junit.Assert;
import org.junit.Test;
 
public class BlocTest {
 
        @Test
        public void testRotationDirecte() {
                boolean v = true;
 
 
                Assert.assertEquals(true, v);
        }
 
}
 
/code>
 
    * src/test/java/fr/cd/jtetris/util/LigneUtilTest.java
 
<code LigneUtilTest.java>
package fr.cd.jtetris.util;
 
import static org.junit.Assert.*;
import org.junit.Test;
 
public class LigneUtilTest {
 
        /**
         * ligne compl<C3><A8>te : 1 1 1 1 1 1 1 1 1 1 1 1 1 --> 8191
         */
        @Test
        public void testIsComplete() {
                int ligne = 0;
                boolean complete = LigneUtil.isComplete(ligne);
                assertEquals(false, complete);
                ligne = 8191;
                complete = LigneUtil.isComplete(ligne);
                assertEquals(true, complete);
        }
 
}
 
/code>
 
    * src/test/java/fr/cd/jtetris/util/BlocUtilTest.java
 
<code BlocUtilTest.java>
package fr.cd.jtetris.util;
 
import static org.junit.Assert.*;
 
import org.junit.Test;
 
public class BlocUtilTest {
 
        /**
         *      Pour le bloc
         *
         *  X .
         *  X .
         *  X X
         * 
         * La colonne 1 a pour valeur binaire : 1 1 1
         * La colonne 0 a pour valeur binaire : 0 0 1
         */
        @Test
        public void testGetColonne() {
                int[] parts = {1,1,3};
                int result = BlocUtil.getColonne(parts, 1, 2);
                assertEquals(7, result);
                result = BlocUtil.getColonne(parts, 0, 2);
                assertEquals(1, result);
        }
}
/code>
 
 
incluons le à notre branche
 
<code>
# git add src/test
# git commit -m "ajout des test"
  • Revenons sur notre branche 'master'
# git co master
# git branch --no-merged
  • La dernière commande nous montre les branches qui n'ont pas été fusionnées avec la branche courante
  • Avant de procéder à la fusion, faisons un commit sur notre branche 'master'
  • éditer le fichier src/main/java/fr/cd/jtetris/util/LigneUtil.java et rajouter le code suivant :
package fr.cd.jtetris.util;
 
import fr.cd.jtetris.Constantes;
 
/**                                                                                                                                                                                      
 * Utilitaire de traitement des lignes                                                                                                                                                   
 * @author CD                                                                                                                                                                            
 * @version 1.0.0                                                                                                                                                                        
 * @since 1.0.0                                                                                                                                                                          
 */
public final class LigneUtil {
 
    /**                                                                                                                                                                                  
     * Constructeur privé pour classe utilitaire                                                                                                                                        
     */
    private LigneUtil() {
    }
 
    /**                                                                                                                                                                                  
     * Indique si une ligne est complète                                                                                                                                                
     * @param ligne La ligne à tester                                                                                                                                                   
     * @return true si la ligne est complète, false sinon                                                                                                                               
     */
    public static boolean isComplete(int ligne) {
 
        return ligne == ((1 << Constantes.NB_COLONNES) - 1);
    }
 
    public static boolean isAutreFonction() {
        return true;;
    }
 
}
  • Ajoutons cette modification à la zone d'index, puis commitons la
# git add src/main/java/fr/cd/jtetris/util/LigneUtil.java 
# git commit -m "ajout fonction"
  • Fusionnons maintenant la branche gui avec la branche 'master'
# git merge gui -m "fusion de branche"
  • Notre branche master contient maintenant le code de la branche 'test-unit' ainsi que ses propres modifications
  • Nous pouvons maintenant récupérer les fichiers placés en remisement
# git stash apply

Removing src/main/java/fr/cd/jtetris/util/AutreUtil.java
On branch master
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	deleted:    src/main/java/fr/cd/jtetris/util/AutreUtil.java
	modified:   src/main/java/fr/cd/jtetris/util/BlocUtil.java
  • Lancer ensuite l'exécution du job sur Jenkins
  • L'exécution des tests devrait apparaitre dans les logs de la console
  • Rendez vous sur l'interface de Sonar, une métrique indiquant les tests unitaires doit être présente sur le tableau de bord

4.18 Métriques

Le tableau de bord de l'analyse vous fournit plusieurs informations sur la qualité du code source, notamment les problèmes relevés

Des problèmes existent donc sur notre projet. Cliquer sur le lien des problèmes Majeur, vous pourrez voir la liste des défaillances relevées par l'analyse, avec une explication du problème, une suggestion pour la résoudre, et dans quel fichier elles ont été relevées.

Plusieurs anomalie facile à corriger se trouve dans le fichier : “src/main/java/fr/cd/jtetris/bean/Grille.java”, il s'agit d'accolades qui n'ont pas été écrites. Ce qui n'est pas faux en Java mais pas forcément recommandé par les outils d'analyses de code.

Vous pouvez afficher le fichier entier sur Sonar, afin de voir précisément sur quelles lignes les problèmes apparaissent. Nous allons corriger quelques anomalies.

  • Editer le fichier “src/main/java/fr/cd/jtetris/bean/Grille.java”, rajouter les accolades demandées par Sonar.
  • Ensuite ajouter le fichier à la zone d'index du dépôt git, puis commiter le.
  • pousser votre modification sur le dépôt distant.
  • Jenkins devrait être notifier qu'une modification à eu lieue, et relancera un build.
  • A la fin du build, retourner sur Sonar pour voir si les résultats ont changé.

4.19 Interaction avec Nexus

  • Retourner sur la page de configuration de votre job
  • supprimer l'analyse Sonar ajouté précédemment
  • Ajouter une cible Maven à la suite des deux déjà déclarer
    • le champ cible contiendra : clean package deploy
  • Sauvegarder le job

Sur votre copie de travail git, éditer maintenant le fichier pom.xml

# emacs pom.xml
  • Ajouter le code suivant :
<distributionManagement>
    <repository>
      <id>sourcesup</id>
      <url>http://sourcesup.renater.fr/nexus/content/repositories/form-git-XXX-releases</url>
    </repository>
    <snapshotRepository>
      <id>sourcesup</id>
      <url>http://sourcesup.renater.fr/nexus/content/repositories/form-git-XXX-snapshots</url>
    </snapshotRepository>
  </distributionManagement>

Cette configuration indique à Maven l'URL des dépôts Nexus sur lesquels déployer les artefacts générés par un build du projet.

  • Ajouter cette modification à la zone d'index de git et créer un nouveau commit
# git add pom.xml
# git commit -m "ajout déploiement sur nexus"
  • pousser ce changement sur le dépôt distant de la forge
# git push origin master
  • Lancer l'exécution du job sous Jenkins
  • Lorsque elle est terminée, rendez vous sur l'interface de votre projet SourceSup, au niveau du plugin d'intégration continue, cliquer sur l'onglet 'Nexus'

  • Cliquer sur le menu “repository” à gauche de l'IHM
  • Chercher le dépôt de votre projet, et cliquer dessus

  • Dérouler l'arborescence pour voir les artefacts déployés

4.20 Création d'un espace sur Nuxéo

Nuxéo est un serveur de gestion électronique de document.

  • Activer le plugin sur votre projet SourceSup
  • sur l'onglet “administration” Cliquer sur le bouton permettant de créer un workspace sur Nuxéo
  • Retourner maintenant sur l'accueil du plugin Nuxéo
  • Un formulaire permet d'uploader des documents
  • Télécharger sur votre bureau le fichier pdf suivant

cdp_coursgestionexigences.pdf

  • Uploader le sur à l'aide du formulaire sur Nuxéo
  • Une fois uploadé, ce document pourra être référencé par exemple au niveau d'un bogue sous Mantis.
  • Activer le plugin Mantis pour votre projet
  • Créer un nouveau bogue, vous verrez plusieurs listes déroulantes permettant de lier un document hébergé sur Nuxéo avec le bogue.
  • Choisissez le document dans la liste déroulante et valider.