Dans ce guide, j’adopte une approche différente : plutôt que de lister la syntaxe de façon abstraite, nous allons construire ensemble un projet concret du début à la fin. À chaque étape, j’introduis un nouveau concept du langage Nix que nous appliquons immédiatement.
Nous allons créer un environnement de développement Python complet avec :
- Python 3.12 avec des bibliothèques spécifiques
- Des outils de développement (git, vim, ripgrep)
- Des variables d’environnement personnalisées
- Une configuration modulaire et réutilisable
À la fin de ce guide, vous aurez un shell.nix fonctionnel et vous
comprendrez chaque ligne.
Prérequis
Section intitulée « Prérequis »- Nix installé sur votre système
- Un terminal Linux
Qu’est-ce qu’un fichier shell.nix ?
Section intitulée « Qu’est-ce qu’un fichier shell.nix ? »Un fichier shell.nix est une recette déclarative qui décrit un
environnement de développement. Quand vous lancez nix-shell dans un dossier
contenant ce fichier, Nix :
- Lit le fichier
shell.nix - Télécharge les paquets nécessaires (s’ils ne sont pas en cache)
- Crée un shell isolé avec exactement ces outils disponibles
- Vous place dans cet environnement temporaire
Pourquoi utiliser shell.nix ?
Section intitulée « Pourquoi utiliser shell.nix ? »| Problème classique | Solution avec shell.nix |
|---|---|
| ”Ça marche sur ma machine” | Environnement identique pour tous |
| Conflits de versions entre projets | Chaque projet a son propre environnement |
| Installation manuelle d’outils | Un seul nix-shell suffit |
| Documentation des dépendances | Le fichier shell.nix est la documentation |
Différence avec les environnements virtuels Python
Section intitulée « Différence avec les environnements virtuels Python »Contrairement à venv qui ne gère que Python, shell.nix peut inclure
n’importe quel outil : compilateurs, bases de données, outils CLI, etc.
Et contrairement à Docker, il n’y a pas de conteneur : les outils sont
directement accessibles dans votre terminal.
Étape 1 : Premier shell.nix minimal
Section intitulée « Étape 1 : Premier shell.nix minimal »Créons notre premier fichier dans un nouveau dossier :
mkdir mon-projet-python && cd mon-projet-pythonCréez un fichier shell.nix avec ce contenu :
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell { packages = [ pkgs.python312 ];}Testons immédiatement :
nix-shellpython3 --version # Python 3.12.xexit # Sortir du shellÇa fonctionne ! Mais qu’avons-nous écrit exactement ? Décortiquons chaque élément dans les sections suivantes.
Étape 2 : Comprendre les fonctions
Section intitulée « Étape 2 : Comprendre les fonctions »Rappel de notre code
Section intitulée « Rappel de notre code »Regardons à nouveau notre shell.nix :
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell { packages = [ pkgs.python312 ];}La première ligne { pkgs ? import <nixpkgs> {} }: est mystérieuse. C’est
en fait une fonction. Pourquoi ? Parce que Nix a besoin de savoir où
trouver les paquets comme python312. On lui dit : “donne-moi pkgs et
je te construis un environnement”.
Les fonctions en Nix : la base
Section intitulée « Les fonctions en Nix : la base »En Nix, une fonction s’écrit avec : qui sépare l’argument du corps :
argument: ce_que_la_fonction_retourneExemple concret - une fonction qui double un nombre :
x: x * 2xest l’argument (le paramètre d’entrée)x * 2est le corps (ce qui est calculé et retourné)
Pour appeler cette fonction, on lui passe une valeur :
nix replnix-repl> double = x: x * 2nix-repl> double 510nix-repl> double 100200Fonctions avec plusieurs arguments
Section intitulée « Fonctions avec plusieurs arguments »Nix ne supporte qu’un argument par fonction, mais on peut les chaîner :
a: b: a + bCela signifie : “prends a, puis retourne une fonction qui prend b et
retourne a + b”.
nix-repl> add = a: b: a + bnix-repl> add 3 47Les arguments nommés (notre cas)
Section intitulée « Les arguments nommés (notre cas) »Notre shell.nix utilise une syntaxe avec accolades { } :
{ pkgs }:Cela signifie : “j’attends un dictionnaire (attrset) qui contient une
clé pkgs”. C’est plus lisible quand il y a plusieurs arguments :
{ nom, age, ville }: "Je m'appelle ${nom}, j'ai ${toString age} ans, j'habite à ${ville}"La valeur par défaut avec ?
Section intitulée « La valeur par défaut avec ? »Le ? définit une valeur utilisée si l’argument n’est pas fourni :
{ pkgs ? import <nixpkgs> {} }:Traduction : “Si on me donne pkgs, je l’utilise. Sinon, je charge moi-même
nixpkgs avec import <nixpkgs> {}.”
Pourquoi notre shell.nix est une fonction ?
Section intitulée « Pourquoi notre shell.nix est une fonction ? »Quand vous tapez nix-shell, voici ce qui se passe :
- Nix lit le fichier
shell.nix - Il détecte que c’est une fonction (à cause du
:) - Il l’appelle avec
{}(un dictionnaire vide) - Comme
pkgsn’est pas fourni, la valeur par défautimport <nixpkgs> {}est utilisée - La fonction retourne le résultat de
pkgs.mkShell { ... }
C’est pourquoi cette première ligne est standard dans presque tous les fichiers Nix. Elle dit simplement : “charge les paquets disponibles”.
Étape 3 : Comprendre import et nixpkgs
Section intitulée « Étape 3 : Comprendre import et nixpkgs »Qu’est-ce que import ?
Section intitulée « Qu’est-ce que import ? »import charge et évalue un fichier Nix. C’est comme un require ou include
dans d’autres langages :
# Si config.nix contient { port = 8080; }let config = import ./config.nix;in config.port # 8080Qu’est-ce que <nixpkgs> ?
Section intitulée « Qu’est-ce que <nixpkgs> ? »<nixpkgs> est un chemin spécial qui pointe vers la collection de paquets
Nix installée sur votre système (via les channels). C’est un raccourci pour
accéder aux ~80 000 paquets disponibles.
import <nixpkgs> {}Cette expression :
- Charge le fichier
default.nixde nixpkgs - L’appelle avec
{}(options vides) - Retourne un attrset gigantesque contenant tous les paquets
C’est pourquoi ensuite, on peut écrire pkgs.python312, pkgs.git, etc.
Testons dans le REPL
Section intitulée « Testons dans le REPL »Le REPL (Read-Eval-Print Loop) est un terminal interactif qui permet de tester des expressions Nix à la volée. Vous tapez une expression, Nix l’évalue et affiche le résultat. C’est idéal pour expérimenter :
nix replnix-repl> pkgs = import <nixpkgs> {}nix-repl> pkgs.python312«derivation /nix/store/...-python3-3.12...»nix-repl> pkgs.git«derivation /nix/store/...-git-...»Le texte «derivation ...» signifie que c’est un paquet Nix prêt à être
construit ou utilisé. Tapez :q pour quitter le REPL.
Étape 4 : Comprendre les attrsets
Section intitulée « Étape 4 : Comprendre les attrsets »Structure fondamentale
Section intitulée « Structure fondamentale »Un attrset (attribute set) est un dictionnaire clé-valeur. C’est la structure de données la plus importante en Nix :
{ name = "Alice"; age = 30; active = true;}On accède aux valeurs avec un point :
nix replnix-repl> personne = { name = "Alice"; age = 30; active = true; }nix-repl> personne.name"Alice"nix-repl> personne.age30nix-repl> personne.activetrue:qNotre mkShell est un attrset
Section intitulée « Notre mkShell est un attrset »Regardons à nouveau notre code :
pkgs.mkShell { packages = [ pkgs.python312 ];}pkgs.mkShell est une fonction qui attend un attrset en argument. Nous lui
passons { packages = [ pkgs.python312 ]; }.
Étape 5 : Ajouter plus de paquets
Section intitulée « Étape 5 : Ajouter plus de paquets »Enrichissons notre environnement avec git, vim et ripgrep :
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell { packages = [ pkgs.python312 pkgs.git pkgs.vim pkgs.ripgrep ];}Rechargez l’environnement :
nix-shellgit --version # Disponible !vim --version # Disponible !rg --version # Disponible !Simplifier avec with
Section intitulée « Simplifier avec with »Répéter pkgs. devient fastidieux. Le mot-clé with importe tous les
attributs d’un attrset dans le scope :
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell { packages = with pkgs; [ python312 git vim ripgrep ];}C’est équivalent, mais plus lisible. with pkgs; signifie “dans la suite,
cherche d’abord dans pkgs”.
Étape 6 : Variables locales avec let
Section intitulée « Étape 6 : Variables locales avec let »Pourquoi des variables ?
Section intitulée « Pourquoi des variables ? »Imaginons que nous voulions définir le nom du projet et sa version à un seul
endroit. Le bloc let ... in crée des variables locales :
{ pkgs ? import <nixpkgs> {} }:
let projectName = "mon-api"; pythonVersion = pkgs.python312;inpkgs.mkShell { name = projectName; packages = with pkgs; [ pythonVersion git vim ];}Syntaxe de let
Section intitulée « Syntaxe de let »let variable1 = valeur1; variable2 = valeur2;in expression_qui_utilise_les_variablesVariables qui dépendent d’autres variables
Section intitulée « Variables qui dépendent d’autres variables »Les variables peuvent référencer celles définies avant :
let a = 1; b = 2; sum = a + b; # 3in sum * 2 # 6Étape 7 : Listes de paquets Python
Section intitulée « Étape 7 : Listes de paquets Python »Le problème
Section intitulée « Le problème »Nous voulons Python avec des bibliothèques comme requests, flask ou
pytest. Mais pkgs.python312 est Python “nu” sans bibliothèques.
La solution : withPackages
Section intitulée « La solution : withPackages »nixpkgs fournit une fonction python312.withPackages qui crée un Python
avec les bibliothèques demandées :
{ pkgs ? import <nixpkgs> {} }:
let # Python avec bibliothèques pythonEnv = pkgs.python312.withPackages (ps: [ ps.requests ps.flask ps.pytest ]);inpkgs.mkShell { packages = with pkgs; [ pythonEnv git vim ripgrep ];}Testons :
nix-shellpython3 -c "import requests; print(requests.__version__)"# 2.31.0 (ou version similaire)Comprendre ps: […]
Section intitulée « Comprendre ps: […] »ps: [ ps.requests ps.flask ] est une fonction anonyme (lambda) :
psest l’argument (les paquets Python disponibles)[ ps.requests ps.flask ]est le corps (la liste retournée)
C’est équivalent à écrire :
(ps: [ ps.requests ps.flask ps.pytest ])Cette fonction est appelée par withPackages qui lui passe tous les paquets
Python disponibles.
Étape 8 : Variables d’environnement
Section intitulée « Étape 8 : Variables d’environnement »shellHook pour le démarrage
Section intitulée « shellHook pour le démarrage »shellHook est un script shell exécuté à l’entrée dans nix-shell :
{ pkgs ? import <nixpkgs> {} }:
let pythonEnv = pkgs.python312.withPackages (ps: [ ps.requests ps.flask ps.pytest ]);inpkgs.mkShell { packages = with pkgs; [ pythonEnv git vim ripgrep ];
shellHook = "echo '🐍 Environnement Python activé !' && echo \"Python: $(python3 --version)\"";}Chaînes multilignes
Section intitulée « Chaînes multilignes »En Nix, les chaînes multilignes utilisent deux apostrophes simples consécutives. Voici l’équivalent avec une string standard :
# String simple avec \n"Première ligne\nDeuxième ligne"
# En shellHook, combinez plusieurs echo :shellHook = "echo 'Ligne 1' && echo 'Ligne 2'";L’indentation minimale commune est automatiquement supprimée dans les chaînes multilignes Nix.
Variables d’environnement personnalisées
Section intitulée « Variables d’environnement personnalisées »Ajoutez des variables avec les attributs de l’attrset :
{ pkgs ? import <nixpkgs> {} }:
let pythonEnv = pkgs.python312.withPackages (ps: [ ps.requests ps.flask ps.pytest ]);inpkgs.mkShell { packages = with pkgs; [ pythonEnv git vim ripgrep ];
# Variables d'environnement PROJECT_NAME = "mon-api"; FLASK_ENV = "development"; FLASK_DEBUG = "1";
shellHook = "echo '🐍 Projet:' $PROJECT_NAME && echo 'Flask: mode' $FLASK_ENV";}Testons :
nix-shellecho $PROJECT_NAME # mon-apiecho $FLASK_ENV # developmentÉtape 9 : Interpolation de chaînes
Section intitulée « Étape 9 : Interpolation de chaînes »Syntaxe ${…}
Section intitulée « Syntaxe ${…} »L’interpolation permet d’insérer des expressions dans des chaînes :
let name = "Alice";in "Bonjour, ${name} !"# "Bonjour, Alice !"Conversion de types
Section intitulée « Conversion de types »Seules les chaînes peuvent être interpolées. Pour les nombres, utilisez
toString :
let port = 8080;in "Serveur sur le port ${toString port}"# "Serveur sur le port 8080"Application à notre projet
Section intitulée « Application à notre projet »{ pkgs ? import <nixpkgs> {} }:
let projectName = "mon-api"; projectVersion = "1.0.0";
pythonEnv = pkgs.python312.withPackages (ps: [ ps.requests ps.flask ]);inpkgs.mkShell { name = "${projectName}-dev";
packages = with pkgs; [ pythonEnv git ];
PROJECT_NAME = projectName; PROJECT_VERSION = projectVersion;
shellHook = "echo '📦' ${projectName} v${projectVersion} && echo \"Python: $(python3 --version)\"";}Étape 10 : Figer les versions (reproductibilité)
Section intitulée « Étape 10 : Figer les versions (reproductibilité) »Le problème avec <nixpkgs>
Section intitulée « Le problème avec <nixpkgs> »Jusqu’ici, nous utilisons import <nixpkgs> {}. Le problème ? <nixpkgs>
pointe vers le channel système qui change à chaque mise à jour. Aujourd’hui
vous avez Python 3.12.3, demain peut-être 3.12.4.
Pour un environnement vraiment reproductible, il faut figer la version de nixpkgs.
Solution 1 : Figer avec fetchTarball
Section intitulée « Solution 1 : Figer avec fetchTarball »{ pkgs ? import (fetchTarball { # nixpkgs 24.05 - figé à ce commit url = "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz"; sha256 = "sha256:1lr1h35prqkd1mkmzriwlpvxcb34kmhc9dnr48gkm8hh089hifmx"; }) {}}:
pkgs.mkShell { packages = [ pkgs.python312 ];}Maintenant, peu importe quand ou où vous lancez nix-shell, vous aurez
exactement la même version de Python.
Comment trouver le sha256 ?
Section intitulée « Comment trouver le sha256 ? »Deux méthodes :
Méthode 1 : Laissez Nix le calculer (mettez une valeur bidon) :
sha256 = ""; # Nix affichera le bon hash dans l'erreurMéthode 2 : Utilisez nix-prefetch-url :
nix-prefetch-url --unpack https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gzSolution 2 : Figer avec un commit précis
Section intitulée « Solution 2 : Figer avec un commit précis »Pour encore plus de précision, utilisez un commit spécifique :
{ pkgs ? import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/5ef6c425980847c78a80d759abc476e941a9bf42.tar.gz"; sha256 = "sha256:0123456789abcdef..."; }) {}}:
pkgs.mkShell { packages = [ pkgs.python312 ];}Quelle version d’un paquet ?
Section intitulée « Quelle version d’un paquet ? »Pour voir quelle version d’un paquet est disponible :
# Dans le REPLnix replnix-repl> pkgs = import <nixpkgs> {}nix-repl> pkgs.python312.version"3.12.12"nix-repl> pkgs.nodejs.version"22.20.0"Trouver un paquet avec une version spécifique
Section intitulée « Trouver un paquet avec une version spécifique »Le site Nixhub.io permet de rechercher quelle version de nixpkgs contient une version précise d’un paquet.
Par exemple, si vous avez besoin de Python 3.11.4 exactement, Nixhub vous indiquera quel commit de nixpkgs utiliser.
Limites de cette approche
Section intitulée « Limites de cette approche »Cette méthode fonctionne, mais elle a des inconvénients :
- Pas de fichier de verrouillage partageable facilement
- Mise à jour manuelle des hashes
- Pas de gestion des dépendances entre projets
C’est pourquoi les flakes ont été créés : ils résolvent ces problèmes
avec un fichier flake.lock automatique. Un guide dédié aux flakes sera
bientôt disponible.
Syntaxe if-then-else
Section intitulée « Syntaxe if-then-else »En Nix, if est une expression qui retourne une valeur :
if condition then valeur1 else valeur2Cas pratique : paquets conditionnels
Section intitulée « Cas pratique : paquets conditionnels »Ajoutons des outils de debug uniquement en mode développement. Désormais, nous utilisons une version figée de nixpkgs :
{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }:
let debugMode = true; # Changez à false pour production
pythonEnv = pkgs.python312.withPackages (ps: [ ps.requests ps.flask ]);
# Outils de debug conditionnels debugTools = if debugMode then [ pkgs.htop pkgs.ncdu ] else [];inpkgs.mkShell { packages = with pkgs; [ pythonEnv git ] ++ debugTools; # ++ concatène les listes
shellHook = (if debugMode then "echo '🔧 Mode DEBUG activé'\n" else "");}L’opérateur ++ (concaténation)
Section intitulée « L’opérateur ++ (concaténation) »++ concatène deux listes :
[ 1 2 ] ++ [ 3 4 ]# [ 1 2 3 4 ]Étape 12 : Modularisation avec import
Section intitulée « Étape 12 : Modularisation avec import »Pourquoi modulariser ?
Section intitulée « Pourquoi modulariser ? »Notre shell.nix grandit. Séparons la configuration dans des fichiers distincts.
Structure du projet
Section intitulée « Structure du projet »mon-projet-python/├── shell.nix # Point d'entrée├── python-env.nix # Configuration Python└── config.nix # Variables de configurationconfig.nix : les variables
Section intitulée « config.nix : les variables »{ projectName = "mon-api"; projectVersion = "1.0.0"; debugMode = true; flaskPort = 5000;}python-env.nix : l’environnement Python
Section intitulée « python-env.nix : l’environnement Python »{ pkgs }:
pkgs.python312.withPackages (ps: [ ps.requests ps.flask ps.pytest ps.black ps.mypy])Ce fichier est une fonction qui attend pkgs en argument.
shell.nix : assemblage
Section intitulée « shell.nix : assemblage »{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }:
let # Import des modules config = import ./config.nix; pythonEnv = import ./python-env.nix { inherit pkgs; };
debugTools = if config.debugMode then with pkgs; [ htop ncdu ] else [];inpkgs.mkShell { name = "${config.projectName}-dev";
packages = with pkgs; [ pythonEnv git vim ripgrep ] ++ debugTools;
PROJECT_NAME = config.projectName; PROJECT_VERSION = config.projectVersion; FLASK_RUN_PORT = toString config.flaskPort;
}Vous avez remarqué qu’on demande à utiliser la version figée de nixpkgs dans
shell.nix et qu’on la transmet à python-env.nix via import ./python-env.nix \{ inherit pkgs; \};.
Comprendre inherit
Section intitulée « Comprendre inherit »inherit pkgs est un raccourci pour pkgs = pkgs :
# Ces deux lignes sont équivalentes :import ./python-env.nix { pkgs = pkgs; }import ./python-env.nix { inherit pkgs; }C’est très courant pour passer des variables qui portent le même nom.
Étape 13 : L’opérateur // (merge)
Section intitulée « Étape 13 : L’opérateur // (merge) »Fusionner des attrsets
Section intitulée « Fusionner des attrsets »L’opérateur // fusionne deux attrsets. Le côté droit écrase le gauche :
{ a = 1; b = 2; } // { b = 3; c = 4; }# { a = 1; b = 3; c = 4; }Cas pratique : surcharges de configuration
Section intitulée « Cas pratique : surcharges de configuration »Créons un système de configuration avec des valeurs par défaut :
let defaults = { projectName = "mon-projet"; debugMode = false; flaskPort = 5000; flaskHost = "127.0.0.1"; };
overrides = { debugMode = true; flaskPort = 8080; };in defaults // overrides# Résultat : debugMode=true, flaskPort=8080, le reste inchangéÉtape 14 : Fonctions utilitaires
Section intitulée « Étape 14 : Fonctions utilitaires »Définir ses propres fonctions
Section intitulée « Définir ses propres fonctions »Créons une fonction utilitaire pour formater les messages :
{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }:
let config = import ./config.nix; pythonEnv = import ./python-env.nix { inherit pkgs; };
# Fonction utilitaire - retourne une chaîne pour shellHook formatBanner = title: version: '' echo "╔════════════════════════════════╗" echo "║ ${title} v${version}" echo "╚════════════════════════════════╝" '';inpkgs.mkShell { packages = [ pythonEnv pkgs.git ];
shellHook = formatBanner config.projectName config.projectVersion;}Fonctions avec attrset en argument
Section intitulée « Fonctions avec attrset en argument »Pour plus de clarté, utilisez des arguments nommés :
let formatBanner = { title, version, debug ? false }: '' Project: ${title} v${version} '' + (if debug then "⚠️ DEBUG MODE\n" else "");in formatBanner { title = "mon-api"; version = "1.0.0"; debug = true; }Étape 15 : Builtins essentiels
Section intitulée « Étape 15 : Builtins essentiels »Nix fournit des fonctions intégrées (builtins). Voici les plus utiles :
map : transformer une liste
Section intitulée « map : transformer une liste »map (x: x * 2) [ 1 2 3 ]# [ 2 4 6 ]Cas pratique - ajouter un préfixe à chaque élément :
let libs = [ "requests" "flask" "pytest" ];in map (lib: "python-${lib}") libs# [ "python-requests" "python-flask" "python-pytest" ]filter : filtrer une liste
Section intitulée « filter : filtrer une liste »builtins.filter (x: x > 2) [ 1 2 3 4 5 ]# [ 3 4 5 ]attrNames et attrValues
Section intitulée « attrNames et attrValues »builtins.attrNames { a = 1; b = 2; c = 3; }# [ "a" "b" "c" ]
builtins.attrValues { a = 1; b = 2; c = 3; }# [ 1 2 3 ]Test d’existence avec ?
Section intitulée « Test d’existence avec ? »L’opérateur ? teste si un attribut existe :
let config = { port = 8080; };in config ? port # true config ? host # falseCombiné avec or pour des valeurs par défaut :
let config = { port = 8080; };in config.host or "localhost"# "localhost"Étape 16 : Configuration finale complète
Section intitulée « Étape 16 : Configuration finale complète »Voici notre projet final avec tous les concepts :
mon-projet-python/├── shell.nix├── config.nix├── python-env.nix└── lib/ └── utils.nix{ # Retourne des commandes shell pour afficher une bannière formatBanner = { title, version, debug ? false }: '' echo "┌────────────────────────────────┐" echo "│ 🐍 ${title} v${version}" '' + (if debug then ''echo "│ ⚠️ Debug mode enabled"\n'' else "") + '' echo "└────────────────────────────────┘" '';
mkEnvVars = config: { PROJECT_NAME = config.projectName; PROJECT_VERSION = config.projectVersion; FLASK_RUN_PORT = toString config.flaskPort; FLASK_RUN_HOST = config.flaskHost; FLASK_DEBUG = if config.debugMode then "1" else "0"; };}let defaults = { projectName = "mon-api"; projectVersion = "1.0.0"; debugMode = false; flaskPort = 5000; flaskHost = "127.0.0.1"; };
# Surcharges pour le développement local localOverrides = { debugMode = true; flaskPort = 8080; };in defaults // localOverrides{ pkgs, config }:
let basePackages = ps: [ ps.requests ps.flask ps.python-dotenv ];
devPackages = ps: [ ps.pytest ps.black ps.mypy ps.ipython ];
allPackages = ps: basePackages ps ++ (if config.debugMode then devPackages ps else []);inpkgs.python312.withPackages allPackages{ pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-24.05.tar.gz") {} }:
let # Imports config = import ./config.nix; utils = import ./lib/utils.nix; pythonEnv = import ./python-env.nix { inherit pkgs config; };
# Outils de base baseTools = with pkgs; [ git vim ripgrep jq ];
# Outils de debug debugTools = with pkgs; [ htop ncdu curl ];
# Tous les paquets allPackages = [ pythonEnv ] ++ baseTools ++ (if config.debugMode then debugTools else []);inpkgs.mkShell ({ name = "${config.projectName}-dev"; packages = allPackages;
shellHook = (utils.formatBanner { title = config.projectName; version = config.projectVersion; debug = config.debugMode; }) + "\necho \"Run 'flask run' to start the development server\"";} // utils.mkEnvVars config)Testez le résultat :
nix-shell# Affiche la bannière et les infosecho $FLASK_RUN_PORT # 8080echo $FLASK_DEBUG # 1python3 -c "import flask; print(flask.__version__)"Récapitulatif des concepts
Section intitulée « Récapitulatif des concepts »| Concept | Syntaxe | Description |
|---|---|---|
| Fonctions | arg: corps | Tout est fonction en Nix. Les fichiers .nix sont souvent des fonctions qui attendent pkgs ou d’autres arguments. |
| Attrsets | { clé = valeur; } | Dictionnaires clé-valeur, structure fondamentale. On y accède avec . (ex: pkgs.git). |
| let … in | let x = 1; in x | Variables locales immuables. Permettent de structurer et réutiliser des valeurs. |
| with | with pkgs; | Importe les attributs d’un attrset dans le scope pour éviter les répétitions. |
| import | import ./file.nix | Charge et évalue un fichier Nix. Permet la modularisation en plusieurs fichiers. |
| inherit | inherit x | Raccourci pour x = x. Permet de passer des variables du même nom. |
| Opérateur // | a // b | Fusionne deux attrsets. Le droit écrase le gauche. Idéal pour les surcharges. |
| if-then-else | if c then a else b | Expression conditionnelle. Le else est obligatoire car tout doit retourner une valeur. |
Pour aller plus loin
Section intitulée « Pour aller plus loin »Vous maîtrisez maintenant les bases du langage Nix en pratique. Voici les prochaines étapes pour approfondir :
- Flakes Nix (guide à venir) : workflow moderne avec verrouillage des dépendances et reproductibilité garantie
- NixOS : configurer un système d’exploitation entier de manière déclarative
Ressources complémentaires
Section intitulée « Ressources complémentaires »| Ressource | Description |
|---|---|
| Nix Language | Documentation officielle du langage |
| Nix Pills | Tutoriel approfondi pas à pas |
| nix.dev | Guides pratiques et bonnes pratiques |
Conclusion
Section intitulée « Conclusion »Le langage Nix peut sembler déroutant au premier abord, mais comme vous l’avez vu dans ce guide, ses concepts sont finalement simples : tout est expression, les attrsets structurent les données, les fonctions paramétrent les configurations, et l’immutabilité garantit la reproductibilité.
En construisant progressivement notre environnement Python, vous avez appris à lire et écrire du Nix de façon pratique. La prochaine étape naturelle sera d’explorer les flakes (guide à venir) pour bénéficier du verrouillage des dépendances et d’une structure de projet standardisée.