Dans le cadre d’un projet basé sur Hapi.js (je reviendrai dessus dans un prochain article), j’ai pris un peu de temps pour améliorer la façon de déployer mes applications Node.js.

Voici ce que j’ai mis en place pour mes petits projets.

Déployer à chaque “git push”

Quoi de plus sympa qu’une application qui se déploie en production sur un simple commit/push ? Rien, on est bien d’accord ?
Je me suis donc inspiré de cet article pour mettre en place, sur mon serveur Ubuntu, un bare repository vers lequel je push lorsque je veux déployer mon application.

Contenu d'un bare repository
branches config description HEAD hooks index info objects refs

Je définis une action sur l’événement post-receive, ce qui se fait simplement en créant un fichier du même nom dans le répertoire hooks.

hooks/post-receive
#!/bin/sh
GIT_WORK_TREE=/opt/my-app git checkout -f
cd /opt/my-app
npm install

Ainsi à chaque push, un checkout est fait dans le repertoire d’installation de mon application (un git clone ayant était fait manuellement la première fois) puis un npm install pour mettre à jour mon application.

Facile, non ?

Oui. Un peu trop facile peut-être…

Mettre à jour son application d’un simple push c’est cool, mais encore faut-il s’assurer que la qualité est toujours au rendez-vous. Mon projet étant hébergé sous GitHub, j’utilise Travis CI comme outil d’intégration continu, parfaitement intégré puisqu’il déclenchera un build à chaque push sur GitHub.
Il se configure assez simplement grâce au fichier .travis.yml à la racine du projet.

.travis.yml (début)
sudo: false
language: node_js
node_js:
- 4.0
services: mongodb
addons:
apt:
sources:
- mongodb-3.0-precise
packages:
- mongodb-org-server
...
...

On déclare le langage et runtime qu’on utilise (ici Node.js v4) et les services dont on a besoin (ici MongoDB en l’installant à chaque build).

.travis.yml (fin)
...
...
ssh_known_hosts:
- my.server.fr
after_success:
- eval "$(ssh-agent -s)"
- mkdir ~/.ssh
- mv .travis-config ~/.ssh/config
- mv .deploy.pem ~/.ssh/deploy.pem
- chmod 600 ~/.ssh/deploy.pem
- git remote add deploy ssh://user@my.server.fr/opt/my-app-bare
- git push deploy master
before_install:
- openssl aes-256-cbc -K $encrypted_d3a1e9dbde14_key -iv $encrypted_d3a1e9dbde14_iv
-in .deploy.pem.enc -out .deploy.pem -d

Et vient la partie un peu plus velue.

Tout d’abord, on met en place la tuyauterie pour ssh en rajoutant notre serveur comme hôte de confiance (ssh_known_hosts) puis en décodant notre clé privée ssh (before_install) qui sera utilisé par Travis pour faire le push et qui avait été commit encryptée sur le repository.
La partie intéressante se trouve dans le after_success, juste après la tambouille pour initialiser la connexion ssh, on ajoute notre bare repository sous le nom deploy et on pousse notre branche master vers ce deploy.

Voilà, on a automatisé notre push vers notre serveur de production lorsqu’on push sur GitHub.

Bon, c’est bien joli tout ça mais en apparence on ne fait que dire à Travis de faire un push une fois le build terminé, ce qu’on savait déjà faire manuellement…

En apparence seulement, car ce qui est bien avec Travis c’est que si une cible test est présente dans notre package.json, il va l’exécuter automatiquement et la prendre en compte dans son résultat de build !
Ainsi si nos tests ne passent pas, le build est en échec et notre application n’est pas redéployée.
On peut même aller encore plus loin en fixant un seuil minimal de couverture de code de nos tests en dessous duquel ils sont considérés en échec. C’est ce que je fais avec Lab, l’outil de test de Hapi.js, avec la commande suivante qui fixe le seuil de couverture à 95% :

Lancement de tests
lab -c -t 95 -L

Sympa non ?

Oui. Mais c’est pas suffisant…

C’est pas suffisant parce que même si notre application est installée à la volée, il faut que notre serveur Node.js redémarre pour prendre en compte toutes les modifications. Pour ça, j’utilise PM2 en mode watch qui est un utilitaire qui va monitorer nos serveurs Node.js.

Ainsi lorsque des fichiers vont être mis à jour par notre push, PM2 va automatiquement redémarrer le serveur Node.js qu’on lui avait spécifié avec la commande :

pm2 start
pm2 start my-app.js

Cool !

PM2 est un outil vraiment sympa qui possède plein de fonctionnalités. Je vous recommande d’y jeter un oeil si vous ne connaissez pas.

Bonus

Bien, notre application est redéployée à chacun de nos push à condition que la qualité soit là et les modifications sont prises en compte sans qu’on ait à intervenir grâce à PM2.

Ce qui serait sympa, c’est d’avoir toutes les infos de build, de couverture de tests, versions de nos dépendances, etc, centralisées dans un seul endroit. Et les badges du README.md sont parfaits pour ça.

Les versions de dépendances npm (prod et dev) sont fournis par david-dm.org, le status de build par Travis et la couverture de code par Coverall.io qui se plugge lui-aussi sur GitHub.

Pour transmettre les informations à Coverralls, j’ai rajouté un script coverage dans package.json qui lance coveralls en sortie de rapport de Lab :

Lab et Coveralls
lab -c -L -r lcov | ./node_modules/.bin/coveralls

Je rajoute l’appel à ce script (npm run-script coverage) dans la cible after_success du fichier .travis.yml et le tour est joué.

Tous les outils utilisés ici sont gratuits pour des projets open-source.

Enjoy !