Intégration (presque) continue avec Jenkins et SonarQube

Qualité du code, analyse statique, tests unitaires, …

Après avoir réussi à installer un système d’exploitation non sans mal sur le serveur récupéré au HAUM, il est temps de s’en servir pour quelque-chose d’utile.

J’avais pour idée quand j’ai récupéré la bête de faire de l’intégration continue à l’aide de Jenkins, qui permet de programmer des constructions/tests de programmes, et SonarQube, un analyseur statique de code pour de nombreux langages qui prend également en charge les résultats de tests unitaires.

Installation sous Debian Jessie

Avant toute chose, nous allons installer ces programmes sur le serveur.

Pour ce qui est de Jenkins, ce sera relativement simple étant donné que l’équipe de Jenkins propose des paquets Debian à jour en utilisant leur dépôt dédié.

Par contre pour SonarQube il va falloir passer à une installation à la main en suivant le guide d’installation présent dans la documentation. J’ai tout mis dans /opt pour ne pas mélanger ça avec le système encore relativement propre.

Notez que SonarQube a besoin d’un RDBMS pour fonctionner, j’ai pour ma part choisi mon fidèle PostgreSQL que j’utilise partout.

Activation des services

Une fois les logiciels installés (et après avoir vérifié leur bon fonctionnement), il faut les activer au démarrage pour ne pas avoir à les appeler à chaque boot…

Le « intégration presque continue » dans le titre a du vous intriguer. En effet, étant donné que le serveur est plutôt bruyant et doit aussi sans doute consommer sa dose d’électricité, je ne le laisse pas tourner en continu mais l’allume de temps à autre pour qu’il fasse son travail.

C’est donc pour cela qu’il faut que tous les services démarrent au boot et se mettent à travailler automatiquement.

Jenkins propose un service systemd qu’il nous suffit donc d’activer à l’aide de systemctl enable jenkins.

Pour SonarQube aucun fichier service n’est fourni, mais il a été facile d’en trouver un sur le web et de l’adapter pour qu’il soit compatible avec la version que j’utilise :

[Unit]
Description=SonarQube 5
After=network.target network-online.target
Wants=network-online.target

[Service]
ExecStart=/opt/sonarqube-5.3/bin/linux-x86-32/sonar.sh start
ExecStop=/opt/sonarqube-5.3/bin/linux-x86-32/sonar.sh stop
ExecReload=/opt/sonarqube-5.3/bin/linux-x86-32/sonar.sh restart
PIDFile=/opt/sonarqube-5.3/bin/linux-x86-32/./SonarQube.pid
Type=forking
User=sonarqube

[Install]
WantedBy=multi-user.target

Il suffit ensuite de placer ce fichier sonarqube.service dans /etc/systemd/system et de l’activer avec systemctl enable sonarqube.

Pfiou, on va enfin pouvoir commencer à s’amuser maintenant que tout est en place et fonctionne correctement.

Configuration de Jenkins

Tout d’abord, il va falloir installer plusieurs plugins essentiels :

  • Git, afin de pouvoir récupérer le code et de travailler dessus
  • SonarQube, pour envoyer les résultats de build au serveur Sonar

L’extension git ne nécessite pas de configuration supplémentaire. Pour l’extension Sonar il va falloir lui préciser à quelle adresse se trouve le serveur afin de pouvoir faire le lien entre les builds et les résultats sur Sonar :

On spécifie l’adresse du serveur SonarQube dans le champ  correspondant

Configuration de Jenkins pour pouvoir parler avec SonarQube

Bien que Jenkins et Sonar tournent sur le même serveur, je donne à Jenkins l’adresse du serveur sur le réseau local afin qu’un clic sur l’icône de Sonar me redirige vers la page correspondante quand je navigue sur le serveur avec une autre machine sur le réseau local.

Création d’une tâche Jenkins

Maintenant que Jenkins est configuré on va pouvoir lui demander de cloner automatiquement notre dépôt Git et de lancer les tests unitaires qu’il contient, afin de vérifier que tous les tests passent et de voir le code que l’on a couvert avec les tests.

On indique l’URL HTTPS pour cloner le dépôt Git depuis Github

On va ici cloner RedditHAUM puis poll toutes les minutes.

