Les équipes de développement intègrent souvent des règles de fonctionnement qui les aident à produire du code de qualité. L'une d'elles est le "git workflow" :
Une nouvelle fonctionnalité est décidée. Le développeur (appelons-le Didier) crée une nouvelle branche dédiée.
Il y réalise les changements nécessaires et envoie régulièrement son code sur GitHub (ou Gitlab...).
Lorsque la fonctionnalité est prête à être mise en ligne, il crée une pull request pour intégrer sa branche dans la master.
Une développeuse de son équipe (appelons-la Sonia) examine la pull request (PR). Elle fait un pull en local et lance les tests pour vérifier que tout fonctionne.
Quand les tests sont terminés, elle laisse des commentaires sur la PR.
Didier intègre les corrections.
Lorsque tout est prêt, Sonia valide la PR. La branche de développement est alors fusionnée dans la branche master.
Si les tests sont tous au vert, quelle a été la valeur ajoutée de Sonia ? Elle a perdu 10 minutes à charger le code en local, lancer les tests et valider qu'ils passaient. Elle a été interrompue dans son travail pour une tâche qui pourrait être facilement automatisable !
Étant donné que cela la déconcentre, elle a bloqué un moment dans son agenda afin d'examiner la PR tranquillement. Mais elle ne peut pas le faire souvent. Didier s'est alors dit : "Je ne vais pas faire beaucoup de PR, il vaut mieux que je regroupe tous les changements en une".
Progressivement, les PR grossissent et deviennent ingérable. Sonia perd du temps à lancer les tests sur son ordinateur et ne peut pas se concentrer sur l'essentiel : le code.
Et si vous pouviez utiliser un service qui exécuterait les tests quand une PR est acceptée ? Vous avez deviné, je vous parle d'intégration continue.
Intégration continue (CI)
On appelle "intégration" les tâches à faire lorsqu'un·e développeur·se a fini de coder une nouvelle fonctionnalité. Concrètement, il/elle doit s'assurer :
que son code ne contient aucun bug ;
que le reste de l'application continue de bien fonctionner si il/elle intègre ses changements ;
que la fonctionnalité correspond aux bonnes pratiques de développement de l'équipe.
Autrement dit, le/la développeur·se s'arrête d'écrire du code pour tester différents aspects de l'application. Plus le projet est grand, plus le risque d'erreur est élevé et plus la tâche est longue.
L'intégration continue (Continuous Integration) est une des pratiques les plus répandues des méthodologies de projet Agile. Elle permet de diminuer le risque d'erreur en production et d'améliorer le temps consacré à l'intégration.
Une équipe qui pratique l'intégration continue vise deux objectifs:
réduire à presque zéro la durée et l'effort nécessaire à chaque épisode d'intégration ;
pouvoir, à tout moment, fournir un produit exploitable.
Il est assez simple d'utiliser un service d'intégration continue qui va lancer des tests automatisés lorsqu'une pull request est acceptée. Voici comment cela se déroule :
Une pull request est créée.
Le service d'intégration continue lance l'application sur un serveur de test.
Si les tests échouent, le service indique que la fonctionnalité n'est pas prête à être intégrée.
Si les tests passent, le service affiche un voyant vert.
Sonia peut alors relire sereinement la pull request. Si elle la valide, le code est intégré.
Il existe plusieurs services qui proposent de l'intégration continue : Ansible, Jenkins, Travis... Le plus simple à utiliser est Travis. Vous verrez, cela ira tout seul !
Travis
Travis est un service d'automatisation qui s'interface parfaitement avec GitHub. Il permet de créer un environnement de développement rapidement, d'y faire tourner une application et d'y lancer des tests.
Vous souvenez-vous de toutes les manipulations que vous avez effectuées dans la première partie de ce cours ? Travis le fait pour vous ! Cadeau !
Pourquoi Travis est-il si facile à prendre en main ? Car il intègre une configuration par défaut modifiable très aisément. Concrètement, voici les différentes étapes à mettre en place pour utiliser Travis :
indiquer à Travis de surveiller un dépôt GitHub ;
intégrer un fichier de configuration .travis.yml dans le projet GitHub.
Et c'est tout ! Incroyable, n'est-ce pas ?!
Voici, par exemple, un fichier .travis.yml :
language: python
python:
- "3.5"
# command to install dependencies
install:
- pip install -r requirements.txt
# command to run tests
script:
- pytest # or py.test for Python versions 3.5 and below
Ce script va installer les dépendances listées dans le fichier requirements.txt puis exécuter la commande pytest
. Fastoche. :)
Il est temps de pratiquer ! Voyons tout de suite comment lier un compte Travis à GitHub.
Activer l'intégration continue avec Travis CI
Rendez-vous à l'adresse https://www.travis-ci.com/ puis cliquez sur "Sign in with GitHub". Connectez-vous en utilisant votre identifiant et votre mot de passe GitHub.
Vous êtes redirigé vers le tableau de bord de Travis. Il regroupe les différents projets pour lesquels vous avez activé l'intégration continue. Pour l'instant vous n'en avez aucun, quelle tristesse ! Cliquez sur la croix à droite de l'onglet "My repositories" pour en ajouter un.
Travis affiche alors tous les dépôts publics de votre compte GitHub. Trouvez celui que vous avez forké tout à l'heure et activez-le en cliquant sur la croix grise.
C'est tout ! :) Vous devez maintenant indiquer à Travis les commandes à exécuter lors du build.
Étapes d'un déploiement sur le serveur de test
Le processus de déploiement que suit Travis est toujours le même :
installation : il commence par installer les logiciels demandés ;
exécution : puis il exécute les tests.
Dans le fichier de configuration, l'installation est désignée par le mot install
et l'exécution par script
.
À noter que vous pouvez également demander à Travis d'exécuter certaines commandes avant ou après ces étapes :
before_install
: avant d'exécuter les commandes d'installation, fais ça.before_script
: quand les étapes d'installation ont été correctement effectuées, fais ça.after_success
orafter_failure
: si les scripts échouent ou réussisent, fais ça.after_script
: quand les scripts ont été exécutés, fais ça.
Avant de continuer, prenez cinq minutes pour réfléchir à ces étapes puis appliquez-les au projet en cours. Quelles commandes voulez-vous exécuter ? À quel moment ?
Pour ma part, je vois les étapes suivantes :
Avant d'exécuter les tests, Travis installe les dépendances listées dans
requirements.txt
;Puis Travis exécute la commande suivante :
./manage.py test
.
Mais ce n'est pas tout ! Comment lui indiquer qu'il doit installer PostgreSQL et lancer le service ? Et la version de Python ? Les variables d'environnement ? Pour répondre à toutes ces questions, lisez la documentation avant de poursuivre.
Créer un fichier de configuration
Créez un nouveau document, .travis.yml au même niveau que manage.py.
.travis.yml
language: python
python:
- '3.5'
before_script:
- pip install -r requirements.txt
env: DJANGO_SETTINGS_MODULE="disquaire_project.settings"
services:
- postgresql
script:
- ./manage.py test
Mais il manque énormément d'éléments ! Comment utiliser PyPI si Virtualenv n'est pas installé ?
Travis est un peu magique ! Si vous lui indiquez le langage, il installera automatiquement certains outils qu'il estime indispensables, tel que Pip et Virtualenv. Il va même activer l'environnement virtuel pour vous !
Le fichier est prêt à être envoyé sur GitHub mais prenez quelques instants pour réfléchir à ce que vous souhaitez faire. À quel moment, dans votre cycle de développement, les tests doivent-ils se déclencher ? Vous avez plusieurs possibilités :
Lorsqu'une modification est apportée sur n'importe quelle branche. Autrement dit tout nouveau
git push origin unebranche
lancera l'exécution des tests.Lorsqu'un push est réalisé sur une branche en particulier.
Lorsqu'une pull-request a été ajoutée ou mise à jour.
...
Pour ma part, je décide de lancer un build à chaque fois que je pousserai du code sur la branche staging
. Ainsi, si les tests passent je pourrais créer une pull-request pour demander à fusionner cette branche dans master
.
Travis permet cela de manière assez simple :
# ...
# safelist
branches:
only:
- staging
# ...
Et voilà, le tour est joué ! Vous n'avez plus qu'à envoyer ce fichier sur GitHub !
$ git add .travis.yml
$ git commit -m "Add travis file"
$ git push origin master
Allez, vous êtes prêt à tester ? :)
Retournez dans l'interface d'administration de Travis. Rien n'apparaît et c'est bien normal : vous avez demandé à ce qu'un build ne soit exécuté que lorsqu'un changement sur la branche staging
est détecté.
Créez donc une nouvelle branche et poussez-la :
(env) celinems@disquaire:~/disquaire$ git checkout -b staging
(env) celinems@disquaire:~/disquaire$ git push origin staging
Revenez à Travis. Un build se lance ! Youpi ! Malheureusement, il s'interrompt au bout de quelques minutes.
Saperlipopette, qu'a-t-il bien pu se passer ?
Lisez les logs affichés par Travis :
# ...
File "/home/travis/virtualenv/python3.5.3/lib/python3.5/site-packages/django/db/backends/base/base.py", line 189, in connect
self.connection = self.get_new_connection(conn_params)
File "/home/travis/virtualenv/python3.5.3/lib/python3.5/site-packages/django/db/backends/postgresql/base.py", line 176, in get_new_connection
connection = Database.connect(**conn_params)
File "/home/travis/virtualenv/python3.5.3/lib/python3.5/site-packages/psycopg2/__init__.py", line 130, in connect
conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
django.db.utils.OperationalError: FATAL: role "celinems" does not exist
The command "./manage.py test" exited with 1.
Done. Your build exited with 1.
Hum... Avez-vous trouvé l'erreur ?
Avant d'exécuter les tests, Django crée une base de données spécifique pour éviter de mélanger les torchons et les serviettes. Or actuellement les réglages par défaut sont ceux de développement !
Malheureusement, l'utilisateur PostgreSQL créé par Travis ne correspond pas au vôtre... La documentation de Travis est très explicite sur ce sujet.
Que faire ? Créez simplement un nouveau fichier dans settings
. Heureusement que vous avez déjà un module dédié ! ;)
Comme pour les réglages de production, créez un nouveau fichier travis.py
dans le dossier settings
.
settings/travis.py :
from . import *
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': '',
'USER': 'postgres',
'PASSWORD': '',
'HOST': '',
'PORT': '',
},
}
Comme tout à l'heure, utilisez la variable d'environnement DJANGO_SETTINGS_MODULE
. Il suffit de l'ajouter à .travis.yml
!
language: python
python:
- '3.5'
# safelist
branches:
only:
- staging
before_script:
- pip install -r requirements.txt
services:
- postgresql
env: DJANGO_SETTINGS_MODULE=disquaire_project.settings.travis
script:
- ./manage.py test
Allez, on retente ?
Envoyez les fichiers modifiés sur GitHub.
$ git add .travis.yml disquaire_project/settings/travis.py
$ git commit -m "update travis config"
$ git push origin staging
Retournez sur l'interface de Travis et admirez le travail !