Automatisation des contrôles en local
Qu’est-ce qu’un git hooks ?
Les hooks Git sont des scripts qui s’exécutent automatiquement dès qu’un événement particulier se produit dans un dépôt Git. Ils vous permettent de personnaliser le comportement interne de Git et de déclencher des actions personnalisables à des points clés dans le cycle de vie du développement.
Création d’un hook
Nous souhaitons automatiser le formatage des fichiers qui seront inclus dans notre prochain commit. Pour cela, nous allons créer un hook qui s’occupera de l’auto-formatage et de la validation au moment du commit.
En supposant que vous ayez installé les packages nécessaires, sinon, tapez la commande suivante :
# Commande pour installer les packages
pip install black isort flake8
Pour utiliser ces packages, il vous suffit de taper les commandes suivantes à la racine de votre projet :
python -m black
python -m isort
python -m flake8
- Black : Suit les règles PEP 8 par défaut.
- isort : Organise les importations dans un ordre logique.
- Flake8 : Vérifie la conformité avec PEP 8.
Pour cela, nous allons créer un fichier pre-commit
personnalisé directement dans le dossier .git/hooks
de votre projet.
Voici un lien vers un tutoriel qui explique comment créer un git hook.
TP création d’un hook pre-commit
Pour ce TP, nous allons nous concentrer sur l’utilisation de la ligne de commande.
0. Observer le dossier .git
Nous allons parcourir le dossier .git
, qui contient des informations cruciales sur la gestion des versions de notre projet. Pour cela, nous utiliserons les commandes suivantes :
# Se déplacer dans le répertoire du projet
cd chemin/vers/ton/projet
# Afficher le contenu du dossier .git
ls -la .git
# Entrer dans le dossier .git
cd .git
# Voir le contenu du dossier .git
ls
# Accéder au dossier objects
cd objects
# Afficher le contenu du dossier objects
ls
# Revenir au dossier .git
cd ..
# Revenir au dossier parent du projet
cd ..
ls
: Affiche la liste des fichiers et des répertoires.-l
: Affiche les détails sous forme de liste (permissions, propriétaire, taille, etc.).-a
: Affiche tous les fichiers, y compris ceux qui commencent par un point (.
), qui sont normalement cachés.
cd
: Permet de changer de dossier.
Sous-dossiers importants dans .git
Voici quelques exemples de sous-dossiers essentiels que vous pouvez trouver dans .git
:
objects
: Contient les objets Git (commits, arbres, blobs).refs
: Contient les références aux branches et aux tags.config
: Fichier de configuration du dépôt Git.
1. Créer ou modifier le fichier pre-commit
Accédez au dossier .git/hooks
de votre projet et créez un fichier nommé pre-commit
:
touch .git/hooks/pre-commit
touch
est utilisée pour créer des fichiers vides
2. Ajouter du contenu au fichier pre-commit
Ouvrez ce fichier dans un éditeur de texte :
nano .git/hooks/pre-commit
nano
est un éditeur de texte en ligne de commande.
Ajoutez le script suivant :
#!/bin/bash
# Chemin vers Python (modifiez si nécessaire)
PYTHON_BIN=$(which python3)
# Récupérer la liste des fichiers actuellement dans l\'index (staging area)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=d | grep '\.py$')
if [[ -z "$STAGED_FILES" ]]; then
echo "Aucun fichier Python à vérifier."
exit 0
fi
echo "Exécution des vérifications sur les fichiers ajoutés au staging..."
# Formatage avec Black
echo "Formatage avec Black..."
$PYTHON_BIN -m black $STAGED_FILES
# Organisation des imports avec isort
echo "Organisation des imports avec isort..."
$PYTHON_BIN -m isort $STAGED_FILES
# Vérification avec Flake8
echo "Vérification du style avec Flake8..."
$PYTHON_BIN -m flake8 $STAGED_FILES
FLAKE8_STATUS=$?
if [[ $FLAKE8_STATUS -ne 0 ]]; then
echo "Erreur : Flake8 a détecté des problèmes. Commit bloqué."
exit 1
fi
# Réajouter uniquement les fichiers qui étaient dans le staging
echo "Ajout des fichiers modifiés au staging..."
git add $STAGED_FILES
echo "Toutes les vérifications sont passées. Commit autorisé."
exit 0
$(...)
est utilisé pour exécuter une commande et insérer son résultat dans une autre commande ou une variable.
echo
est utilisée pour afficher du texte ou des variables dans le terminal.
3. Rendre le fichier exécutable
Rendez le fichier pre-commit
exécutable avec la commande suivante :
chmod +x .git/hooks/pre-commit
chmod
(abréviation de Change Mode) est utilisée en ligne de commande pour modifier les permissions d’accès à des fichiers ou des répertoires
4. Tester le hook
Essayez de committer un fichier dans votre projet pour voir si le hook est exécuté :
git add .
git commit -m "Test du hook pre-commit"
Si des fichiers sont modifiés par Black ou isort, le hook les ajoutera automatiquement au staging et réexécutera les étapes.
Points importants
- Si un fichier ne respecte pas les règles, Flake8 bloquera le commit. Vous devrez corriger les erreurs avant de réessayer.
- Si vous avez besoin d’une configuration spécifique pour Black, isort, ou Flake8, placez les fichiers de configuration (
pyproject.toml
,.isort.cfg
,.flake8
) à la racine de votre projet.
Pour aller plus loin : le passage a Tox
Il existe un outil très adapté a l’agglomération des scripts et environnements pythons pour l’execution de contrôle dans le projet : il s’agit de tox
.
Les codes sources construit, surtout pour des languages interprétés dépendent de l’environnement d’execution cible.
On peut se prémunir d’incompatibilités et de bon fonctionnement de nos développements dans un environnement a l’aide de gestionnaires de la configuration dans l’environnement cible.
C’est un des objectif du projet tox : https://tox.wiki/en/4.12.1/index.html
Il offre une configuration des environnements virtuels ainsi qu’une matrice de compatibilité python pour vous permettre de mettre en place des tests sur différentes version de python et également de gérer des groupes de dépendances en fonction de dépendances de run / dépendances de test.
Il permet également d’aggréger les petites commandes que l’on execute dans des modules scripts, qu’on peut ensuite lancer par une simple commande.
Cela répond donc aux deux problèmes : dépendances spécifiques aux tests et execution automatisée des scripts. Mais depuis un environnement qui a le projet. (donc pour l’instant notre machine).
Nous n’irons pas plus loin sur l’implémentation, mais vous pouvez explorer cet outil, il est très utilisé dans les projets industriels python.
Tox en bref :
pip install tox
tox -e pytest
avec une configuration
[tox]
env_list =py310
minversion = 4.12.1
[testenv:unittest]
description = run the unit tests with unittest
commands =
python -m pytest ...
Automatisation des contrôles sur des dépots hébergés
Les Forges Logicielles : Gitlab, Github (etc …) proposent une infrastructure de runners et executeurs qui permettent d’éxecuter des traitements lors d’événements liés a la mise a jour, publication ou autre de votre code.
L’un des principaux usages de ces fonctionnalité, c’est de faire tourner des vérifications statiques et dynamiques de votre code. Ainsi, vous n’avez plus a penser a vos contrôles, ils s’executent a chaque nouveau commit par exemple et donc cela vous permet de voir apparaître les bugs, les régression et d’avoir des arguments objectifs pour la validation ou le rejet d’ajout du code d’un collègue.
Cela permet également l’hébergement de pages web construites (comme ce site web par exemple 🔥).
Github Actions, une introduction
GitHub Actions est un outil d’intégration et de déploiement continu (CI/CD) natif à GitHub, qui permet d’automatiser des workflows directement dans vos dépôts. Il repose sur trois concepts principaux :
les Workflows : Définissent une suite d’actions à exécuter. Ils sont configurés via des fichiers YAML placés dans le dossier
.github/workflows/
du projet sur github.les Jobs : Chaque workflow est composé de jobs qui s’exécutent dans des environnements distincts.
les Steps : Un job est lui-même décomposé en une séquence d’étapes, comprenant des actions prédéfinies ou personnalisées.
Les traitements sont executés sur des machines fournies par github, vous pouvez voir la typologie des machines ici : https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for--private-repositories
Dans le TP d’aujourd’hui et dans vos projets on privilégiera d’utiliser une machine qui est un
ubuntu-22.04
comme votre environnement personnel.
Le format des workflow est en YAML, c’est un format de fichier comme le JSON, le XML ou le CSV. On le retrouve spécifiquement pour la configuration de fichier d’intégration et de configuration car il est assez léger a la lecture : il repose comme le python sur de l’indentation.
Exemple avec un yaml “de base”.
name: Simple Python Workflow
on: [push]
jobs:
simple-check:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Run a simple Python command
run: python --version
Le principe est le suivant :
- D’une machine nue on peut executer des commandes pour lui installer ou bien lancer des commandes a partir de ce qui est déjà installé sur la machine.
Avec en plus : des jobs déjà construits par d’autres personnes c’est ce que l’on voit dans actions/checkout@v4
actions/setup-python@v5
On peut voir le code source ici pour le `actions/setup-python par exemple ici : https://github.com/actions/setup-python => https://github.com/actions/setup-python/releases/tag/v5.3.0 => https://github.com/actions/setup-python/blob/0b93645e9fea7318ecaed2b359559ac225c90a2b/src/setup-python.ts (oui c’est assez illisible mais on sait jamais)
TP : mise en place d’un workflow simple LINT et TESTS
Mettez en place sur le projet différents yaml pour la mise en place d’actions spécifiques : tests, lint
- 1 a l’aide de l’interface actions en bas a droite du projet, mettre en place une action qui lance pylint
Solution clickable partie 1
- Il faut suivre l'installation mais veiller a ce que les requirements.txt soient bien installés, pylint nécessitant un environnement fonctionnel
- Il faut ajouter l’option fail-under car par défaut c’est un seuil très bas
name: Pylint
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install Pylint
run: |
python -m pip install --upgrade pip
pip install pylint
pip install -r requirements.txt
- name: Analysing the code with isort
run: |
pylint $(git ls-files '*.py') --fail-under 5
- 2 Faire la même chose pour black, isort, flake8 cette fois ci en éditant le fichier pylint.yaml en le renommant lint.yaml
Solution clickable partie 2
name: Lint
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install Pylint Black Flake8 Isort
run: |
python -m pip install --upgrade pip
pip install pylint black flake8 isort
- name: Flake8
run: |
python -m flake8 predicat
- name: Linting with pylint
run: |
pylint $(git ls-files '*.py') --fail-under 5
- name: Formatting checks isort
run: |
python -m isort --profile black --check predicat
- name: Formatting checks black
run:
python -m black --check predicat
- 3 Faites un nouveau fichier tests.yaml où vous incorporerez les tests unitaires
Solution clickable partie 3
name: Tests
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install Deps
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
- name: Run tests
run: |
python -m unittest discover app/tests/
Un projet bilan des 2 premiers cours est disponible ici : https://github.com/conception-logicielle-ensai/archi-exemple/
Bonus: TP guidé sur la configuration de l’intégration continue sur Gitlab (gitlab-ci)

Gitlab propose une offre d’intégration continue qui se présente au global comme cela :
Vous partez d’un dépot git.
Définition d’étapes d’intégration : linting / test / build / release / deploiement (nom libre)
Précision de ces étapes en petits scripts qui correspondent aux operations que vous voulez faire et dans quel environnement vous voulez les lancer : (python avec un C++, ubuntu,…).
Un exemple
exemple pour une étape d’affichage d’un message dans la console d’un ubuntu 20.04 :
stages:
- echo
hello-world:
stage: echo
image: ubuntu:20.04
script:
- echo "hello world"
Il faut écrire ce yaml dans un fichier .gitlab-ci.yml situé à la racine du dépot git (là ou se trouve le dossier .git).
Création d’étapes
On peut effectivement créer des étapes (stages)
différentes qui fonctionneront de manière successives : d’abord echo1 puis echo2
stages:
- echo1
- echo2
hello-world:
stage: echo1
image: ubuntu:20.04
script:
- echo "hello world"
hello-world2:
stage: echo2
image: ubuntu:20.04
script:
- echo "hello world"
Dans notre cas on voudra plutôt étudier le linting puis le testing, adaptez donc les étapes.
spoiler
ça pourrait donner quelque chose de la forme :
stages:
- lint
- test
lint-python-application:
stage: lint
image: ubuntu:20.04
script:
- echo "hello world"
test-python-application:
stage: test
image: ubuntu:20.04
script:
- echo "hello world"
Mise a niveau des scripts
Le champ script attend une liste de scripts suivis les un des autres avec des tirets :
stages:
- echo1
- echo2
somme-2-2:
stage: echo1
image: ubuntu:20.04
script:
- echo "2+2="
- echo "2+2=4"
somme-4-4:
stage: echo2
image: ubuntu:20.04
script:
- echo "4+4="
- echo "8"
Adaptez donc en rajoutant autant d’étapes que nécessaires pour partir d’un ubuntu et de votre dépot git, avoir un environnement avec pip, installer les dépendances, lancer les tests (toujours en echo)
spoiler
ça pourrait donner quelque chose de la forme :
stages:
- lint
- test
lint-python-application:
stage: lint
image: ubuntu:20.04
script:
- echo "recuperer pip"
- echo "installer les dependances"
- echo "lancer le linting"
test-python-application:
stage: test
image: ubuntu:20.04
script:
- echo "recuperer pip"
- echo "installer les dependances"
- echo "lancer les tests"
:boom: Ajoutez donc tout cela a votre dépot git via un add/commit/push
Finalisation
Précedemment, on a vu que via la ligne de commande on pouvait effectivement lancer les tests et le lint dans un environnement qui contient python3 et pip.
Il faut donc adapter les scripts pour, en partant d’un ubuntu version 20.04, récupérer le packet python3-pip puis faire les opérations en ligne de commande successives dans le script permettant de faire en sorte que du linting soit lancé et que des tests le soient également.
:warning: Pour l’usage d’apt : utilisez l’option -y, cela permet de dire oui à tout et donc de permettre le téléchargement sans encombre.
Faites le nécessaire en changeant les lignes de script nécessaires. Comme vous le feriez sur un ubuntu en partant de 0.
Cela donnerait au final :
stages:
- lint
- test
lint-python-application:
stage: lint
image: ubuntu:20.04
script:
- apt update
- apt install -y python3-pip
- pip3 install pylint
- find . -type f -name "*.py" | xargs pylint
check-format-python-application:
stage: lint
image: ubuntu:20.04
script:
- apt update
- apt install -y python3-pip
- pip3 install black isort
- black --check .
- isort --profile black .
test-python-application:
stage: test
image: ubuntu:20.04
script:
- apt update
- apt install -y python3-pip
- python3 -m unittest discover