Un push-to-deploy simple et gratuit pour votre frontend


Dans cet article, nous verrons ce qui a été mis en place pour le site http://www.bdx.io (une conférence pour les développeurs qui se tiendra sur Bordeaux en octobre ;-)) afin d’automatiser le déploiement d’un site statique, hébergé chez github, optimisé pour le web (pré-processing css, minification et concaténation, optimisation des images etc…) et ce, via un  simple commit Git.

Le principe de fonctionnement s’apparente un peu au fonctionnement d’heroku, à ceci près :

  • Qu’il est déclenché automatiquement lors de l’apparition de nouveaux commits sur une branche de votre choix
  • Qu’un processus d’optimisation des ressources statiques est appliqué en amont du déploiement

A travers cet article nous parlerons :

  • De Github pages pour l’hébergement des ressources statiques du site
  • De Yeoman qui nous servira à générer une ossature « clef en main » pour un site web optimisé
  • D’un petit script sh maison permettant de pusher notre site web optimisé sur une branche de notre repository git
  • D’un build Jenkins déclenché chez Cloudbees qui exécute un script Grunt pour optimiser votre site Web
  • De hooks Git pour déclencher automatiquement ce job Jenkins, sans avoir à poller votre repository Git

L’hébergement : chez Github avec Github Pages

Github offre un service d’hébergement « Github pages« , gratuit, qui vous permet d’héberger votre site web très facilement.

Pour cela, créez simplement un repository git public nommé <username>.github.io. Le contenu (index.html) de ce repository sera alors accessible et servi via l’url éponyme. Dans mon cas, j’ai simplement eu à créer le repository github bdxio.github.io qui pointe donc sur http://bdxio.github.io.

Détail  important pour la suite : c’est systématiquement la branche master qui sera déployée sur les github pages et ce, même si la « branche par défaut » de votre repository n’est pas la branche master.
Dans mon cas, c’est quelque chose de plutôt pratique (nous verrons pourquoi dans une prochaine section).

Un générateur Yeoman pour une ossature d’application web optimisée

Yeoman est un projet visant à regrouper des générateurs pour différentes typologies projet (angular, backbone, etc…) généralement dans le monde du frontend.

Dans mon cas, j’avais simplement besoin :

  • D’une ossature de projet me permettant d’intégrer des librairies js via bower (un provisionneur de librairies web js/css)
  • D’un build Grunt me permettant de compiler à la volée des scripts SASS, de manière à faire des CSS un peu avancées (mixins, variables, nesting de sélecteurs)
  • D’un build Grunt me permettant de générer une arborescence « optimale » pour le site en production : minification et concaténation des ressources js/css, optimisation des images, etc..

Le générateur webapp, que j’avais utilisé lors de mon live coding à Devoxx France l’année dernière, était une valeur sûre pour cela :

yo webapp

Une fois l’arborescence générée, il est nécessaire de provisionner les dépendances node (utilisées par le build Grunt) et les dépendances bower (utilisées par notre site web) :

npm install && ./node_modules/.bin/bower install

Note : j’aurais pu installer bower comme un paquet npm global (npm install -g bower grunt). Cependant, pour garantir une reproductibilité de mon build, je préfère déclarer cette dépendance (tout comme ma dépendance grunt) dans le package.json de mon projet. De cette manière, je suis capable de définir la version de mon tooling, histoire d’éviter les mauvaises surprises si de nouvelles versions non backward compatibles venaient à sortir pour ces outils.
Il est alors possible de référencer les exécutables via le répertoire ./node_modules/.bin/.
L’inconvénient derrière cela est que pour chacun de mes projets, je duplique potentiellement mes exécutables. Mais je préfère cela à vivre dans la peur de monter de version globale sur ces outils.

Suite au provisioning des dépendances, il m’est maintenant possible :

  • De démarrer un serveur web avec du live reload, qui va compiler a la volée mes fichiers SASS (très utile en développement) : ./node_modules/.bin/grunt serve
  • D’exécuter la tâche ./node_modules/.bin/grunt build me permettant de générer une arborescence « optimisée » dans le répertoire dist de mon projet.
  • De servir le contenu « optimisé » : ./node_modules/.bin/grunt serve:dist

Un script maison pour pusher le site optimisé sur une branche git

Avoir un site optimisé en local, c’est bien. Pouvoir le déployer sur les Github Pages, ce serait mieux !

Pour ce faire, j’ai réalisé un petit script bash permettant de générer l’arborescence dist, puis de la pusher sur la branche master de Git utilisée pour mon site bdxio.github.io.

Cela signifie que j’aura à minima 2 branches Git :

  • Une branche de dev qui contient les ressources utiles pour le développement du site
    C’est cette branche que je considèrerai comme la « branche par défaut » github de mon repository : c’est celle sur laquelle arriveront les visiteurs du repository git.
  • Une branche master qui correspond aux ressources brutes et optimisées qui seront servies par les Github Pages

En réalité, il y aura également une 3ème branche, nommée prod, me permettant de travailler sur ma branche de dev sans nécessairement déployer tout ce que je push sur cette dernière : je n’aurai qu’à faire des merges dev => prod lorsque je souhaiterai réellement déployer mes développements.

Le script bash est sommaire :

  • Il garantit que les dépendances npm / bower ont bien été provisionnées sur la machine (pour la première fois où le script est exécuté)
  • Il nettoie le répertoire dist/ afin d’être sûr qu’il n’y aura pas de reliquats d’un précédent build dans ce répertoire
  • Il clone la branche master des github pages dans le répertoire dist/ (qui devra, au préalable, avoir été rajouté au .gitignore)
  • Il génère le site web optimisé dans le répertoire dist, via un grunt build
  • Il met à jour les fichiers dans le repository git local, puis les push sur la branche master distante.

