#+TITLE: Contrôler un environnement logiciel avec Guix
#+DATE: August, 2019
#+STARTUP: overview indent
#+OPTIONS: num:nil toc:t
#+PROPERTY: header-args :eval never-export
* Point de départ
Pour utiliser Guix, il faut évidemment l'installer. Il y a deux façons d'y arriver:
1. Installer [[https://guix.gnu.org/manual/en/html_node/System-Installation.html][Guix System]] sur votre ordinateur, à la place d'une distribution Linux plus traditionelle comme Debian ou Ubuntu. Tout votre système est alors géré avec Guix, ce qui a des avantages (notamment la reproductibilité totale de votre configuration), mais aussi des inconvénients (il y a moins que logiciels disponibles pour Guix que pour une vieille distribution genre Debian).
2. Installer [[https://guix.gnu.org/manual/en/html_node/Binary-Installation.html][le gestionnaire de paquets Guix]] sur un ordinateur qui tourne déjà sous Linux. Vous pouvez alors utiliser Guix en parallèle avec le gestionnaire de paquets de votre distribution, ce qui donne beaucoup de flexibilité, mais il faut bien sûr faire attention à quel paquet est installé comment.
* Séquence 2: Créer son propre environnement, automatiser sa construction et le partage
** 2.2 Installer tous les paquets dont on a besoin
Une installation Guix de base ne contient même pas python,
il va donc falloir l'installer ainsi que =jupyter= et différents paquets
comme =matplotlib=, =pandas=, =numpy=, =statsmodels=... L'installation
se fait à l'aide de la commande =guix install <nom_du_paquet>=. Comment
faire pour trouver le nom du paquet Guix qui contient ce qui vous intéresse ?
Avec la commande =guix search "<un truc lié au nom de ce que vous cherchez>"=.
D'abord, je cherchertout ce qui a trait à jupyter
#+begin_src shell :session *docker* :results output :exports both
synopsis: Web-based notebook environment for interactive computing
description: The Jupyter HTML notebook is a web-based notebook environment for
+ interactive computing.
relevance: 2
#+end_example
Aouch! Ça fait beaucoup. Dans le tas, il y a un paquet qui s'appelle
=jupyter=. C'est un bon point de départ. Installons-le et voyons ce qu'on peut faire avec. Attention, ce qui se passe quand vous lancez la commande suivante peut être très variable. Dans le meilleur cas, Guix télécharge une version précompilée de Jupyter. Au pire, Guix compile Jupyter à partir de son code source. Et si vous n'avez vraiement pas de chance, Guix va d'abord compiler juste la bonne version des compilateurs qu'il faut pour compiler Jupyter. Dans ce cas, vous devez attendre longtemps - mais à la fin, vous aurez ce que vous avez demandé.
Alors... allons-y !
#+begin_src sh :results output :exports both
guix install jupyter
#+end_src
#+RESULTS:
: 85 packages in profile
Pour tester, lançons-le:
#+begin_src sh :results output :exports both
jupyter notebook
#+end_src
Un essai rapide confirme que tout est là pour créer un notebook en langage Python 3, donc tout va bien.
Il reste à faire la même chose avec =matplotlib=, =pandas=, =numpy= et =statsmodels=. Je trouve les noms des paquets avec =guix search=, et puis j'installe tout avec une seule commande:
Je rajoute le paquet =python-nbconvert= qui permet de
convertir les notebooks en ligne de commande sans passer par
l'interface graphique, ça peut toujours servir:
#+begin_src sh :results output :exports both
guix install python-nbconvert
#+end_src
#+RESULTS:
: 90 packages in profile
** 2.4 Automatiser la construction de son environnement
Vous remarquerez que dans tout ce qui a précédé, j'ai noté dans mon
journal de ce que j'ai effectué mais vous n'avez aucune garantie que
je n'ai rien oublié. De plus, si vous voulez refaire cet environnement
vous même, il vous faudra suivre ces instructions scrupuleusement en
espérant que rien n'aille de travers.
Je vais donc maintenant introduire alors la notion du =manifest= qui
va réaliser la préparation de l'environnement automatiquement à l'aide
de la commande =guix environment=. Un =manifest= est une spécification
complète d'un environnement de calcul. Voyons à quoi ça ressemble.
#+begin_src shell :results output :exports none
mkdir -p moocrr_guix_jupyter
#+end_src
#+RESULTS:
Je crée un fichier [[file:moocrr_guix_jupyter/manifest.scm][moocrr_guix_jupyter/manifest.scm]] dont voici
le contenu.
#+begin_src sh :results output :exports both
cat moocrr_guix_jupyter/manifest.scm
#+end_src
#+RESULTS:
: (specifications->manifest
: '("jupyter"
: "python-matplotlib"
: "python-numpy"
: "python-pandas"
: "python-statsmodels"
: "python-nbconvert"))
:
C'est essentiellement la liste des paquets que j'ai installé à la main auparavant, mais dans un format un peu particulier qu'il faut respecter scrupuleusement. En fait, ce format n'est rien d'autre que le langage de programmation [[https://fr.wikipedia.org/wiki/Scheme][=scheme=]]. Guix est écrit en scheme, et exprimer une liste de paquets en scheme a le grand avantage qu'on peut utiliser des fonctionnalités avancées de Guix pour définir son environnement. Par exemple, je pourrais demander que tous mes paquets, en commençant par Python, soient compilés avec =gcc 7= plutôt qu'avec le compilateur par défaut de Guix, qui est actuellement =gcc 5=.
Je peux alors créer un environnement avec ces paquets avec
#+begin_src sh session *jupyter-env* :results output :exports both
Un environnement "pur" ne contient que les paquets définis dans le manifeste, pendant qu'un environnement "standard" contient aussi tout ce qu'on a disponible par défaut par la ligne de commande, donc des utilitaires comme =ls=, =cp=, etc. Avec un environnement pur, on est sûr de n'utiliser rien qui n'est pas listé dans le manifeste, même pas par erreur.
** 2.5 Mettre son environnement à disposition
L'environnement étant défini par le manifeste, il suffit de mettre ce petit fichier à disposition de ses collègues pour leur permettre de travailler dans un environnement identique. Sauf que... les versions qu'ils auront ne sont peut-être par les mêmes ! Peu après la publication d'une nouvelle version de Jupyter, Guix adoptera cette nouvelle version, et l'environnement créé par mon manifest ne sera plus le même. Ceci est d'ailleurs voulu: souvent on veut tout juste avoir la dernière version de tout. Mais pour la reproductibilité, on veut tout à l'identique.
L'information qu'il faut rajouter, c'est la version de Guix à laquelle le manifeste fait référence. On l'obtient avec
Ceci me dit d'abord que je suis dans la génération 24 de Guix, ce qui veut dire que j'ai mis à jour Guix 23 fois depuis la première installation. Guix garde une trace de mes mise à jour et me permet de revenir en arrière si je le souhaite. Mais ce qui nous intéresse maintenant, c'est la suite. La version de Guix que j'utilise, c'est la =44881ca=. En tant qu'habitués de =git=, vous devinez peut-être ce que c'est, et vous avez raison: c'est bien un commit. Guix est développé sous git, et on peut donc identifier une version précise par son identifiant de commit. Les trois dernières lignes donnent un peu plus de précision, dont notamment l'URL où on peut récupérer le dépôt.
Pour notre exercice de reproductibilité, il vaut mieux utiliser une petite variante qui affiche les mêmes informations autrement:
Et oui, c'est encore =scheme= comme notation. Un "channel" est une collection de définitions de paquets. Ici nous avons seulement la distribution de base, le canal "guix", mais on peut rajouter des paquets qui viennent d'autres sources, et avec l'option =-f channels= on obtient toujours une description complète et lisible par Guix.
On va alors mettre cette description dans un fichier:
Les deux fichiers dans mon répértoire =moocrr_guix_jupyter= permettent donc de reconstruire mon environnement à l'identique, à tout moment, tant qu'il y aura des ordinateurs avec Guix.
** Reconstruire un environnement
Maintenant je me place du côté du consommateur. J'ai reçu un notebook Jupyter accompagné d'un répértoire =moocrr_guix_jupyter= contenant un fichier =guix-channels.scm= et un fichier =manifest.scm=. Au travail!
D'abord je demande à Guix de se restorer les définitions des paquets:
Normalement, =guix pull= sert à mettre à jour Guix, et la ressemblance avec =git pull= n'est pas un accident. Avec l'option =-C=, je ne mets pas "à jour", mais au commit demandé. Je crée ainsi une nouvelle génération de Guix, mais comme je peux facilement revenir en arrière, pas de souci. Maintenant je peux exécuter Jupyter dans son environnement d'origine, exactement comme on a vu avant:
Attention, la première fois que je lance =guix environment= après =guix pull=, il se peut que Guix se met à compiler Jupyter. Mieux vaut prévoir du temps!
Une fois que j'ai terminé mon exploration, je peux revenir à la version de Guix que j'avais avant:
J'espère que cette dernière commande vous a fait sursauter. Pourquoi =guix package=? Et pourquoi =-p ~/.config/guix/current=? En fait, Guix gère les générations de la distribution exactement comme les générations des "profils", qui sont des environnements installés de façon plus permanente. Mais dans ce tutoriel, nous ne couvrons pas les profils, ni plein d'autres aspects de Guix. À vous d'explorer!