Donovan Bourlard
Actuellement en alternance, Donovan est en 2ème année de master ICONE dispensé à La Rochelle. Dans le cadre de son alternance, il occupe le poste de Développeur web Back-end chez BiiG.
Qui n’a jamais été témoin d’une mise en production catastrophique, où toute l’application se met à bugger. Plus rien ne fonctionne, c’est la pagaille. Votre chef de projet vous met la pression et finit par vous demander de revenir à l’ancienne version.
Comment faire ? Vous venez de tirer votre branche ! Vous vous mettez donc à rechercher le commit de l’ancienne mise en production pour revenir dessus.
Voilà pourquoi il est nécessaire de tagger son application, cela évite pas mal de problèmes lors des mises en
production. Si la mise en production fait passer notre application de la v1.1
à la
v1.2
mais qu’un soucis survient, on peut facilement revenir en v1.1
par la simple
commande git reset v1.1 --hard
.
L’utilisation de tags va nous permettre d’identifier facilement vos commits, de manipuler facilement la rétrogradation / incrémentation de versions.
On se repère avec les numéros de versions pour la production plutôt que les numéros de commit: « On est en
version 2.1 » plutôt que de dire « On est sur la master au commit f23e9a
».
Tagger son application permet aussi de donner un sens sémantique via les numéros de version utilisés.
En effet, la version 2.1.0
et la version 1.1.3
ne vont pas avoir la même
signification. On constate que les chiffres sont différents et on en devine l’évolution de l’application
(une v2
signifiera que l’application s’est refaite une jeunesse par exemple).
C’est pourquoi il faut s’approprier une certaine logique dans son taggage. On ne va pas tagger n’importe
comment son application.
Sur un numéro de version de la forme X.Y.Z
, on va donc chercher à donner une certaine
signification au X
, Y
et Z
.
Mais attention, selon l’application que nous développons nos tags vont avoir de petites variantes. Je vais ici vous prendre l’exemple d’un site web sur lequel je suis amené à faire des mises à jours fréquentes (fonctionnalités, bugs, …) et le développement d’un package PHP ou un bundle Symfony.
Je vais vous faire part de ma manière de tagger les applications. Sachez que je ne suis pas la voix de la raison, loin de là :).
Repartons de la base « Version X.Y.Z ». Comment je gère ces incrémentations ? Et bien cela est simple:
X
: Représente la version majeure de l’application. Je l’incrémente lorsque l’on fait évoluer
le site de manière considérable (nouvelle charte graphique, modification de la navigation globale, …).
C’est les fameuses v2
ou v3
dont on entend souvent parler.
Y
: Représente la version mineure de l’application. Je l’incrémente lorsque l’on
développe de nouvelles fonctionnalités sur le site actuellement en production. C’est celle que
j’incrémente le plus souvent lorsque je suis sur la maintenance d’une application où nous faisons des
mises en production fréquentes.
Z
: Représente la version de correctif. Je l’incrémente lorsque je découvre un bug en
production qu’il faut rapidement fixer. Je déploie rapidement le correctif et incrémente ce numéro de
version. Bien évidemment, comme nous développons toujours dans les règles de l’art cet indice de
version ne nous sert jamais ! ;)
Libre à chacun d’exercer le taggage comme il le souhaite (à charge du développeur de le documenter pour ses utilisateurs). Il est possible d’appliquer ce que je viens de vous énumérer ci-dessus mais je n’en suis pas spécialement fan.
Pourquoi me direz vous ? Qui n’est pas déjà tombé sur un package où la mise à jour de la version
2.3.10
à la version 2.3.11
faisait crasher toute l’application qui l'utilise ?
C’est pourquoi j’aime avoir un cadrage de version assez strict pour mes dépendances.
Plusieurs solutions existent mais pour ma part je fais le choix de SemVer (Semantic Versioning). SemVer est utilisé dans l’écosystème Symfony (moins dans Laravel par exemple) et est surtout utilisé sur la quasi totalité des projets NPM.
Je vois SemVer comme une sorte de contrat entre les développeurs du package et ses utilisateurs. La version va fonctionner un peu comme expliqué précédemment mais à une petite nuance près.
X
: changement non rétro compatibleY
: changement rétro compatibleZ
: correctif
Dans ce cas précis l’utilisateur peut mettre à jour son package sans soucis, que ce soit sur la version mineure
(Y
) ou celle de correctif (Z
). Le package fonctionnera toujours pour notre application
et ne fera pas l’objet de BC Breaks.
Lorsque nous passerons à une version majeure supérieure, nous aurons (très probablement) des BC Breaks à gérer dans notre application.
Comment prévenir un BC Break ? Lors de votre développement d’une feature, vous vous rendez compte qu’une classe
n’a plus d’utilité ou que vous souhaitez la supprimer. Cela peut créer un BC Break si vous développez une
librairie par exemple car une personne peut avoir surchargé une méthode. Il ne faut donc pas la supprimer mais
la « déprécier » jusqu’à la prochaine version majeure. La prochaine version majeure supprimera tout simplement
la classe A
(cf. ci dessous).
Admettons que nous soyons en version 3.2
, notre classe ne sera donc supprimé qu’en version
4.0
.
Vous aurez donc à ajouter un @deprecated
au dessus de la classe dépréciée.
Ce qui vous donnerait ceci :
namespace Namespace\to\your\ClassA;
use Namespace\to\your\ClassB;
/**
* @deprecated ClassA class is deprecated since version 3.2, to be removed in 4.0. Use the Namespace\to\your\ClassB class instead.
* @trigger_error('Deprecation message', E_USER_DEPRECATED);
*/
class ClassA extends ClassB
{
// ...
}
namespace Namespace\to\your\ClassB;
class ClassB
{
// ...
}
Pour tagger des versions alpha
, beta
ou autre, il vous suffit de suffixer votre numéro
de version (ex: 2.0.0-alpha
, 2.0.0-alpha+0001
pour un fix en alpha, …) mais il faut
garder à l’esprit l’ordre des versions lors de vos nommage:
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0
Je vous invite à aller vous documenter directement sur le site de SemVer que je trouve complet.
Nous pouvons illustrer cette problématique de taggage à une application que vous connaissez tous: Symfony.
Symfony possède plusieurs versions (2.8.11
, 3.3
, 4.0
, …) et va respecter
une certaine charte dans ses numéros de versions. Ne cherchez plus, c’est bien SemVer !
Là où je trouve SemVer puissant c’est lorsqu’on le combine avec Composer. En effet, composer vous permet d’indiquer les dépendances que vous allez utiliser ainsi que leur version associée.
« Je ne comprend pas, j’ai fais un composer update
et mon application ne fonctionne plus ! »
Vous pourrez oublier cette phrase si vous faite bien attention aux dépendances que vous utilisez sur votre application. Si ces dernières utilisent SemVer, il vous faudra simplement bien renseigner la version à utiliser dans composer.
Pour renseigner la version de votre paquet, vous pourrez utiliser 3 symboles:
^
, ~
et *
.
Chacun va avoir une signification que vous allons découvrir à travers un exemple concret.
Nous utilisons une librairie utilisant SemVer dont la dernière version est la 1.2.6
.
Comment brider composer à des versions compatibles ?
De base je bride toutes mes versions avec 2 notations lorsque je ne suis pas sûr que la bibliothèque utilise SemVer.
{
// ...
"require": {
"awkan/my-bundle-1": "~1.2.6",
"awkan/my-bundle-2": "1.2.*",
// ...
},
// ...
}
Ces deux notations vont avoir le même impact: brider notre montée de version dans les versions de fix de la
librairie, soit le composer update ne pourra mettre à jour que l’indice Z
de la librairie
(1.2.7
, 1.2.13
, …). Cela me permet d’éviter les BC Break en pleine version mineure
comme j’ai déjà eu le cas.
Remarque: J’ai déjà pu observer un BC Break en version de fix ! Il ne nous reste plus qu’à brider totalement la librairie à une version donnée. Pensez également à créer une issue sur le projet correspondant pour signaler le BC Break, ça permettra aux suivants de rencontrer ce souci ;).
{
// ...
"require": {
"awkan/my-bundle-1": "1.2.6",
// ...
},
// ...
}
La dernière notation que je vais aborder ici est le ^
.
Dans le cas d’un bundle utilisant SemVer je vais utiliser sans soucis le ^
:
{
// ...
"require": {
"awkan/my-bundle-1": "^1.2",
"awkan/my-bundle-2": "^1.2.6",
// ...
},
// ...
}
Le ^
va brider la version à la majeure, soit ici nous empêcher de passer en 2.0.0
(accepte toutes les versions >2.0.0
). Dans le cas de SemVer c’est parfait ! Nous profiterons des
corrections de bug et des nouvelles fonctionnalités du bundle SANS soucis de non rétro compatibilité.
En conclusion:
~X.Y.Z
ou X.Y.*
lorsque vous n’êtes pas sûr•e que le bundle respecte SemVer
^X.Y
ou ^X.Y.Z
lorsque le bundle que vous utilisez respecte SemVer
Dans les développement de tous les jours, nous utilisons GitLab au boulot. Lorsque nous taggons notre application, la version taggée va être soumise à la CI de Gitlab. Cette dernière va exécuter tous les tests (unitaires et fonctionnels). Si les tests sont validés, le déploiement en production se fait directement de manière automatisé. Cependant si les tests ont échoué, il nous faudra les corriger jusqu’à ce qu’ils passent pour que la mise en production s’effectue.
Voici une configuration simple de la CI (à adapter bien sur …).
#.gitlab-ci.yml
stages:
- test
- deploy
- cleanup
before_script:
# Avant l’exécution du script, je copie les paramètres de mon application
- cp .env.test .env
- cp apps/api/app/config/parameters.yml.gitlab-ci apps/api/app/config/parameters.yml
tests:
stage: test
script:
# Ici voici un exemple dans lequel:
# - Je "up" mes containers docker
# - J'installe mon application Symfony 3 et crée sa base de données
# - Je lance les tests
- docker-compose up -d
- docker-compose ps
- docker-compose exec -T --user www-data app composer install --prefer-dist --no-interaction --optimize-autoloader # Composer install
- docker-compose exec -T --user www-data app bin/console doctrine:schema:drop --force
- docker-compose exec -T --user www-data app bin/console doctrine:schema:create
- docker-compose exec -T --user www-data app bin/console assets:install
- docker-compose exec -T --user www-data app bin/console cache:warmup
- docker-compose exec -T --user www-data app bin/console doctrine:schema:drop --force
- docker-compose exec -T --user www-data app bin/console redis:flushall --no-interaction
- docker-compose exec -T --user www-data app bin/console doctrine:schema:create
- docker-compose exec -T --user www-data app bin/console doctrine:fixtures:load -n
- docker-compose exec -T --user www-data app ./bin/phpunit --colors=always
tags:
- docker-compose
deploy:
stage: deploy
# Effectuer le deploy seulement sur les tags
only:
- tags
script:
# Effectuez ici vos commandes de déploiement
# Par exemple: Capistrano
# Si vous avez installé Capistrano sur votre site web et qu’un container (ici deployer-container) possède ruby, vous pouvez lancer la commande standard de déploiement
# Nous effectuons ici le déploiement du stage “prod”
- docker-compose exec -T --user www-data deployer-container bundle exec cap prod deploy
# Son but est de cleaner les container utilisés précédemment
cleanup_job:
stage: cleanup
# Toujours effectuer ce job, même si les anciens jobs ont échoué
when: always
script:
- docker-compose down
tags:
- docker-compose
La CI va donc exécuter dans l’ordre les « stages » test, deploy puis cleanup.
Tagger son application est une bonne chose, abusez-en !! (pas trop quand même). Cela permet d’améliorer la lisibilité dans les MEP et de faire un tas d’autre choses.
Les numéros de versions que vous donnez ont tous un sens sémantique et peuvent être un gage de confiance et/ou qualité (comme par exemple une application utilisant SemVer).