remote safe push

Dans les principes de l’agilité, l’un des ingrédients essentiels est d’avoir une boucle de retour rapide. En tant que développeur, vous êtes donc profondément convaincu par l’intégration continue. Seulement, vous en avez assez de cette situation :

À tout moment de la journée peut retentir cette fameuse phrase: « Mais qui a (encore) cassé le build ??« 
Il y a alors plusieurs stratégies d’équipe :

  • ce n’est pas grave, cela arrive tout le temps. Cela va bien finir par passer au vert !
  • celui qui casse paie et corrige le problème
  • l’ensemble de l’équipe se sent concerné, une personne se dévoue pour enquêter et corriger le problème.

La règle d’or : aucune modification ne peut être apportée sur le code tant que l’intégrité du projet n’est pas restaurée.
Une fois le problème résolu, au Scrum Master de créer un système à base de croissants à offrir, de cagnottes à remplir, d’avatars à afficher ou encore de lance-missile USB pour que cela se produise le moins souvent possible.
Une carte Xebia Essentials illustre cette démarche : NO BLAME BUT NO MERCY.

Vient alors l’idée de s’assurer que le commit que je me prépare à envoyer correspond un minimum aux critères de validation de l’intégration continue. Le processus de validation doit être automatisé et en mode bac-à-sable (son exécution ne doit perturber personne).

Personal Build dans Teamcity

Le serveur d’intégration continue Teamcity, de Jetbrains, propose ce service sous forme de personnal build. On peut les déclencher à la demande. Ces jobs ne sont pas visibles aux autres utilisateurs et des plugins permettent d’être averti dans l’IDE du résultat de build.
Cela présente l’avantage de déporter la charge de compilation et tests à des serveurs d’intégration. Vous économisez ainsi de la mémoire et de la CPU sur votre poste de développement.
Si vous utilisez un gestionnaire de source non-distribué (comme SVN), vous pouvez alors déclenchez des Personal Builds sur vos modifications prêtes à être commitées.

Cela va transmettre vos sources sous forme de Patch à Teamcity. Celui-ci va ensuite lancer le job de votre choix. Si le build est un succès, vous avez gagné le droit de voir vos sources promues sur le repository. Sinon, l’IDE vous invite à corriger les erreurs remontées et à retenter votre chance plus tard.
Cette solution est simple et rapide à mettre en place. Teamcity est gratuit jusqu’à trois agents et vingt projets.

Seulement avec Git, Teamcity peut seulement exécuter des Personal-Build sur vos changements en cours, ou vos commits en attente. Il ne pourra pas (en version actuelle) les pousser si le résultat du job est OK.

Personal Build en script

Chez notre client, nous avons donc développé une solution pragmatique qui serait indépendante du serveur d’intégration, une solution à base de script. Pour l’environnement de travail, nous avons :

  • MacOS
  • Git
  • un projet Java
  • Maven
  • un serveur d’intégration pour chaque développeur, avec les mêmes versions de JDK, Maven et serveur d’application, et un repository Git local

Comme chaque développeur possède sa machine virtuelle distante, chacune d’entre elles possède un repository local Git. On peut alors pousser ses commits sur cette machine, et avec Maven, lancer une liste de tâches pour vérifier l’état des commits.

Initialisation du repository Git distant

Voici la liste des commandes Git pour initialiser ce repository distant:

git init .
git config receive.denyCurrentBranch ignore
git config receive.denyDeleteCurrent ignore

Les deux commandes précédentes permettent à Git d’autoriser un repository distant (celui du développeur) à modifier et supprimer la branche qui est en cours de checkout (interdit par défaut).

Le script côté développeur

Le script repose essentiellement sur la puissance de Git et de communication SSH pour lancer des commandes Maven. Le principe est simple

  • Récupération des sources depuis origin
  • Détection en local de la branche courante ainsi que la dernière révision à pousser
  • Envoi du code sur la machine distante
  • Exécution des tests sur la machine distante
  • Si tous les tests passent, les sources sont envoyées sur origin.