Une fois la méthode de rapatriement de code choisie, on peut donc écrire une suite de commandes bash qui va installer les dépendances du projet et lancer les tests unitaires ainsi que vérifier la couverture de code.

Suite de commandes afin d’installer les dépendances et appel à nosetests

Suite de commandes afin d’installer les dépendances et appel à nosetests

On remarque que l’on appelle le runner de SonarQube après avoir construit notre projet afin qu’il récupère les résultats des tests, exécute une analyse statique du code et envoie les résultats au serveur SonarQube.

C’est tout pour la partie Jenkins, le service va ensuite se charger de relancer les tests en vérifiant toutes les minutes si de nouveaux commits ont été pushés sur la branche master du dépôt distant.

Consultation des résultats avec SonarQube

Sonar va nous permettre de consulter les résultats des tests, mais aussi et surtout de faire de l’analyse statique de code et de nous signaler les points à améliorer en fonction de différents critères (performance, conventions de nommage, …)

Je n’ai pas de capture d’écran de Sonar sur le projet RedditHAUM (qui a reçu ses premiers tests unitaires récemment) donc je vous propose une suite de captures d’écran de l’interface de SonarQube pour un autre projet open-source que je développe.

Technical debt, coverage, duplications, structure, …

Page d’accueil du projet SonarQube

Code coverage, unit tests

Détails sur la couverture de code du projet

Nombre de lignes de codes, de classes, de méthodes

Structure du projet et étude de complexité

Problèmes bloquants, majeurs, mineurs, etc.

Liste des problèmes rencontrés lors de l’analyse, par degré de gravité

Tout fonctionne plutôt bien…

Les deux services communiquent bien entre eux et je suis satisfait du résultat.

On pourrait imaginer laisser tourner ça en continu afin de s’assurer de la bonne qualité des logiciels que l’on produit et de pouvoir rectifier le tir le plus rapidement possible sur de petites anomalies introduites.

Le principe de dette technique est intéressant et permet donc d’estimer le temps de travail nécessaire à améliorer le programme pour qu’il soit d’une qualité irréprochable (cependant Sonar ne détecte pas les problèmes de conception, donc ça n’empêche pas de réfléchir un peu au système que l’on développe).

…à condition d’avoir une bonne bécane

J’avais essayé de faire tourner ces services sur mon serveur ARM et Jenkins était beaucoup trop lent (environ 10s par chargement de page). Quand à Sonar, aucun build ARM n’est proposé à l’heure actuelle mais je suppose qu’il doit aussi bien consommer en temps de calcul et en mémoire (on notera que ces deux logiciels sont écrits en Java, ce qui peut expliquer au moins la consommation de mémoire).

Sur le serveur x86 ibeme, le Pentium D arrive avec ses 3 GHz à suivre la cadence mais la barette de 1 Go de RAM fait moins la fière avec environ 700 Mo utilisés sans qu’aucun build ne tourne.

Enfin, vu que j’ai remplacé le disque SAS défaillant par un SATA récupéré dans une tour, le serveur est très lent à démarrer à cause de l’I/O disque et il faut attendre plusieurs minutes avant qu’il soit opérationnel et commence à travailler en arrière plan.

Conclusion

Voici donc ce que j’ai réussi à faire tourner sur ce serveur et le résultat est honorable.

Étant donné que je travaille le plus souvent sur des projets en Python, je n’ai pas l’utilité d’utiliser Jenkins pour autre chose que faire des tests sur mes logiciels mais il peut être également être utilisé afin de construire/compiler en continu une application écrite en C/C++/Rust/watever afin de proposer des builds frais pour des utilisateurs (pourquoi pas sous forme de PKGBUILD pour AUR).

Un bon point pour Jenkins qui est très maléable et permet de créer des tâches pour tout et n’importe quoi grâce à l’utilisation de script shell, même si le tout semble un peu lourd pour l’utilisation que j’en ai eu jusqu’à présent (Buildbot semble être une alternative un peu moins clicodrome et en plus c’est écrit en Python).

Une question ou remarque ? N'hésitez pas à me contacter en envoyant un mail à microjoe, suivi d'un arobase, puis encore microjoe et enfin un « point org ».