Tester localement le build d'un projet à la façon de Travis CI
Publié le
Pour les geeks, vous pouvez zapper l'introduction et aller directement ici.
Intégration continue, quésaco ?
Récemment, j'ai voulu utiliser un outil d'intégration continue bien connu (Travis-CI pour ne pas le citer). Pour ceux qui ne connaissent pas le principe, c'est assez simple : les développements sont poussés sur une plateforme qui va exécuter toutes sortes de tests pré-configurés par l'administrateur du projet (format, PHPUnit, Jasmine, …) sur un ou plusieurs environnements (OS, SGBD, version applicative, …). Si tous les tests sont passants, le commit (les modifications) est validé, sinon son auteur est amené à revoir sa copie (correction des bugs, de la syntaxe ou des règles d'ingénierie).
Ce service a deux avantages immédiats :
- il permet de valider que le produit livré est fonctionnel dans plusieurs configurations systèmes (cela nécessite évidement d'avoir prévu des tests automatisés),
- dans le cas d'un projet Open source, où tout un chacun peut apporter sa contribution, il permet de cadrer les propositions (identification de potentiels nouveaux bugs ou d'une rupture dans le formalisme du code).
Et c'est gratuit.
L'expérience
Comme je le disais au tout début, j'ai voulu utiliser ce service (dans le cadre de ce projet de lecteur musical auto-hébergé en PHP / JavaScript, auquel tu peux contribuer si le cœur t'en dit). Résultat : mes tests n'étaient pas valides dans un environnement utilisant une base de données SQLite.
Surprise et déception passées, l'analyse du problème se montre laborieuse : le principe du build, c'est d'exécuter des commandes pré-établies (initialisation du système, installation du projet et de ses dépendances et enfin déroulement des tests prévus) puis d'afficher le résultat final en indiquant si c'est passant ou non. A peu près ce que je vous racontais dans l'introduction.
Mais quand ce n'est pas passant, bin tu peux pas débuguer : C'EST KO ET PIS C'EST TOUT ! Si les tests ne sont pas conçus pour expliciter la cause du ko, le build n'en dira pas plus. Or sur mon poste local, le code passait l'ensemble des tests avec joie et félicité y compris lors de l'utilisation d'une base de données SQLite.
[ … suspens … ]
Convaincu que c'était un problème de configuration de Travis, les commits se succèdent pour essayer différentes pistes de correction portant sur le fichier de configuration (création du répertoire contenant le fichier SQLite, modification des droits, …) : tous en échec (paie ton historique de versionning…).
Google, StackOverflow (gros vent), retour à Google qui me glisse une idée : reproduire la configuration de l'environnement Travis en local sur mon poste et débuguer ce merdouilli.
Travis en local (l'objet du post, enfin)
Pour pouvoir faire du pas à pas et identifier un bug qui ne remonte que sur Travis, on peut installer un conteneur via Docker avec la même image système que celle utilisée par la plateforme d'intégration continue. Tout ceci est parfaitement documenté sur le site de Travis CI, mais voici une version courte et commentée (l'environnement local est Ubuntu 16.04 LTS, environnement Travis PHP).
On commence par installer une version récente de Docker, pour cela on ajoute le dépôt officiel et sa clé :
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
sudo nano /etc/apt/sources.list.d/docker.list
On y ajoute la ligne suivante :
deb https://apt.dockerproject.org/repo ubuntu-xenial main
Mise à jour des sources et installation et test de bon fonctionnement :
sudo apt-get update
sudo apt-get install docker-engine
sudo service docker start
sudo docker run hello-world
Ensuite, on télécharge l'image utilisée par Travis (qui dépend du langage du projet), dans le cas d'un projet PHP :
sudo docker run -it quay.io/travisci/travis-php /bin/bash
Une fois l'image chargée (ça peut être assez long, pas de panique), on bascule sur l'utilisateur Travis via un :
su - travis
Enfin on applique les commandes qui sont listées dans le journal de build de Travis (qui est une interprétation shell du fichier de configuration travis.yml), ce qui devrait vous donner quelque chose de ce type là :
# récupération du code du projet depuis Github (vous remplacerez évidement "vous" et "votre_projet")
git clone --depth=50 --branch=master https://github.com/vous/votre_projet.git vous/votre_projet
# positionnement dans le répertoire du projet
cd vous/votre_projet
# application du commit souhaité
git checkout -qf 9d947c3746e357a8a690c9f29607b543cc3a074a
# valorisation des variables d'environnement (ici utilisation de SQLite et version de PHP)
export DB=sqlite
phpenv global 5.6 2>/dev/null
phpenv global 5.6
# mise à jour
composer self-update
php --version
composer --version
# lancement des tests
(cd tests ; phpunit -c phpunit.xml)
Et voila ! On retrouve normalement le même résultat que sur la plateforme Travis, sauf qu'ici, vous pouvez modifier le code, ajouter des logs, forcer des valeurs, … bref faire du debug sans pourrir l'historique des commits.
Épilogue “Et ton problème à toi, c'était quoi ?”
Merci de l'intérêt que tu portes à tout ça (je te tutoie car tu es probablement le seul à avoir lu ce post jusqu'au bout). Et bien il se trouve que je suis un pauv' noob qui ne savait pas que SQLite ne permet pas (dans sa configuration par défaut) d'appliquer une limite (clause LIMIT) sur une requête de modification (ALTER) ou suppression (DELETE). Sur mon poste, SQLite était étonnement compilé avec l'option le permettant.
Au passage, merci les tests automatiques d'avoir pointé du doigt ce bug qui est désormais corrigé.
PS : tu auras quand-même appris que kezako s'écrit en vrai “quésaco”, et ça, c'est chouette.