Le voici à l’heure où j’écris ces lignes :

#!/bin/bash -x

# Ensuring npm & bower deps are installed
npm install
./node_modules/.bin/bower install

# Cleaning dist directory
rm -Rf dist/
mkdir dist

# Cloning master branch into dist directory
git clone -b master git@github.com:bdxio/bdxio.github.io.git dist/

# Generating dist/
./node_modules/.bin/grunt build

# Checking in dist/
cd dist
# Adding files, removing missing files (including files with spaces as well)
git add .
if [ $(git ls-files --deleted | wc -l) -ne 0 ]; then git ls-files --deleted | sed -e 's/^/"/g' -e 's/$/"/g' | xargs git rm; fi;
git commit -m "Auto-deploy"

# Pushing to master branch, which is sync-ed with www.bdx.io
git push origin master
cd ..

Suite à cela, lorsqu’on lance le script bash, on déploie un site optimisé sur les github pages de bdx.io 🙂

Déporter le build dans le cloud

Lorsque j’ai développé le site bdx.io, j’avais une problématique de bande passante limitée. Etant dans une maison de vacances sans wifi, la seule connexion dont je disposais était une connexion edge pas très fiable, qui plantait régulièrement.
Difficile, dans ces conditions, de faire fréquemment re-provisionner les dépendances npm/bower, ainsi que les différents clone/push du master.
D’autre part, il est toujours plus rassurant de déporter le build sur une autre machine que la sienne, histoire de garantir qu’on ne déploie que ce qui est commité.

J’ai tout d’abord cherché à faire cela sur drone.io qui me paraissait de prime abord plus léger qu’un build Jenkins. Le problème sur ce service est que lorsqu’on fait appel au filesystem, on a des soucis. Typiquement, je n’ai jamais réussi à faire fonctionner un build Grunt dessus … dommage !
J’ai donc changé mon fusil d’épaule pour créer un build Jenkins chez Cloudbees (gratuit).

Première chose à faire : installer les plugins nécessaires au bon fonctionnement de mon build :

  • Tout d’abord le plugin NodeJS que je connais bien, qui me permet d’installer automatiquement un nodejs sur le slave qui va exécuter mon build. Il va également me permettre d’identifier la version de node que je vais utiliser, et cloisonner éventuellement les paquets npm globaux dont j’aurai besoin pour travailler.
    Une fois installé, il est nécessaire de paramétrer l’installation dans la configuration globale de jenkins :
    Jenkins NodeJS - Global config
  • Ensuite, juste pour enjoliver un peu les choses, le Ansi Color plugin qui permettra d’afficher les couleurs de notre build grunt dans la console jenkins

 

Vient ensuite la création du job Jenkins :

  • Tout d’abord la déclaration de la clef publique SSH de mon user Cloudbees dans les clefs autorisées sur mon repository Github (le job jenkins aura besoin de pouvoir pusher des choses sur le master)
    SSH - JenkinsSSH - Github
  • Ensuite, demander à cloner le repository git, particulièrement la branche prod (cf section précédente)
    Jenkins - SCM
  • Faites en sorte de systématiquement supprimer le workspace avant le clone (pour des raison que je n’ai pas compris, sans ça, les déploiements ne se faisaient pas toujours correctement…).
    L’option est disponible dans la configuration avancée du repository Git.
    Jenkins - Clean workspace
  • Rajoutez votre version de Node au PATH, activez le plugin ANSI Color, et validez que Ruby et Compass sont bien installés sur la machine.
    Sur ce dernier point, je dis un grand merci à Cyril Lakech qui m’a bien simplifié la vie avec son script d’installation de Ruby + Compass.
    Une fois fait, finissez par exécuter le script bash évoqué dans la section précédente pour pousser votre site sur la branche master.
    Jenkins - Core config

Vous devriez alors être en mesure de déclencher le build manuellement, ce qui devrait déployer votre application optimisée sur la branche master de votre repository Git.

 

Notifier automatiquement Jenkins d’un nouveau commit

Dans la section précédente, nous avons vu comment publier sur master via notre job Jenkins.
Nous pourrions poller le repository Git toutes les N minutes pour vérifier qu’il n’y a pas eu de nouveau commits, mais il y a plus optimal (tant pour le serveur Git, que pour la latence entre le commit et le déclenchement du build, aka le déploiement de vos modifications sur votre site web) : déclarer un hook Git qui va déclencher le build Jenkins adéquat.

Pour cela, allez dans la configuration de votre repository sur Github, dans la section « Web hooks & Services », et rajouter un service de type « Jenkins Git Plugin » avec votre URL personnelle :
Github - Jenkins hook

Puis rajoutez un polling (pas forcément fréquent, on s’en fiche) comme préconisé dans l’aide en ligne :
Jenkins - polling

A chaque nouveau commit sur la branche prod, le build sera immédiatement déclenché, générant votre site web optimisé et le déployant sur la branche master de votre repository github, c’est à dire sur vos Github Pages.

Et voilà, désormais, vous pouvez travailler sur votre branche de dev, merger sur votre branche de prod lorsque vous souhaitez déployer votre site : le build jenkins vous construira la branche master qui sera utilisée par les Github Pages.

Une chaîne de Continuous Deployment pour pas cher ! 🙂

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :