Aller au contenu
Outils medium

Cookiecutter : générer des projets standardisés en 10 secondes

12 min de lecture

Cookiecutter génère une structure de projet complète en une commande. Vous choisissez un template (local ou GitHub), répondez à quelques questions, et obtenez un projet prêt à coder, cohérent d'un développeur à l'autre.

Ce guide s'adresse aux développeurs et équipes DevOps qui répètent le même démarrage de projet. Vous allez installer Cookiecutter, utiliser des templates existants, créer le vôtre (avec cookiecutter.json et Jinja2), et automatiser des tâches avec des hooks.

  • Installer Cookiecutter proprement avec pipx.
  • Générer un projet depuis un template GitHub ou local.
  • Écrire votre propre template avec cookiecutter.json et Jinja2.
  • Conditionner fichiers et contenus selon les réponses.
  • Automatiser avec des hooks (init Git, validation).

Démarrer un nouveau projet implique toujours les mêmes tâches :

  • Créer la structure de dossiers
  • Ajouter les fichiers de configuration (.gitignore, pyproject.toml, Dockerfile)
  • Copier du code boilerplate depuis un ancien projet
  • Renommer les variables, nettoyer les références au projet précédent

Ce processus prend 30 minutes à 2 heures selon la complexité. Pire, chaque développeur fait différemment, créant des incohérences dans l'équipe.

Cookiecutter automatise tout cela :

Sans CookiecutterAvec Cookiecutter
Copier-coller d'un ancien projetUne commande génère la structure
Rechercher/remplacer les nomsVariables pré-remplies automatiquement
Oublier des fichiers de configTemplate complet et cohérent
30 min à 2h par projet10 secondes

Version actuelle : 2.7.1 (mars 2026). Les versions 2.7.x corrigent des vulnérabilités des branches antérieures : gardez Cookiecutter à jour, surtout si vous exécutez des templates tiers.

Cookiecutter est écrit en Python. L'installation recommandée utilise pipx pour isoler les dépendances.

  1. Installer pipx (si pas déjà fait)

    Fenêtre de terminal
    # Ubuntu/Debian
    sudo apt install pipx
    pipx ensurepath
    # Ou avec pip
    pip install --user pipx
    pipx ensurepath
  2. Installer Cookiecutter

    Fenêtre de terminal
    pipx install cookiecutter

    Sortie attendue :

    done! ✨ 🌟 ✨
    installed package cookiecutter 2.7.1, installed using Python 3.12.3
    These apps are now globally available
    - cookiecutter
  3. Vérifier l'installation

    Fenêtre de terminal
    cookiecutter --version
    # Cookiecutter 2.7.1

La façon la plus simple d'utiliser Cookiecutter : pointer vers un template GitHub.

Fenêtre de terminal
cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage

Cookiecutter clone le dépôt, lit le fichier cookiecutter.json, et vous pose les questions :

full_name [Audrey Roy Greenfeld]: Stéphane Robert
email [audreyr@example.com]: me@stephane-robert.info
project_name [Python Boilerplate]: Mon Super Projet
project_slug [mon_super_projet]:
...

Le projet est généré dans le dossier courant :

Fenêtre de terminal
ls mon_super_projet/
# CODE_OF_CONDUCT.md docs/ LICENSE pyproject.toml README.md src/ tests/

Pour les pipelines CI/CD ou les scripts, utilisez --no-input :

Fenêtre de terminal
cookiecutter https://github.com/audreyfeldroy/cookiecutter-pypackage --no-input

Cookiecutter utilise les valeurs par défaut du template.

Les paires clé=valeur placées après l'URL du template surchargent les valeurs par défaut du cookiecutter.json. Elles se combinent avec --no-input : les clés fournies prennent votre valeur, les autres gardent celle du template, et aucune question n'est posée. C'est la forme à privilégier en CI/CD, car elle rend la commande reproductible et lisible dans les journaux. Un nom de clé absent du cookiecutter.json est ignoré silencieusement, sans avertissement : vérifiez l'orthographe des variables avant de vous étonner du résultat.

Fenêtre de terminal
cookiecutter https://github.com/user/template \
project_name="Mon Projet" \
author_name="Stéphane"

--checkout s'applique après le git clone du template et accepte une branche, un tag ou un commit. C'est la seule façon de garantir qu'une génération donne le même résultat dans six mois : sans cette option, Cookiecutter suit la branche par défaut du dépôt, qui évolue sans prévenir. Épinglez un tag pour la lisibilité, un commit quand vous voulez une garantie absolue, un tag pouvant être redéplacé par le mainteneur.

