Intégrer pre-commit au cycle de vie du devcontainer
Ce module dépend de 2 et 3, pas de 4
Rappel important : la configuration pre-commit que vous branchez ici est celle, fonctionnelle, du module 3 — pas la fixture volontairement cassée du module 4, qui n'était qu'un exercice de diagnostic. Le projet de ce module est mon-script-python, avec le .pre-commit-config.yaml corrigé et entièrement vert à la fin du module 3 (check-yaml, detect-private-key, black, flake8).
L'objectif
Jusqu'ici, activer pre-commit sur un projet supposait de taper pre-commit install soi-même après avoir cloné le dépôt — une étape manuelle de plus, facile à oublier, invisible dans un README que personne ne relit. L'objectif de ce module : que pre-commit install s'exécute automatiquement à la création du conteneur, pour que n'importe qui ouvrant ce Dev Container ait les hooks actifs sans rien faire de plus.
Vérifier d'abord s'il existe une Feature officielle
Même réflexe qu'au module 2 : avant d'écrire quoi que ce soit à la main, on vérifie si une Dev Container Feature officielle couvre déjà ce besoin. Une recherche sur le registre officiel (containers.dev/features) pour « pre-commit » remonte plusieurs Features, mais aucune sous le namespace officiel ghcr.io/devcontainers/features/ — uniquement des implémentations tierces maintenues par des contributeurs individuels, sous leurs propres namespaces (par exemple ghcr.io/prulloac/devcontainer-features/pre-commit ou ghcr.io/hspaans/devcontainer-features/pre-commit).
Ces Features tierces peuvent très bien fonctionner, mais ce module ne les utilise pas : elles ajoutent une dépendance à un mainteneur externe non affilié à Microsoft ou au projet Dev Container Spec, pour un gain minime par rapport à deux commandes shell dans postCreateCommand. La règle du module 2 s'applique dans les deux sens : ne pas fabriquer une référence de Feature qu'on n'a pas vérifiée, mais aussi ne pas en utiliser une simplement parce qu'elle existe si une alternative plus simple et plus transparente couvre le même besoin.
Le devcontainer.json de mon-script-python
Ce projet n'avait pas encore de .devcontainer/devcontainer.json — vous en écrivez un maintenant, en réutilisant directement les compétences du module 1 :
{
"name": "Mon script Python",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"postCreateCommand": "pip install pre-commit && pre-commit install"
}
image
mcr.microsoft.com/devcontainers/python:3.12 : une image officielle avec Python 3.12 préinstallé, cohérente avec le langage du projet (contrairement au module 2, ici pas besoin d'ajouter Python via une Feature — il est natif à cette image de base).
postCreateCommand
Deux commandes chaînées avec && :
pip install pre-commit— installe l'outil pre-commit lui-même dans le conteneur (ce n'est pas une dépendance du projet,pre-commitn'a donc pas besoin d'unrequirements.txt).pre-commit install— écrit le hook Git.git/hooks/pre-commitdans le dépôt. C'est cette seconde commande qui active concrètement l'interception des commits.
Le && garantit que pre-commit install ne s'exécute que si l'installation de pre-commit a réussi — pas d'exécution silencieuse dans un état à moitié configuré.
pre-commit install a besoin d'un dépôt Git déjà initialisé (.git/) pour savoir où écrire le hook : c'est le cas ici, puisque git init a été fait dès le module 3.
Rebuild sans cache
Pour prouver que l'automatisation fonctionne réellement — pas seulement parce que vous aviez déjà lancé pre-commit install manuellement dans une session précédente — repartez d'un état complètement neuf. Depuis la palette de commandes : Dev Containers: Rebuild Without Cache and Reopen in Container.
Contrairement à Rebuild Container, cette commande force Docker à reconstruire chaque couche de l'image sans réutiliser quoi que ce soit du cache existant, et relance postCreateCommand depuis un conteneur qui n'a strictement rien d'installé au préalable — la situation la plus proche possible de la machine d'un nouveau contributeur.
La preuve : un commit qui viole une règle, bloqué sans rien taper de plus
Une fois le rebuild terminé, ouvrez un terminal intégré — sans lancer pre-commit install vous-même, l'objectif est justement de vérifier que ce n'est plus nécessaire. Réintroduisez volontairement un défaut dans app.py :
import os
def greet(name):
x = 1
print( "hello, "+name )
greet('world')
Puis tentez le commit :
git add app.py
git commit -m "test violating commit"
Si git réclame une identité (Please tell me who you are) au lieu de lancer les hooks, c'est indépendant de pre-commit : configurez-la une fois avec git config --global user.email "vous@exemple.com" et git config --global user.name "Votre nom", puis relancez le commit.
Sortie réelle obtenue :
check yaml...............................................................Passed
detect private key.......................................................Passed
black....................................................................Failed
- hook id: black
- files were modified by this hook
reformatted app.py
All done! ✨ 🍰 ✨
1 file reformatted.
flake8...................................................................Failed
- hook id: flake8
- exit code: 1
app.py:1:1: F401 'os' imported but unused
app.py:5:5: F841 local variable 'x' is assigned to but never used
Le commit est refusé (code de sortie non nul, aucun nouveau commit créé) : les hooks se sont déclenchés automatiquement au moment du git commit, sans qu'aucune commande pre-commit install n'ait été tapée manuellement après le rebuild. C'est exactement le comportement recherché — corrigez app.py comme au module 3, relancez le commit, et il passe.
Ce que vous venez de faire
Vous avez fermé la boucle entre les deux fils de ce parcours : le mécanisme du devcontainer (postCreateCommand, maîtrisé au module 1) sert maintenant à activer automatiquement la configuration pre-commit (écrite au module 3) pour chaque personne qui ouvre ce projet — zéro étape manuelle, zéro README à lire, et une preuve par le comportement réel du terminal plutôt que par la confiance. C'est la même philosophie que celle du tout premier module du parcours Débutant : ne jamais croire qu'un environnement fonctionne, le prouver par une commande.
Vérifiez votre compréhension
Ce module s'appuie sur quels modules précédents ?
Pourquoi ce module utilise-t-il postCreateCommand plutôt qu'une Dev Container Feature pour installer pre-commit ?
Quelle est la preuve que l'intégration fonctionne réellement, sans étape manuelle oubliée ?
Envie d'être prévenu des prochains modules ?
L'Académie reste gratuite et en accès libre, sans inscription. Si vous voulez juste être averti par email à la sortie d'un nouveau module, c'est ici — aucune obligation, désinscription en un clic.