Je ne vais pas détailler les raisons pour lesquelles il est important de versionner une application, je vais plutôt m'attacher à décrire comment faire.
Ce tuto se repose sur l'outil python semantic release qui s'appuie sur les conventions de nommage des commits et de la convention de numérotation sémantique. C'est-à-dire qu'en respectant une certaine structure de message de commit git, la numérotation de version de votre application sera automatiquement réalisée.
SemVer
La numérotation sémantique est largement utilisée, elle consiste à mettre en place 3 chiffres dont chacun ont un rôle précis :
- 1er chiffre : version majeur : indique qu'il y a de gros changements dans le code (suppression méthode, renommage, configuration supplémentaire...) pouvant introduire des modifications dans le code où vous utilisez cette librairie
- 2ème chiffre : version mineure : indique qu'il n'y a pas de changement important profond, on garde la rétro-compatibilité (ajout d'une méthode, warning sur les méthodes qui seront supprimées, ajout d'une fonctionnalité légère...)
- 3 ème chiffre : version patch : indique qu'il y a des corrections d'anomalies (bugs, style, doc ...) en gardant la rétro-compatibilité
Cela donnera une version de ce type :
1.2.3Pour plus de renseignement https://semver.org
Convention Angular
Angular utilise une convention pour structurer ses messages de commits https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits
Je vais me centrer sur le `type` qui peut contenir plusieurs valeurs :
- : Ajout/Modification de fonctionnalité
feat
- : Correction d'une anomalie
fix
- : Ajout/Modification de documentation
doc
- : Ajout/Modification de l'aspect visuel
style
- : Ré-écriture d'un bout de code
refactor
- : Optimisation d'exécution
perf
- : Ajout/Modification des tests
test
Configuration python semantique release
Il faut commencer par installer la librairies dans vos dépendances de dev (car nous n'utiliserons ce module dans la CI et non en prod) avec votre gestionnaire de package (ici
pipenv
pipenv install python-semantic-release --dev
Il faut stoquer l'endroit où la version de l'application est mémorisé, j'ai choisi de le faire dans
setup.py
Copier le code
1 2 3 4 5 6 7 8 9 10 from setuptools import setup __version__ = "0.3.8" if __name__ == "__main__": setup( name="Blog", version=__version__, author="ITARVERNE", ...
Pour la configuration du module python semantique release j'ai choisi de tout placer dans le fichier
pyproject.toml
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [tool.semantic_release.branches.main] match = "(master)" [tool.semantic_release.remote] type="gitlab" token = { env = "GITLAB_TOKEN" } [tool.semantic_release] version_variables=['setup.py:__version__'] commit_message = "build: tag {version}" commit_author = "david <user@company.fr>" [tool.semantic_release.commit_parser_options] allowed_tags = [ "build", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test" ] minor_tags = ["feat"] patch_tags = ["fix", "perf", "style", "ci", "test", "docs"]
La section
[tool.semantic_release.branches.main]
master
La section
[tool.semantic_release.remote]
gitlab
La section
[tool.semantic_release]
__version__
setup.py
La section
[tool.semantic_release.commit_parser_options]
Si comme moi vous utilisez docker, pensez à mettre git dans votre container car l'outil en aura besoin
L'automatisation
J'ai choisi gitlab, voici un extrait du job version :
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 version: stage: version environment: production rules: - if: $CI_COMMIT_TAG != null && $CI_COMMIT_BRANCH == "master" when: never - if: $CI_COMMIT_BRANCH == "master" && $CI_COMMIT_TAG == null when: manual variables: GIT_STRATEGY: clone script: - export PIP_NO_CACHE_DIR=off # Git config, according clone strategy - git config --global user.email ${GITLAB_USER_EMAIL} - git config --global user.name ${GITLAB_USER_NAME} - git remote set-url origin https://${GITLAB_USER_LOGIN}:${GITLAB_TOKEN}@$CI_SERVER_HOST/$CI_PROJECT_PATH.git - git checkout $CI_COMMIT_BRANCH # For Python Semantic Release - cd back - pipenv install --dev - set +e - new_release=$(pipenv run semantic-release --noop version --print | grep "No release will be made" | wc -l) # Si pas de nouvelle version on arrête le job - if [[ new_release -ge 1 ]]; then exit 0 ; fi # Stop le job si erreur - set -e - pipenv run semantic-release -v version ${FORCE_SEMVER_MAJOR:+"--major"} - last_commit_hash_master=$(git log -1 --format=%H) # Report des modifs sur la branche dev - git fetch origin dev - git checkout -f dev - git branch # -m pour prendre la branche master dans le cas d'un commit de merge # --strategy=recursive -X ours : pour forcer la version de master - git cherry-pick --strategy=recursive -X theirs -x -m 1 $last_commit_hash_master - git push
- La règle pour exécuter ce job est qu'une MR doit être réalisée avec la branche master en destination
- La permet de ne pas utiliser le cache des runners pour bien avoir les derniers commits
GIT_STRATEGY: clone - Je modifie la configuration de git pour notamment intégrer dans le token
origin
généré auparavantTOKEN_GITLAB
- La commande permet de lancer l'outil pour vérifier s'il y a un incrément de version à faire
pipenv run semantic-release -v version
- Et enfin je reporte les modifications de la branche master (où travaille l'outil) sur la branche de dev
Workflow
Voici le schéma simplifié des branches du projet

Lors de la validation de la MR entre dev et master, le job version sera exécuté. Il lancera la commande
semantic-release version- Lire les messages de commits ce cette MR pour en extraire les types
- En fonction de la configuration détermine quelle incrémentation de version faire
- Mettre à jour la version dans la variable du fichier
__version__
setup.py
- Mettre à jour le structuré en fonction des messages de commit
CHANGELOG.md

- Crée un tag correspondant à la version trouvée
- Push les modifications sur la branche master
- Créer une release gitlab

A ce moment master est à jour, je décide d'appliquer également ces modifications sur la branche dev en faisant un cherry pick
La version générée par l'outil ne prend pas en compte la mise à jour de la version majeure. Par définition elle est importante et doit être forcer volontairement. J'ai donc une variable d'env
FORCE_SEMVER_MAJOR
--majorpipenv run semantic-release -v version ${FORCE_SEMVER_MAJOR:+"--major"}
Il ne vous reste plus qu'à déployer votre application depuis le tag git généré, via
Ansible