Fenêtre de terminal
cookiecutter https://github.com/user/template --checkout v1.0.0

Cookiecutter met en cache les templates. Pour les lister :

Fenêtre de terminal
cookiecutter --list-installed
# 1 installed templates:
# * cookiecutter-pypackage

Pour réutiliser un template déjà cloné :

Fenêtre de terminal
cookiecutter cookiecutter-pypackage

Au cœur de chaque template, ce fichier JSON définit les variables et leurs valeurs par défaut.

{
"project_name": "Mon Projet",
"author_name": "Stéphane Robert",
"version": "0.1.0",
"description": "Un projet Python"
}

Chaque clé devient une variable utilisable dans les fichiers du template avec la syntaxe Jinja2 : {{ cookiecutter.project_name }}.

{
"license": ["MIT", "GPLv3", "Apache 2.0", "BSD-3-Clause"],
"use_docker": ["yes", "no"]
}

L'utilisateur sélectionne parmi les options. La première option est la valeur par défaut.

{
"project_name": "Mon Super Projet",
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_') }}"
}

project_slug est calculé automatiquement à partir de project_name.

  • Répertoiremon-template/
    • cookiecutter.json
    • Répertoire{{cookiecutter.project_name}}/
      • README.md
      • .gitignore

Le dossier {{cookiecutter.project_name}} sera renommé avec la valeur saisie par l'utilisateur.

Créons un template pour un projet avec Docker et CI/CD :

  1. Créer la structure

    Fenêtre de terminal
    mkdir -p mon-template/\{\{cookiecutter.project_name\}\}/.github/workflows
    cd mon-template
  2. Créer cookiecutter.json

    Fenêtre de terminal
    cat > cookiecutter.json << 'EOF'
    {
    "project_name": "my-app",
    "author_name": "Stéphane Robert",
    "description": "Application DevOps",
    "python_version": ["3.12", "3.11", "3.10"],
    "include_docker": ["yes", "no"],
    "include_ci": ["yes", "no"]
    }
    EOF
  3. Créer les fichiers du template

    {{cookiecutter.project_name}}/README.md :

    # {{ cookiecutter.project_name }}
    {{ cookiecutter.description }}
    ## Auteur
    {{ cookiecutter.author_name }}
    ## Installation
    ```bash
    pip install -e .
    ```

    {{cookiecutter.project_name}}/.gitignore :

    __pycache__/
    *.pyc
    .env
    .venv/
    dist/
    *.egg-info/
  4. Tester le template

    Fenêtre de terminal
    cd ..
    cookiecutter mon-template/

Utilisez Jinja2 dans les noms de fichiers pour créer des fichiers conditionnels :

  • Répertoiremon-template/
    • cookiecutter.json
    • Répertoire{{cookiecutter.project_name}}/
      • README.md
      • {{"Dockerfile" if cookiecutter.include_docker == "yes" else ""}}
      • Répertoire{{".github" if cookiecutter.include_ci == "yes" else ""}}/
        • Répertoireworkflows/
          • ci.yml

Le Dockerfile n'est créé que si l'utilisateur répond "yes" à include_docker.

Dans un fichier, utilisez les conditions Jinja2 :

# {{ cookiecutter.project_name }}
{{ cookiecutter.description }}
{% if cookiecutter.include_docker == "yes" %}
## Docker
Build l'image :
```bash
docker build -t {{ cookiecutter.project_name }} .
```
{% endif %}
{% if cookiecutter.include_ci == "yes" %}
## CI/CD
Ce projet utilise GitHub Actions. Voir `.github/workflows/ci.yml`.
{% endif %}

Les hooks sont des scripts Python exécutés à différents moments de la génération.

HookMoment d'exécutionUsage typique
pre_prompt.pyAvant les questionsVérifier des prérequis
pre_gen_project.pyAvant générationValider les réponses
post_gen_project.pyAprès générationInitialiser Git, installer deps

Les hooks se placent dans le dossier hooks/ à la racine du template :

  • Répertoiremon-template/
    • cookiecutter.json
    • Répertoirehooks/
      • pre_gen_project.py
      • post_gen_project.py
    • Répertoire{{cookiecutter.project_name}}/
      • ...

hooks/post_gen_project.py :

