Semantic versioning and releasing made easy
Utiliser le semantic versioning pour générer des releases et déployer simplement
Publié le
Do not index
Do not index
Primary Keyword
Lié à Analyse sémantique (Articles liés) 1
Lié à Analyse sémantique (Articles liés)
Statut rédaction
Terminé
Lié à Analyse sémantique (Articles liés) 2
IntroductionPrésentation des outilsSemantic versioningSemantic releaseGitMise en placeInstallation de Semantic releaseConfiguration de Semantic releaseConfiguration des pipelines de CI/CD Conclusion
Introduction
La gestion des versions reste encore et toujours un problème ouvert, et ce depuis le premier programme de l’histoire.
Aujourd’hui nous allons essayer d’apporter une réponse à ce problème en utilisant les concepts suivants :
- Semantic versioning pour avoir un standard sur la manière dont les montées de versions sont effectuées
- Semantic release pour gérer l’interaction entre les versions du répertoire de code, et les releases de l’application
- Git et une gestion des branches et du flow standardisée au sein d’un même répertoire
Présentation des outils
Semantic versioning
Semantic versioning est un standard ayant pour but d’uniformiser la gestion des montées de versions pour toutes les applications ayant cette thématique.
SemVer utilise une notation fixée et spécifiée entièrement, pouvant inclure une note de prerelease, ainsi qu’une note de build. D’après la documentation officielle, la grammaire BNF (non exhaustive) de la version finale est la suivante :
<valid semver> ::= <version core>
| <version core> "-" <pre-release>
| <version core> "+" <build>
| <version core> "-" <pre-release> "+" <build>
<version core> ::= <major> "." <minor> "." <patch>
Ce qui nous donne, par exemple, les versions suivantes :
1.4.3
63.0.0
2.0.0-alpha1
3.5.1+20231002
0.9.11-preview+pr193
Pour ensuite gérer les montées de version, on utilise les critères suivants :
- si la version corrige un bug, ou n’inclue pas de nouvelles fonctionnalités (par exemple une optimisation, ou une réorganisation du code), alors on monte la version de patch
- si la version inclue une nouvelle fonctionnalité, autant pour les utilisateurs qu’en interne de la base de code, alors on monte la version mineure
- si la version inclue des changements incompatibles avec la version précédente, alors on monte la version majeure
D’autres cas plus spécifiques, comme les versions
0.Y.Z
, sont couverts et documentés sur la page officielle de Semantic versioning.Semantic release
Semantic release est un logiciel utilisant le Semantic versioning pour générer des releases et des journaux de changements de manière automatisée. Il s’intègre dans un répertoire Git existant sous la forme de tâches à lancer dans les pipelines de CI/CD.
Ici, nous allons l’intégrer de telle manière à générer de nouveaux tags sur Git lors de la poussée de nouvelles versions sur des branches.
Git
Enfin, pierre angulaire de l’immense majorité de nos outils, Git n’a plus besoin d’être présenté.
Ici, nous n’allons pas décrire le logiciel, mais la manière dont nous l’utilisons pour gérer les montées de versions, et les différents environnements.
Nous allons utiliser les branches pour décrire l’environnement sur lequel nous travaillons, et les tags pour marquer les différentes versions au fur et à mesure du développement.
Les développeurs travaillent sur des branches pour chacune des tâches qu’ils ont à effectuer, avec la nomenclature suivante :
chore/<branch-name>
: pour les changements qui ne s’intègrent pas dans les catégories d’au-dessus (perf, refactoring, etc).
fix/<branch-name>
: réparation d’un bogue
feat/<branch-name>
: ajout d’une nouvelle fonctionnalité
Les noms des branches doivent suivre également un standard, et peuvent prendre la forme suivante :
- nom de la fonctionnalité, description du bogue, etc
- identifiant d’un ticket sur un logiciel externe (Jira, etc)
- nom du développeur et identifiant d’une tâche, etc
- et bien d’autres possibilités
Une fois leur fonctionnalité développée, les changements sont intégrés sur une branche de développement, couramment nommée
develop
. Lorsque l’on veut intégrer ces changements sur un environnement plus proche de celui de production et y effectuer des tests plus généraux, on peut intégrer les changements de la branche
develop
sur une autre branche, couramment nommée staging
cette fois.Si tout semble bon, et que les changements sont prêts à partir en production, alors on peut tout intégrer sur la branche principale, typiquement
main
.Mise en place
Nous avons désormais tous les outils en main pour déployer Semantic release sur un répertoire Git.
Les tâches à effectuer sont les suivants :
- Installation de Semantic release
- Configuration de Semantic release
- Mise-à-jour de la CI/CD pour tout y intégrer
Installation de Semantic release
Semantic release prend la forme d’un paquet JavaScript, que l’on peut installer via NPM via la commande suivante :
npm install --save-dev semantic-release
Semantic release vient également avec des dépendances que nous pouvons installer dans la volée, et sur lesquelles nous reviendrons dans la section suivante :
# Requis dans tous les cas
npm install --save-dev @semantic-release/commit-analyzer @semantic-release/exec
# À adapter suivant la plateforme que vous utilisez
npm install --save-dev @semantic-release/gitlab
Toutes les dépendances sont maintenant installées, et nous pouvons passer à la configuration.
Configuration de Semantic release
La configuration de Semantic release est rangée dans le fichier
.releaserc.yaml
au sein du répertoire Git.Un exemple minimal de configuration est le suivant :
branches:
# N'activer que sur la branche 'main'
- main
plugins:
# Analyse des commits pour extraire le type et le nom des changements effectués
- "@semantic-release/commit-analyzer"
# Créer des nouveaux tags sur Gitlab
- "@semantic-release/gitlab"
# Créer un fichier contenant le nom de la nouvelle version à chaque release
# /!\ il y a bien deux tirets d'affilée
- - "@semantic-release/exec"
- verifyReleaseCmd: "echo ${nextRelease.version} > .VERSION"
Ce fichier de configuration met en place les paramètres suivants :
- Semantic release ne s’active que sur la branche
main
, en effet, sur les autres branches, il n’y a pas de release à créer puisque les changements n’ont lieu que sur des environnements de tests éphémères et instables
- configuration de trois plugins, un pour pouvoir lire et analyser le contenu des commits effectués entre chaque release, un pour pouvoir se connecter à Gitlab et créer et pousser de nouveaux tags, et enfin un dernier pour créer un fichier contenant le nom de la nouvelle version. Ce fichier sera utilisé dans les pipelines de CI/CD que nous décrirons après.
Configuration des pipelines de CI/CD
Nous prendrons ici comme exemple une pipeline Gitlab CI, mais le fonctionnement est le même sur Github Actions, Jenkins, Azure DevOps, et tous les autres outils de ce type.
Le fonctionnement de la CI/CD va être le suivant. Deux nouvelles tâches vont s’ajouter à celles précédemment existantes, et effectuer les actions suivantes :
- La première, dite de Pre-Release, va calculer la nouvelle version de l’application
- La seconde, dite de Release, va construire puis pousser la nouvelle version de l’application
On va, pour ça, utiliser plusieurs mécanismes. Le fichier
.VERSION
généré par Semantic release voit son contenu être injecté dans la variable d’environnement VERSION
. Cette variable sera ensuite utilisée dans l’action Release comme tag de l’application créée.Le code utilisé par cette pipeline aura donc l’allure suivante :
Pre-Release:
stage: pre-release
image: node:18
script:
- |
if [[ "$CI_COMMIT_BRANCH" = "main" ]]; then
npm install
npx semantic-release
test -e ".VERSION" || exit 0
echo "VERSION=v$(cat .VERSION)" >> build.env
elif [[ "$CI_COMMIT_BRANCH" = "staging" ]]; then echo "VERSION=staging" >> build.env
elif [[ "$CI_COMMIT_BRANCH" = "develop" ]]; then echo "VERSION=dev" >> build.env
else false
fi
artifacts:
reports:
dotenv: build.env
variables:
GL_TOKEN: $GITLAB_PAT
only:
- develop
- staging
- main
Release:
stage: release
image: docker:latest
dependencies:
- Build
- Pre-Release
services:
- docker:dind
script:
- test -z "$VERSION" && { echo "VERSION is not set or is empty - skipping release"; exit 0; }
# On suppose ici qu'un autre job s'occupe de constuire une image Docker $IMAGE
- docker tag $IMAGE $REGISTRY/$IMAGE:$VERSION
- docker push $REGISTRY/$IMAGE:$VERSION
only:
- develop
- staging
- main
L’action Pre-Release est donc en charge de générer la nouvelle version. Si l’on est sur une branche de test (
develop
ou staging
) alors la version est le nom de ladite branche, et, si l’on est sur la branche de production (main
) alors c’est Semantic release qui va générer la nouvelle version.Enfin, l’action Release détecte si le déploiement d’une nouvelle version est nécessaire (via la variable
VERSION
), auquel cas elle met en ligne la nouvelle version de l’application (présentement une image Docker).Ensuite, on peut faire ce que l’on veut avec cette nouvelle release, par exemple, laisser ArgoCD Image Updater détecter la nouvelle image et tout déployer automatiquement !
Conclusion
Dans cet article, nous avons présenté une solution pour gérer la montée de version des applications, ainsi que le déploiement continu et automatique de ces nouvelles versions.
Les montées de version sont standardisées, grâce au Semantic Versioning, et tout est intégré d’une manière unique via des pratiques Git établies.
Nous avons séparé l’intégration de la délivrance, en laissant la seconde être gérée par ArgoCD, et toutes les fonctionnalités qui vont avec.
Le cycle de mise-à-jour devient alors le suivant :
- Travail local, test du code, puis envoi vers l’hébergeur Git
- Intégration continue via l’hébergeur, et calcul d’une nouvelle version suivant la branche courante et le nom des commits précédants
- Construction d’une nouvelle image Docker
- Délivrance continue gérée par ArgoCD
Cette solution élégante et simplifiée pour les développeurs permet de leur retirer de la charge de travail, et les laisser se concentrer sur ce qui les importe le plus : le code !
Écrit par
Victor Franzi
Victor est notre première recrue, il est passionné depuis son plus jeune âge et ne veut jamais s’arrêter d’apprendre 🧠
Sujets