Le script est disponible sur le repository Github.com de Financeactive (https://github.com/financeactive/fa-toolkit/). Il contient certes des commandes de tests spécifiques au projet mais reste simple à lire.

Schéma descriptif

La solution est détaillée sur ce schéma.

Description de la solution

Vous souhaitez plus d’explication sur le fonctionnement du script ? Voici la liste détaillée de ses étapes.

1. Détection des informations suivantes :

git rev-parse --show-toplevel #Root du projet en cours
git rev-parse --symbolic --abbrev-ref $(git symbolic-ref HEAD) #Branche locale de travail
git log --pretty=%H -1 #Dernière révision à pousser 

git config branch.$CURRENT_BRANCH.remote #L'alias du remote associé à cette branche
git config remote.$REMOTE_ALIAS.url #L'url du remote
git config branch.$CURRENT_BRANCH.merge #Le nom de la branche remote suivie

2. Présentation à l’écran de la liste des commits prêts à pousser

git log $REMOTE_ALIAS/$REMOTE_BRANCH.. --format='; ;%Cred%h%Creset;%C(yellow)%cd%Creset;%f' --date=iso | column -t -s';'

3. Vérification de l’état du repository local avant de faire le pull

git status --porcelain | grep -v '??' | wc -l

==> S’il y a des modifications en cours (fichiers suivis par Git, modifiés mais non commités)

=> SI OUI

git stash

==> SINON RIEN

4. On récupère l’historique distant

git pull $REMOTE_ALIAS $CURRENT_BRANCH

==> S’il y avait un stash

git stash pop

5. On recharge la révision à tester car le pull a pu créer des commits de merge ou de rebase

git log --pretty=%H -1

6. On tente un push en dry-run pour détecter au plus tôt les possibles problèmes de merge

git push -n  $REMOTE_REPO $CURRENT_REVISION:$REMOTE_BRANCH

exemple : git push -n origin f313ae:master

7. On envoie le code sur la VM

git push remote-run +$CURRENT_BRANCH:$CURRENT_BRANCH

Le + avant current-branch permet de forcer l’update quoi qu’il arrive, après-tout c’est votre VM, vous en faites ce que vous voulez. Attention, ne surtout pas utiliser sur Github. Vous écrasez alors tout l’historique de Github avec le vôtre.

8. Sur la VM, on charge la branche

Le git push n’envoie que les blobs dans le répertoire .git. Pour que cela ait un impact sur le filesystem, il faut faire :

git checkout -f ${CURRENT_BRANCH}
git clean -df

Le git clean permet de supprimer les fichiers non trackés par git (dans le cas de passage de master à patch par exemple)

9. Lancement de tous les tests

mvn clean install #via SSH

10. On pousse si tout va bien

git push $GIT_DRY_RUN $REMOTE_REPO $CURRENT_REVISION:$REMOTE_BRANCH

Le fait d’utiliser la commande git push, avec le nom de la révision, le remote et la branche, permet de continuer à travailler sur son environnement local sans soucis. Vous pouvez même changer de branche durant l’exécution du script. Cela n’aura pas d’impact sur le code envoyé en cas de succès. La seule possibilité est d’amender le dernier commit en cours de test. Mais le but de ce script est d’aider le développeur, pas de l’empêcher de faire ce qu’il veut.

Nous avons créer un alias sous git pour appeler ce script. Ainsi, un git remote-safe-push nous lance la validation de notre code avant le push. Nous n’utilisons pas de hook pour permettre une plus grande flexibilité.

Retour d’expérience

L’exécution dure aujourd’hui quinze minutes. Cela peut paraître aberrant d’attendre autant de temps pour pouvoir pousser votre code sur le repository central. Mais vous n’êtes pas obligés d’attendre la fin du script pour continuer de travailler. Il y a donc très peu d’impact pour le développeur.

Dans son discours sur le déploiement continu lors du dernier Devoxx, Dave Farley aborde ce sujet. Il explique que dans son équipe, chaque développeur devait attendre la fin de l’intégration continue pour son commit avant de prendre une autre tâche. Ils n’avaient pas à ce moment de « Personal Build ». Chaque développeur s’assurait donc que son commit n’allait pas impacter les autres. Quand on pense chaque commit comme une release-candidate, cela change la vision des choses !

C’est une solution simple et pragmatique qui répond au besoin : garder une intégration continue en parfait état. Elle est personnalisable et facile à mettre en place. Elle est aussi évolutive ; ajouter des tests fonctionnels avec Selenium, de charge avec JMeter, ou plein d’autres choses reste possible car après tout, c’est un script.
Si vous envisagez de faire du déploiement continu, cela présente encore un avantage. Vous pouvez déjà tester votre procédure de déploiement de production sur cette machine, et cela plusieurs fois par jour et par développeur. Il n’est jamais facile d’avoir un environnement local identique à la production. La virtualisation est une bonne technique pour palier à cela. Si vous pensez à utiliser des solutions cloud de type PaaS, le script peut permettre aussi de tester la création d’instances à la demande, de tester l’installation complète d’un environnement d’un serveur, d’une base de données, ou encore de tester les configurations des proxys.

Nous envisageons d’ailleurs d’utiliser la virtualisation. Des outils comme Vagrant permettent de créer des machines virtuelles sur VirtualBox. Puppet peut ensuite nous permettre de monter un environnement similaire à la production mais en local. Toute la chaîne de déploiement est ainsi testable en local.

Le script personnalisé revêt néanmoins quelques inconvénients. Il ne nous permet pas de gérer des files d’attentes de jobs, ou de l’intégrer pour l’instant dans l’IDE. Plus le temps entre la commande git pull (je reçois) et git push (j’envoie) est long, plus le risque d’avoir un commit « étranger » poussé entre-temps est grand. Git ne vous permettra pas de pousser un seul commit si votre historique n’est pas à jour. Vous serez alors condamné à mettre à jour votre historique local avant de pousser. Il faut donc trouver l’équilibre à mettre en place dans ce mécanisme, pour ne pas rendre l’envoi de modifications complètement impossible. Nous avons avec notre client l’objectif de descendre sous la barre des 10 minutes, ce qui nous semble un temps satisfaisant.

Cette solution nous a permis d’augmenter la qualité globale de l’application en encourageant les développeurs à faire des tests. Depuis, nous poussons notre code beaucoup plus régulièrement. Cela permet d’avoir des retours toujours plus rapides sur nos stories de la part de l’équipe produit.

Un dernier petit inconvénient (c’est à voir) : si vous aviez choisi la méthode « croissants » au petit-déjeuner pour limiter le nombre de casseurs de builds, il faudra vous trouver autre chose pour avoir des viennoiseries le matin !

source image http://www.geekandpoke.com

Laisser un commentaire