import subprocess
import os
def main():
project_path = os.getcwd()
# Initialiser Git
subprocess.run(["git", "init"], cwd=project_path, check=True)
subprocess.run(["git", "add", "."], cwd=project_path, check=True)
subprocess.run(
["git", "commit", "-m", "Initial commit from Cookiecutter"],
cwd=project_path,
check=True
)
print("✓ Dépôt Git initialisé avec le premier commit")
if __name__ == "__main__":
main()

hooks/pre_gen_project.py :

import re
import sys
def main():
project_name = "{{ cookiecutter.project_name }}"
if not re.match(r"^[a-zA-Z][a-zA-Z0-9_-]*$", project_name):
print("ERREUR: Le nom du projet doit commencer par une lettre")
print(" et ne contenir que lettres, chiffres, tirets ou underscores.")
sys.exit(1)
if __name__ == "__main__":
main()

Contrairement à une idée répandue, Cookiecutter exécute les hooks sans rien demander : la valeur par défaut de --accept-hooks est yes. Le mode ask, qui affiche la question Do you want to execute hooks?, doit être demandé explicitement. Un hook est un script Python arbitraire livré avec le template : lancer un template GitHub inconnu revient donc à exécuter le code de son auteur sur votre poste, avec vos droits.

Fenêtre de terminal
# Toujours exécuter (comportement par défaut)
cookiecutter template --accept-hooks yes
# Demander confirmation avant chaque exécution
cookiecutter template --accept-hooks ask
# Jamais exécuter
cookiecutter template --accept-hooks no

Ce tableau retient les options les plus utiles au quotidien ; cookiecutter --help affiche la liste complète, notamment --directory pour un dépôt hébergeant plusieurs templates et --keep-project-on-failure pour inspecter un dossier partiellement généré après une erreur. Deux options sont mutuellement exclusives : --no-input et --replay refusent de cohabiter, la première ignore les réponses enregistrées tandis que la seconde les rejoue. Attention aussi à -f (--overwrite-if-exists), qui écrase les fichiers du dossier cible sans confirmation : -s (--skip-if-file-exists) est le comportement prudent quand vous régénérez par-dessus un projet existant.

OptionDescription
-V, --versionAffiche la version
--no-inputUtilise les valeurs par défaut
-c, --checkoutClone une branche/tag spécifique
-o, --output-dirDossier de destination
-f, --overwrite-if-existsÉcrase si le projet existe
-s, --skip-if-file-existsIgnore les fichiers existants
-l, --list-installedListe les templates en cache
--replayRéutilise les réponses précédentes
--accept-hooksyes|ask|no pour les hooks
TemplateDescriptionLien
cookiecutter-djangoDjango production-ready avec DockerGitHub
cookiecutter-pypackagePackage Python avec tests et CIGitHub
cookiecutter-ansible-roleRôle Ansible avec moléculeGitHub
cookiecutter-flaskFlask avec API REST et DockerGitHub

Pour définir des valeurs par défaut globales, créez ~/.cookiecutterrc :

default_context:
full_name: "Stéphane Robert"
email: "me@stephane-robert.info"
github_username: "stephrobert"
abbreviations:
gh: https://github.com/\{0\}.git
bb: https://bitbucket.org/\{0\}

Maintenant, utilisez les abréviations :

Fenêtre de terminal
cookiecutter gh:audreyfeldroy/cookiecutter-pypackage

Les questions ci-dessous reprennent les points d'entrée du guide : ce qu'est réellement Cookiecutter, comment écrire son propre template, sa portée au-delà de Python, et son usage en CI/CD. Un point qu'elles ne couvrent pas et qui surprend souvent : un projet généré ne reste pas lié à son template. Cookiecutter fait une génération ponctuelle, aucune commande ne propage ensuite les évolutions du template vers les projets déjà créés.

  1. Cookiecutter = générateur de projets : une commande génère une structure complète
  2. Templates GitHub : cookiecutter https://github.com/user/template
  3. cookiecutter.json définit les variables et valeurs par défaut
  4. Jinja2 pour le contenu dynamique : {{ cookiecutter.variable }}
  5. Fichiers conditionnels : nommez {{"Dockerfile" if var == "yes" else ""}}
  6. Hooks : scripts Python exécutés avant/après génération
  7. --no-input pour les pipelines CI/CD
  8. ~/.cookiecutterrc pour vos valeurs par défaut globales

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn