Skelets numériques
Bonheur déclaratif
Dans le précédent billet, j’ai détaillé quelques frustrations que j’avais avec la gestion de paquet. Je vais maintenant présenter concrètement la solution technique que j’utilise, Nix. Ça risque d’être un peu technique, mais je vais tâcher de créer un billet qui soit “autocontenu”: je n’introduirai les notions utiles qu’au besoin, et uniquement ce qui sera nécessaire pour comprendre.
Nix, donc est un gestionnaire de paquet déclaratif. On a vu dans le billet précédent ce qu’était un gestionnaire de paquet. On va donc s’intéresser au deuxième mot dans le titre: « déclaratif ».
Déclaratif?
Ma définition personnelle est la suivante: « on décrit l’état final que doit atteindre un programme, plutôt qu’une séquence d’opération »1. Donc, plutôt que de décrire comment passer d’un état du système à un autre, on décrit l’état dans lequel on souhaite se trouver. C’est un petit peu comme si vous décriviez avec précision à un·e pâtissier·e votre gâteau d’anniversaire préféré (composition, apparence, dressage, quantité), plutôt que de lui décrire quelles étape æl devrait suivre pour le réaliser.
Ça veut dire quand on parle de l’état d’un système? Et pour un gestionnaire de paquets ? On décrit (ou déclare) l’état du système, dans un certain langage. Une machinerie s’occupe ensuite de traduire la description en un système qui corresponde à la description qui en a été faite. Ça a plusieurs conséquences, qui ont l’air très théoriques pour l’instant mais qui se clarifieront plus tard.
Prédictibilité
Reprenons l’analogie de læ pâtissier·e. Si vous lui donnez la même description du gâteau, le résultat sera vraisemblablement le même (un succulent gâteau). Si vous allez voir quelqu’un d’autre, qui sait lire votre recette et qui a des compétences comparables, Si vous vous concentrez sur décrire l’état d’un système plutôt que les opérations pour y parvenir, on peut se donner
Généralement, les langages de programmation fonctionnelle sont dit « idempotents », c’est ne même évaluation produira le même résultat (si je relance un build avec les mêmes entrées, j’obtiens la même sortie)
Exhaustivité
Pour avoir un système qui fonctionne, il est nécessaire de préciser ce qu’on veut dedans. Ça paraît évident dit comme ça, mais pour donner
Toutes les dépendances doivent être spécifiées vu qu’on build dans un environnement dédié
Non destructivité
Comme je le décrit dans l’article précédent, mettre à jour, c’est un peu casser son système. Quand vous mettez à jour un programme (votre appli de compta, de discussion instantanée, le système d’exploitation de votre téléphone…), l’ancienne version du programme est désinstallé, ses dépendances mises à jour, puis la nouvelle version installée.
Nix offre des mécanismes qui permettent de conserver les anciennes versions des logiciels[^destructivite]. Pour simplifier, Nix conserve toutes les versions qui pourraient être utiles et s’assure que l’état du système courant mobilise les bonnes versions des dépendances.
C’est plutôt rassurant pour des raisons que j’expliquerai dans un futur billet. Il y a beaucoup moins de chances de “casser” sa machine et de revenir à une configuration précédente.
[destructivite]: Il est tout à fait possible d’avoir des approches déclaratives et destructives, mais ici je fais la confusion entre les deux par simplicité.
Concrètement, à quoi ça ressemble? Voici un extrait d’une spécification Nix pour ma machine - elle n’est pas complète et nécessite d’autres fichiers pour produire un environnement qui soit à mon goût. On voit que je spécifie que j’utilise un clavier en azerty, que j’utilise la famille de fonte inconsolata et que je veux que le bluetooth soit activé par défaut. Un point important: comme il s’agit d’un fichier texte, il est tout à fait possible de le documenter pour clarifier des options qui seraient obscures, ou pourquoi certains choix ont été faits!
{ pkgs, ... }:
{
# Select internationalisation properties.
i18n.defaultLocale = "fr_FR.UTF-8";
console = {
font = "Lat2-Terminus16";
keyMap = "fr";
};
# Pick only one of the below networking options.
networking.networkmanager.enable = true; # Easiest to use and most distros use this by default.
fonts.packages = with pkgs; [
inconsolata
];
programs.zsh.enable = true;
programs.dconf.enable = true;
users.defaultUserShell = pkgs.zsh;
# Enable OpenGL Replaced by the hardware.graphics
hardware.graphics.enable = true;
hardware.bluetooth.enable = true;
hardware.bluetooth.powerOnBoot = true;
# Set your time zone.
time.timeZone = "Europe/Paris";
environment.enableDebugInfo = true;
environment.systemPackages = with pkgs; [
vim
wget
];
}
Pourquoi j’aime ça
- facile pour moi de pas avoir à me préoccuper de ce qui se passe quand j’installe un système. je veux quelque chose qui fournit un résultat final, j’estime que je n’ai pas à me préoccuper de détails techniques sur lesquels je n’ai pas la main. Baisse de la complexité.
- je peux tout casser en changeant plein de paramètres et si ça pète je peux revenir en arrière (il y a des entrées de boot fournies et comme la config est versionnée, je peux rollback à une version précédente)
- je peux sauvegarder ma config qui est représentée comme un ensemble de fichiers textes dans un dépôt git et la mettre sur d’autres ordinateurs en cas de crashes ou de réinstallation. plus besoin de passer des plombes à refaire une config
Mon nouvel OS basé sur le gestionnaire de paquet [[nix]]. Je l’ai setup avec [[home-manager]].
J’ai un dépôt [[git]] avec quelques fichiers de confs que je version ne. Truc stylé: dans l’option home.file
, je peux lister des fichier qui seront gérés par home-manager et les pointer sur des fichiers dans les dépôts. Fort pratique pour versionner sa config de manière unique.
-
Ce mot est très riche de sens, et il est emprunté à une tradition de programmation appellée programmation fonctionnelle. Je confonds allègrement déclaratif et fonctionnel ici. ↩︎