\input texinfo
@c ===========================================================================
@c
@c This file was generated with po4a. Translate the source file.
@c
@c ===========================================================================

@c -*-texinfo-*-

@c %**start of header
@setfilename guix-cookbook.fr.info
@documentencoding UTF-8
@documentlanguage fr
@frenchspacing on
@settitle Livre de recettes de GNU Guix
@c %**end of header

@c Onion service for ci.guix.gnu.org.
@set SUBSTITUTE-TOR-URL https://4zwzi66wwdaalbhgnix55ea3ab4pvvw66ll2ow53kjub6se4q2bclcyd.onion

@copying
Copyright @copyright{} 2019, 2022 Ricardo Wurmus@*
Copyright @copyright{} 2019 Efraim Flashner@*
Copyright @copyright{} 2019 Pierre Neidhardt@*
Copyright @copyright{} 2020 Oleg Pykhalov@*
Copyright @copyright{} 2020 Matthew Brooks@*
Copyright @copyright{} 2020 Marcin Karpezo@*
Copyright @copyright{} 2020 Brice Waegeneire@*
Copyright @copyright{} 2020 André Batista@*
Copyright @copyright{} 2020 Christine Lemmer-Webber@*
Copyright @copyright{} 2021 Joshua Branson@*
Copyright @copyright{} 2022, 2023 Maxim Cournoyer@*
Copyright @copyright{} 2023-2025 Ludovic Courtès@*
Copyright @copyright{} 2023 Thomas Ieong@*
Copyright @copyright{} 2024 Florian Pelz@*
Copyright @copyright{} 2025 45mg@*
Copyright @copyright{} 2023 Marek Felšöci@*
Copyright @copyright{} 2023 Konrad Hinsen@*
Copyright @copyright{} 2023 Philippe Swartvagher@*

Vous avez la permission de copier, distribuer ou modifier ce document sous
les termes de la Licence GNU Free Documentation, version 1.3 ou toute
version ultérieure publiée par la Free Software Foundation ; sans section
invariante, texte de couverture et sans texte de quatrième de couverture.
Une copie de la licence est incluse dans la section intitulée « GNU Free
Documentation License ».
@end copying

@dircategory Administration système
@direntry
* Livre de recettes de Guix: (guix-cookbook.fr).  Didacticiels et exemples 
                                                    pour GNU Guix.
@end direntry

@titlepage
@title Livre de recettes de GNU Guix
@subtitle Didacticiels et exemples d'utilisation du gestionnaire de paquets GNU Guix
@author Les développeurs de GNU Guix

@page
@vskip 0pt plus 1filll

@insertcopying
@end titlepage

@contents

@c *********************************************************************
@node Top
@top Livre de recettes de GNU Guix

Ce document propose des didacticiels et des exemples détaillés pour
GNU@tie{}Guix, un outil de gestion des paquets fonctionnel écrit pour le
système GNU. @pxref{Top,,, guix.fr, le manuel de référence de Guix} pour
plus de détails sur le système, son API et les concepts associés.

@c TRANSLATORS: You can replace the following paragraph with information on
@c how to join your own translation team and how to report issues with the
@c translation.
Ce manuel est aussi disponible en anglais (@pxref{Top,,, guix-cookbook, GNU
Guix Cookbook}), en chinois simplifié (@pxref{Top,,, guix-cookbook.zh_CN,
GNU Guix 烹饪书}), en allemand (@pxref{Top,,, guix-cookbook.de,
GNU-Guix-Kochbuch}), en coréen (@pxref{Top,,, guix-cookbook.ko, GNU Guix
쿡북}), en espagnol (@pxref{Top,,, guix-cookbook.es, Libro de recetas de GNU
Guix}), en italien (@pxref{Top,,, guix-cookbook.it, Ricettario di GNU
Guix}), en portugais brésilien (@pxref{Top,,, guix-cookbook.pt_BR, Livro de
receitas do GNU Guix}), en russe (@pxref{Top,,, guix-cookbook.ru, GNU Guix
Рецепты}), en slovaque (@pxref{Top,,, guix-cookbook.sk, Receptár GNU Guix}),
en suédois (@pxref{Top,,, guix-cookbook.sv, Kokbok för GNU Guix}) et en
tamoul (@pxref{Top,,, guix-cookbook.ta, GNU Guix கையேடு}). Si vous désirez
traduire ce document dans votre langue maternelle, rejoignez
@uref{https://translate.codeberg.org/projects/guix/documentation-cookbook,
Weblate} (@pxref{Traduire Guix,,, guix.fr, le manuel de référence de Guix}).

@menu
* Didacticiels pour Scheme::  Découvrez votre nouveau langage préféré !
* Empaquetage::              Didacticiels d'empaquetage
* Configuration du système::  Personnalisation du système GNU
* Conteneurs::               Environnements isolés et systèmes imbriqués
* Machines virtuelles::      Utilisation et configuration des machines 
                               virtuelles
* Gestion avancée des paquets::  Le pouvoir aux utilisateurs !
* Développement logiciel::   Environnements, intégration continue, etc.
* Gestion de l'environnement::  Environnement de contrôle
* Recherche reproductible::  Les bases pour la recherche reproductible.
* Installer Guix sur une grappe de calcul::  Calcul haute-performance.
* Gestion du système Guix::  Spécificités de la gestion système.

* Remerciements::            Merci !
* La licence GNU Free Documentation::  La licence de ce document.
* Index des concepts::       Les concepts.

@detailmenu
 --- Liste détaillée des nœuds ---



Didacticiels pour Scheme



* Cours accéléré du langage Scheme::  Cours accéléré du langage Scheme

Empaquetage



* Didacticiel d'empaquetage::  Un didacticiel sur la manière d'ajouter des 
                                 paquets à Guix.
* Fonctionnement de l'empaquetage::  Exemples réels sur la manière de 
                                       travailler avec des systèmes de 
                                       construction particuliers.

Didacticiel d'empaquetage



* Un paquet « hello world »::  Un paquet « hello world »
* Configuration::            Configuration
* Exemple avancé::           Exemple avancé
* Autres systèmes de construction::  Autres systèmes de construction
* Définition programmable et automatisée::  Définition programmable et 
                                              automatisée
* Se faire aider::           Se faire aider
* Conclusion::               Conclusion
* Références::               Références

Configuration



* Fichier local::            Fichier local
* Canaux::                   Canaux
* Bidouillage direct dans le dépôt git::  Bidouillage direct dans le dépôt 
                                            git

Définition programmable et automatisée



* Les importateurs récursifs::  Les importateurs récursifs
* Mise à jour automatique::  Mise à jour automatique
* Héritage::                 Héritage

Fonctionnement de l'empaquetage



* Empaqueter des crates Rust::  Empaqueter des crates Rust

Empaqueter des crates Rust



* Méthode de travail habituelle pour l'empaquetage Rust::  Méthode de 
                                                             travail 
                                                             habituelle pour 
                                                             l'empaquetage 
                                                             Rust
* Espaces de travail Cargo et images de développement::  Espaces de travail 
                                                           Cargo et images de 
                                                           développement
* Utiliser les bibliothèques Rust dans d'autres systèmes de construction::  Utiliser les bibliothèques Rust dans d'autres systèmes de construction

Configuration du système



* Connexion automatique à un TTY donné::  Connecter automatiquement un 
                                            utilisateur sur un TTY donné
* Personnalisation du noyau::  Créer et utiliser un noyau Linux personnalisé 
                                 sur le système Guix.
* L'API de création d'images du système Guix::  Personnaliser des images 
                                                  pour des plateformes 
                                                  particulières.
* Utiliser des clés de sécurité::  Comment utiliser des clés de sécurité 
                                     avec le système Guix.
* Tâche mcron pour le DNS dynamique::  Tâche pour mettre à jour l'adresse IP 
                                         derrière un nom d'hôte DuckDNS.
* Se connecter à un VPN Wireguard::  Se connecter à un VPN Wireguard.
* Personnaliser un gestionnaire de fenêtres::  Gérer la personnalisation 
                                                 d'un gestionnaire de fenêtres 
                                                 sur le système Guix.
* Lancer Guix sur un serveur Linode::  Lancer Guix sur un serveur Linode.
* Lancer Guix sur un serveur Kimsufi::  Lancer Guix sur un serveur Kimsufi.
* Mettre en place un montage dupliqué::  Mettre en place un montage dupliqué 
                                           dans une définition de système de 
                                           fichiers.
* Récupérer des substituts via Tor::  Configurer le démon Guix pour 
                                        récupérer les substituts via Tor.
* Configurer NGINX avec Lua::  Configurer le serveur web NGINX pour qu'il 
                                 charge des modules Lua.
* Serveur de musique avec l'audio bluetooth::  Lecteur de musique sans 
                                                 affichage avec entrée 
                                                 bluetooth.

Personnaliser un gestionnaire de fenêtres



* StumpWM::                  StumpWM
* Verrouillage de session::  Verrouillage de session

Verrouillage de session



* Xorg::                     Xorg

Conteneurs



* Conteneurs Guix::          Environnements parfaitement isolés
* Conteneurs pour le système Guix::  Un système dans votre système

Conteneurs pour le système Guix



* Un conteneur de base de données::  Un conteneur de base de données
* Utilisation du réseau dans le conteneur::  Utilisation du réseau dans le 
                                               conteneur

Machines virtuelles



* Pont réseau pour QEMU::    Pont réseau pour QEMU
* Réseau routé pour libvirt::  Réseau routé pour libvirt
* Réseau personnalisé basé sur le NAT pour libvirt::  Réseau personnalisé 
                                                        basé sur le NAT pour 
                                                        libvirt
* Références : Références sur les VM.  De la lecture à propose des machines 
                                         virtuelles.

Gestion avancée des paquets



* Les profils Guix en pratique::  Stratégies pour gérer plusieurs profils et 
                                    manifestes.

Les profils Guix en pratique



* Utilisation de base avec des manifestes::  Utilisation de base avec des 
                                               manifestes
* Paquets requis::           Paquets requis
* Profil par défaut::        Profil par défaut
* Les avantages des manifestes::  Les avantages des manifestes
* Profils reproductibles::   Profils reproductibles

Développement logiciel



* Guide de démarrage::       Étape 0 : Utiliser `guix shell'.
* Compilation avec Guix::    Étape 1 : compiler votre code.
* Le dépôt comme canal::     Étape 2 : Transformer le dépôt en canal.
* Variantes de paquets::     Bonus : Définir des variants.
* Mettre en place une intégration continue::  Étape 3 : intégration 
                                                continue.
* Manifeste de construction::  Bonus : Manifeste.
* Récapitulatif::            Récapitulatif

Gestion de l'environnement



* Environnement Guix avec direnv::  Créer un environnement Guix avec direnv

Utiliser Guix pour la recherche reproductible



* Mettre en place l'environnement::  Étape 1 : Utiliser `guix shell'.
* Enregistrer l'environnement::  Étape 2 : Utiliser `guix describe'.
* S'assurer d'un archivage du code source sur le long-terme::  Étape 3 : 
                                                                 Software 
                                                                 Heritage.
* Référencer l'environnement logiciel::  Étape 4 : les SWHID.

Installer Guix sur une grappe de calcul



* Mettre en place un nœud principal::  Le nœud qui fait tourner le démon.
* Mettre en place des nœuds de calcul::  Les nœuds clients.
* Accès réseau de la grappe::  Gérer les restrictions d'accès réseau.
* Utilisation de disque de la grappe::  Considérations d'utilisation de 
                                          l'espace disque.
* Considérations de sécurité de la grappe::  Garder votre grappe sécurisée.

Gestion du système Guix



* Mise à jour de Postgres pour Cuirass::  Mise à jour des paramètres par 
                                            défaut de postgres.

@end detailmenu
@end menu

@c *********************************************************************
@node Didacticiels pour Scheme
@chapter Didacticiels pour Scheme

GNU@tie{}Guix est écrit dans le langage de programmation Scheme.  Nombre de
ses fonctionnalités peuvent être consultées et manipulées par
programmation. Vous pouvez utiliser Scheme entre autres pour générer des
définitions de paquets, pour les modifier, pour les compiler ou pour
déployer des systèmes d'exploitation entiers.

Connaître les bases de la programmation en Scheme vous permettra d'utiliser
de nombreuses fonctionnalités avancées de Guix --- et vous n'avez pas besoin
d'être un·e programmeur·euse chevronné·e !

C'est parti !

@menu
* Cours accéléré du langage Scheme::  Cours accéléré du langage Scheme
@end menu

@node Cours accéléré du langage Scheme
@section Cours accéléré du langage Scheme

@cindex Scheme, cours accéléré

Guix utilise l'implémentation Guile du langage Scheme. Pour commencer à
jouer avec le langage, installez-le avec @code{guix install guile} et
démarrer une @dfn{BLÉA} (@dfn{REPL} en anglais), une
@uref{https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop,
@dfn{boucle de lecture, évaluation, affichage}}, en lançant @code{guile} sur
la ligne de commande.

Vous pouvez également lancer la commande @code{guix shell guile -- guile} si
vous préférez ne pas installer Guile dans votre profil utilisateur.

Dans les exemples suivants, les lignes montrent ce que vous devez taper sur
la REPL ; les lignes commençant par « @result{} » montrent le résultat de
l'évaluation, tandis que les lignes commençant par « @print{} » montrent ce
qui est affiché. @xref{Using Guile Interactively,,, guile, GNU Guile
Reference Manual}, pour plus d'information sur la REPL.

@itemize
@item
La syntaxe de Scheme se résume à un arbre d'expressions (ou une
@emph{s-expression} dans le jargon Lisp). Une expression peut être un
litéral comme un nombre ou une chaîne de caractères, ou composée d'une liste
d'autres éléments composés et litéraux, entourée de
parenthèses. @code{#true} et @code{#false} (abrégés @code{#t} et @code{#f})
correspondent aux booléens « vrai » et « faux ».

Voici des exemples d'expressions valides :

@lisp
"Bonjour le monde !"
@result{} "Bonjour le monde !"

17
@result{} 17

(display (string-append "Bonjour " "Guix" "\n"))
@print{} Bonjour Guix
@result{} #<unspecified>
@end lisp

@item
Ce dernier exemple est un appel de fonction imbriqué dans un autre appel de
fonction. Lorsqu'une expression parenthésée est évaluée, le premier élément
est la fonction et le reste sont les arguments passés à la fonction. Chaque
fonction renvoie la dernière expression évaluée.

@item
On peut déclarer des fonctions anonymes — des @dfn{procédures} en Scheme —
avec le terme @code{lambda} :

@lisp
(lambda (x) (* x x))
@result{} #<procedure 120e348 at <unknown port>:24:0 (x)>
@end lisp

La procédure ci-dessus renvoie le carré de son argument. Comme tout est une
expression, l'expression @code{lambda} renvoie une procédure anonyme, qui
peut ensuite être appliquée à un argument :

@lisp
((lambda (x) (* x x)) 3)
@result{} 9
@end lisp

Les procédures sont des valeurs normales comme les nombres, les chaines de
caractère, les booléens, etc.

@item
On peut assigner un nom global à tout ce qu'on veut avec @code{define} :

@lisp
(define a 3)
(define square (lambda (x) (* x x)))
(square a)
@result{} 9
@end lisp

@item
On peut définir des procédure de manière plus concise avec la syntaxe
suivante :

@lisp
(define (square x) (* x x))
@end lisp

@item
On peut créer une structure de liste avec la procédure @code{list} :

@lisp
(list 2 a 5 7)
@result{} (2 3 5 7)
@end lisp

@item
Des procédures standards sont fournies par le module @code{(srfi srfi-1)}
pour créer et traiter des listes (@pxref{SRFI-1, list processing,, guile, le
manuel de référence de GNU Guile}). Voici certaines des plus utiles en
action :

@lisp
(use-modules (srfi srfi-1))  ; importe les procédures de traitement des listes

(append (list 1 2) (list 3 4))
@result{} (1 2 3 4)

(map (lambda (x) (* x x)) (list 1 2 3 4))
@result{} (1 4 9 16)

(delete 3 (list 1 2 3 4))        @result{} (1 2 4)
(filter odd? (list 1 2 3 4))     @result{} (1 3)
(remove even? (list 1 2 3 4))    @result{} (1 3)
(find number? (list "a" 42 "b")) @result{} 42
@end lisp

Remarquez que le premier argument de @code{map}, @code{filter},
@code{remove} et @code{find} est une procédure !

@item
@cindex S-expression
La @dfn{quote} (l'apostrophe) désactive l'évaluation d'une expression
parenthésée, aussi appelée S-expression ou « s-exp » : le premier élément
n'est pas appelé avec les autres éléments en argument (@pxref{Expression
Syntax, quote,, guile, GNU Guile Reference Manual}). Donc, il renvoie une
liste de termes.

@lisp
'(display (string-append "Bonjour " "Guix" "\n"))
@result{} (display (string-append "Bonjour " "Guix" "\n"))

'(2 a 5 7)
@result{} (2 a 5 7)
@end lisp

@item
La @code{quasiquote} (@code{`}, l'apostrophe à l'envers) désactive
l'évaluation d'une expression parenthésée jusqu'à ce qu'un @code{unquote}
(@code{,}, une virgule) la réactive. De cette manière, on garde un contrôle
fin sur ce qui est évalué et sur ce qui ne l'est pas.

@lisp
`(2 a 5 7 (2 ,a 5 ,(+ a 4)))
@result{} (2 a 5 7 (2 3 5 7))
@end lisp

Remarquez que le résultat ci-dessus est une liste d'éléments mixtes : des
nombres, des symboles (ici @code{a}) et le dernier élément est aussi une
liste.

@item
@cindex G-expressions, syntaxe
@cindex gexps, syntaxe
@findex #~
@findex #$
@findex gexp
@findex ungexp
Guix définit une variante des S-expressions gonflées aux stéroïdes appelées
@dfn{G-expressions} ou « gexps », qui fournit une variante de
@code{quasiquote} et @code{unquote} : @code{#~} (ou @code{gexp}) et
@code{#$} (ou @code{ungexp}). Elles vous permettent @emph{d'échelonner du
code pour une future exécution}.

Par exemple, vous rencontrerez des gexps dans certaines définitions de
paquets qui fournissent du code à exécuter pendant la construction du
paquet. Elles ressemblent à ceci :

@lisp
(use-modules (guix gexp)           ;pour qu'on puisse écrire des gexps
             (gnu packages base))  ;pour « coreutils »

;; Ci-dessous, une G-expression représentant du code échelonné.
#~(begin
    ;; Invoque « ls » du paquet défini par la variable
    ;; « coreutils ».
    (system* #$(file-append coreutils "/bin/ls") "-l")

    ;; Crée le répertoire de sortie de ce paquet.
    (mkdir #$output))
@end lisp

@xref{G-Expressions,,, guix.fr, le manuel de référence de GNU Guix}, pour
plus de détails sur les gexps.

@item
On peut nommer localement plusieurs variables avec @code{let} (@pxref{Local
Bindings,,, guile, GNU Guile Reference Manual}) :

@lisp
(define x 10)
(let ((x 2)
      (y 3))
  (list x y))
@result{} (2 3)

x
@result{} 10

y
@error{} In procedure module-lookup: Unbound variable: y
@end lisp

On peut utiliser @code{let*} pour permettre au déclarations de variables
ultérieures d'utiliser les définitions précédentes.

@lisp
(let* ((x 2)
       (y (* x 3)))
  (list x y))
@result{} (2 6)
@end lisp

@item
On utilise typiquement des @dfn{mot-clés} pour identifier les paramètres
nommés d'une procédure. Ils sont précédés de @code{#:} (dièse, deux-points)
suivi par des caractères alphanumériques :
@code{#:comme-ça}. @xref{Keywords,,, guile, GNU Guile Reference Manual}.

@item
On utilise souvent le signe pourcent @code{%} pour les variables globales
non modifiables à l'étape de construction. Remarquez que ce n'est qu'une
convention, comme @code{_} en C. Scheme traite @code{%} de la même manière
que les autres lettres.

@item
On peut créer des modules avec @code{define-module} (@pxref{Creating Guile
Modules,,, guile, GNU Guile Reference Manual}). Par exemple

@lisp
(define-module (guix build-system ruby)
  #:use-module (guix store)
  #:export (ruby-build
            ruby-build-system))
@end lisp

défini le module @code{guix build-system ruby} qui doit se situer dans
@file{guix/build-system/ruby.scm} quelque part dans le chemin de recherche
de Guile. Il dépend du module @code{(guix store)} et exporte deux variables,
@code{ruby-build} et @code{ruby-build-system}.

@xref{Modules de paquets,,, guix.fr, le manuel de référence de GNU Guix},
pour plus d'information sur les modules qui définissent des paquets.
@end itemize

@quotation Pour aller plus loin
Scheme est un langage qui a été beaucoup utilisé pour enseigner la
programmation et vous trouverez plein de supports qui l'utilisent. Voici une
liste de documents qui vous en apprendront plus sur le Scheme :

@itemize
@item
@uref{https://spritely.institute/static/papers/scheme-primer.html, @i{A
Scheme Primer}}, par Christine Lemmer-Webber et le Spritely Institute.

@item
@c There used to be a copy at mitpress.mit.edu but it vanished.
@uref{https://sarabander.github.io/sicp/, @i{Structure and Interpretation of
Computer Programs}}, par Harold Abelson et Gerald Jay Sussman, avec Julie
Sussman.  Souvent appelé ``SICP'', ce livre est une référence.

Vous pouvez aussi l'installer et le lire sur votre ordinateur :

@example
guix install book-sicp info-reader
info sicp
@end example

@end itemize

Vous trouverez plus de livres, de didacticiels et d'autres ressources sur
@url{https://schemers.org/}.
@end quotation


@c *********************************************************************
@node Empaquetage
@chapter Empaquetage

@cindex empaquetage

Ce chapitre est conçu pour vous enseigner comment ajouter des paquets à la
collection de paquets de GNU Guix. Pour cela, vous devrez écrire des
définitions de paquets en Guile Scheme, les organiser en modules et les
construire.

@menu
* Didacticiel d'empaquetage::  Un didacticiel sur la manière d'ajouter des 
                                 paquets à Guix.
* Fonctionnement de l'empaquetage::  Exemples réels sur la manière de 
                                       travailler avec des systèmes de 
                                       construction particuliers.
@end menu

@node Didacticiel d'empaquetage
@section Didacticiel d'empaquetage

GNU Guix se démarque des autres gestionnaire de paquets en étant
@emph{bidouillable}, surtout parce qu'il utilise
@uref{https://www.gnu.org/software/guile/,GNU Guile}, un langage de
programmation de haut-niveau puissant, l'un des dialectes
@uref{https://fr.wikipedia.org/wiki/Scheme, Scheme} de
@uref{https://fr.wikipedia.org/wiki/Lisp, la famille Lisp}.

Les définitions de paquets sont aussi écrites en Scheme, ce qui le rend plus
puissant de manière assez unique par rapport aux autres gestionnaires de
paquets qui utilisent des scripts shell ou des langages simples.

@itemize
@item
Vous pouvez utiliser des fonctions, des structures, des macros et toute
l'expressivité de Scheme dans vos définitions de paquets.

@item
L'héritage facilite la personnalisation d'un paquet en héritant d'un autre
et en modifiant uniquement les points nécessaires.

@item
Traitement par lot : la collection des paquets entière peut être analysée,
filtrée et traitée. Vous voulez construire un serveur sans interface
graphique ? C'est possible. Vous voulez tout reconstruire à partir des
sources avec des drapeaux d'optimisation spécifiques ? Passez l'argument
@code{#:make-flags "…"} à la liste des paquets. Ce ne serait pas aberrant de
penser @uref{https://wiki.gentoo.org/wiki/USE_flag, au drapeau USE de
Gentoo}, mais cela va plus loin : la personne qui crée les paquets n'a pas
besoin de penser à l'avance à ces changements, ils peuvent être
@emph{programmés} par l'utilisateur ou l'utilisatrice !
@end itemize

Le didacticiel suivant traite des bases de la création de paquets avec
Guix. Il ne présuppose aucune connaissance du système Guix ni du langage
Lisp. On ne s'attend qu'à ce que vous aillez une certaine familiarité avec
la ligne de commande et des connaissances de base en programmation.

@menu
* Un paquet « hello world »::  Un paquet « hello world »
* Configuration::            Configuration
* Exemple avancé::           Exemple avancé
* Autres systèmes de construction::  Autres systèmes de construction
* Définition programmable et automatisée::  Définition programmable et 
                                              automatisée
* Se faire aider::           Se faire aider
* Conclusion::               Conclusion
* Références::               Références
@end menu

@node Un paquet « hello world »
@subsection Un paquet « hello world »

La section « Définition des paquets » du manuel explique les bases de
l'empaquetage avec Guix (@pxref{Définition des paquets,,, guix.fr, le manuel
de référence de GNU Guix}). Dans la section suivante, nous reparlerons en
partie de ces bases.

GNU@tie{}Hello est un exemple de projet qui sert d'exemple idiomatique pour
l'empaquetage. Il utilise le système de construction de GNU
(@code{./configure && make && make install}). Guix fournit déjà une
définition de paquet qui est un parfait exemple pour commencer. Vous pouvez
voir sa déclaration avec @code{guix edit hello} depuis la ligne de
commande. Voyons à quoi elle ressemble :

@lisp
(define-public hello
  (package
    (name "hello")
    (version "2.10")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnu/hello/hello-" version
                                  ".tar.gz"))
              (sha256
               (base32
                "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"))))
    (build-system gnu-build-system)
    (synopsis "Hello, GNU world: An example GNU package")
    (description
     "GNU Hello prints the message \"Hello, world!\" and then exits.  It
serves as an example of standard GNU coding practices.  As such, it supports
command-line arguments, multiple languages, and so on.")
    (home-page "https://www.gnu.org/software/hello/")
    (license gpl3+)))
@end lisp

Comme vous pouvez le voir, la plus grosse partie est assez simple. Mais
examinons les champs ensemble :

@table @samp
@item name
Le nom du projet. Avec les conventions de Scheme, on préfère le laisser en
minuscule, sans tiret du bas, en séparant les mots par des tirets.

@item source
Ce champ contient une description de l'origine du code
source. L'enregistrement @code{origin} contient ces champs :

@enumerate
@item La méthode, ici @code{url-fetch} pour télécharger via HTTP/FTP, mais d'autres méthodes
    existent, comme @code{git-fetch} pour les dépôts Git.
@item L'URI, qui est typiquement un emplacement @code{https://} pour @code{url-fetch}. Ici
    le code spécial « mirror://gnu » fait référence à une ensemble
d'emplacements bien connus, qui peuvent tous être utilisés par Guix pour
récupérer la source, si l'un d'entre eux échoue.
@item La somme de contrôle @code{sha256} du fichier demandé. C'est essentiel pour s'assurer
    que la source n'est pas corrompue. Remarquez que Guix fonctionne avec des
chaînes en base32, d'où l'appel à la fonction @code{base32}.
@end enumerate

@item build-system

C'est ici que la puissance d'abstraction du langage Scheme brille de toute
sa splendeur : dans ce cas, le @code{gnu-build-system} permet d'abstraire
les fameuses invocations shell @code{./configure && make && make
install}. Parmi les autres systèmes de construction on trouve le
@code{trivial-build-system} qui ne fait rien et demande de programmer toutes
les étapes de construction, le @code{python-build-system},
@code{emacs-build-system} et bien d'autres (@pxref{Systèmes de
construction,,, guix.fr, le manuel de référence de GNU Guix}).

@item synopsis
Le synopsis devrait être un résumé court de ce que fait le paquet. Pour
beaucoup de paquets, le slogan de la page d'accueil du projet est approprié
pour le synopsis.

@item description
Comme le synopsis, vous pouvez réutiliser la description de la page
d'accueil du projet. Remarquez que Guix utilise la syntaxe Texinfo.

@item home-page
Utilisez l'adresse en HTTPS si elle est disponible.

@item license
Voir @code{guix/licenses.scm} dans les sources du projet pour une liste
complète des licences disponibles.
@end table

Il est temps de construire notre premier paquet ! Rien de bien compliqué
pour l'instant : nous allons garder notre exemple avec @code{my-hello}, une
copie de la déclaration montrée plus haut.

Comme avec le rituel « Hello World » enseigné avec la plupart des langages
de programmation, ce sera sans doute l'approche la plus « manuelle »
d'empaquetage que vous utiliserez. Nous vous montrerons une configuration
idéale plus tard, pour l'instant nous allons suivre la voie la plus simple.

Enregistrez ce qui suit dans un fichier nommé @file{my-hello.scm}.

@lisp
(use-modules (guix packages)
             (guix download)
             (guix build-system gnu)
             (guix licenses))

(package
  (name "my-hello")
  (version "2.10")
  (source (origin
            (method url-fetch)
            (uri (string-append "mirror://gnu/hello/hello-" version
                                ".tar.gz"))
            (sha256
             (base32
              "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"))))
  (build-system gnu-build-system)
  (synopsis "Hello, Guix world: An example custom Guix package")
  (description
   "GNU Hello prints the message \"Hello, world!\" and then exits.  It
serves as an example of standard GNU coding practices.  As such, it supports
command-line arguments, multiple languages, and so on.")
  (home-page "https://www.gnu.org/software/hello/")
  (license gpl3+))
@end lisp

Nous allons expliquer le code supplémentaire dans un moment.

Essayez de jouer avec les différentes valeurs des différents champs. Si vous
changez la source, vous devrez mettre à jour la somme de contrôle. En fait,
Guix refusera de construire quoi que ce soit si la somme de contrôle donnée
ne correspond pas à la somme de contrôle calculée de la source
téléchargée. Pour obtenir la bonne somme de contrôle pour une déclaration de
paquet, vous devrez télécharger la source, calculer la somme de contrôle
sha256 et la convertir en base32.

Heureusement, Guix peut automatiser cette tache pour nous ; tout ce qu'on
doit faire est de lui fournir l'URI :

@c TRANSLATORS: This is example shell output.
@example sh
$ guix download mirror://gnu/hello/hello-2.10.tar.gz

Starting download of /tmp/guix-file.JLYgL7
From https://ftpmirror.gnu.org/gnu/hello/hello-2.10.tar.gz...
following redirection to `https://mirror.ibcp.fr/pub/gnu/hello/hello-2.10.tar.gz'...
 …10.tar.gz  709KiB                                 2.5MiB/s 00:00 [##################] 100.0%
/gnu/store/hbdalsf5lpf01x4dcknwx6xbn6n5km6k-hello-2.10.tar.gz
0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i
@end example

Dans ce cas particulier, la sortie nous dit quel miroir a été choisi. Si le
résultat de la commande au-dessus n'est pas le même que ce qui est montré,
mettez à jour votre déclaration @code{my-hello} en fonction.

Remarquez que les archives des paquets GNU sont accompagnées de leur
signature OpenPGP, donc vous devriez vérifier la signature de cette archive
avec « gpg » pour l'authentifier avant d'aller plus loin :

@c TRANSLATORS: This is example shell output.
@example sh
$ guix download mirror://gnu/hello/hello-2.10.tar.gz.sig

Starting download of /tmp/guix-file.03tFfb
From https://ftpmirror.gnu.org/gnu/hello/hello-2.10.tar.gz.sig...
following redirection to `https://ftp.igh.cnrs.fr/pub/gnu/hello/hello-2.10.tar.gz.sig'...
 ….tar.gz.sig  819B                                                                                                                       1.2MiB/s 00:00 [##################] 100.0%
/gnu/store/rzs8wba9ka7grrmgcpfyxvs58mly0sx6-hello-2.10.tar.gz.sig
0q0v86n3y38z17rl146gdakw9xc4mcscpk8dscs412j22glrv9jf
$ gpg --verify /gnu/store/rzs8wba9ka7grrmgcpfyxvs58mly0sx6-hello-2.10.tar.gz.sig /gnu/store/hbdalsf5lpf01x4dcknwx6xbn6n5km6k-hello-2.10.tar.gz
gpg: Signature faite le dim. 16 nov. 2014 13:08:37 CET
gpg:                avec la clef RSA A9553245FDE9B739
gpg: Bonne signature de « Sami Kerola (https://www.iki.fi/kerolasa/) <kerolasa@@iki.fi> » [inconnu]
gpg: Attention : cette clef n'est pas certifiée avec une signature de confiance.
gpg:             Rien n'indique que la signature appartient à son propriétaire.
Empreinte de clef principale : 8ED3 96E3 7E38 D471 A005  30D3 A955 3245 FDE9 B739
@end example

Vous pouvez ensuite lancer

@c TRANSLATORS: Do not translate this command
@example sh
$ guix package --install-from-file=my-hello.scm
@end example

Vous devriez maintenant avoir @code{my-hello} dans votre profil !

@c TRANSLATORS: Do not translate this command
@example sh
$ guix package --list-installed=my-hello
my-hello	2.10	out
/gnu/store/f1db2mfm8syb8qvc357c53slbvf1g9m9-my-hello-2.10
@end example

Nous sommes allés aussi loin que possible sans aucune connaissance de
Scheme. Avant de continuer sur des paquets plus complexes, il est maintenant
temps de vous renforcer sur votre connaissance du langage Scheme. @xref{Cours accéléré du langage Scheme} pour démarrer.

@node Configuration
@subsection Configuration

Dans le reste de ce chapitre, nous nous appuierons sur vos connaissances de
base du langage Scheme. Maintenant voyons les différentes configurations
possibles pour travailler sur des paquets Guix.

Il y a plusieurs moyens de mettre en place un environnement d'empaquetage
pour Guix.

Nous vous recommandons de travailler directement dans le dépôt des sources
de Guix car ça facilitera la contribution au projet.

Mais d'abord, voyons les autres possibilités.

@menu
* Fichier local::            Fichier local
* Canaux::                   Canaux
* Bidouillage direct dans le dépôt git::  Bidouillage direct dans le dépôt 
                                            git
@end menu

@node Fichier local
@subsubsection Fichier local

C'est ce que nous venons de faire avec @samp{my-hello}. Avec les bases de
Scheme que nous vous avons présentées, nous pouvons maintenant éclairer le
sens du début du fichier. Comme le dit @code{guix package --help} :

@example
  -f, --install-from-file=FICHIER
                         installer le paquet évalué par le code dans
                         FICHIER
@end example

Ainsi, la dernière expression @emph{doit} renvoyer un paquet, ce qui est le
cas dans notre exemple précédent.

L'expression @code{use-modules} indique quels modules sont nécessaires dans
le fichier. Les modules sont des collections de valeurs et de
procédures. Ils sont souvent appelés « bibliothèques » ou « paquets » dans
d'autres langages de programmation.

@node Canaux
@subsubsection Canaux

@cindex canal
Guix et sa collection de paquet peut être étendu par des @dfn{canaux}. Un
canal est un dépôt Git public ou non, qui contient des fichiers @file{.scm}
qui fournissent des paquets (@pxref{Définition des paquets,,, guix.fr, le
manuel de référence de GNU Guix}) ou des services (@pxref{Définir des
services,,, guix.fr, le manuel de référence de GNU Guix}).

Comment créer un canal ? Tout d'abord, créez un répertoire qui contiendra
vos fichiers @file{.scm}, disons @file{~/mon-canal} :

@example
mkdir ~/mon-canal
@end example

Imaginons que vous souhaitiez ajouter le paquet @samp{my-hello} que nous
avons vu plus tôt. Il a d'abord besoin de quelques ajustement :

@lisp
(define-module (my-hello)
  #:use-module (guix licenses)
  #:use-module (guix packages)
  #:use-module (guix build-system gnu)
  #:use-module (guix download))

(define-public my-hello
  (package
    (name "my-hello")
    (version "2.10")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnu/hello/hello-" version
                                  ".tar.gz"))
              (sha256
               (base32
                "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"))))
    (build-system gnu-build-system)
    (synopsis "Hello, Guix world: An example custom Guix package")
    (description
     "GNU Hello prints the message \"Hello, world!\" and then exits.  It
serves as an example of standard GNU coding practices.  As such, it supports
command-line arguments, multiple languages, and so on.")
    (home-page "https://www.gnu.org/software/hello/")
    (license gpl3+)))
@end lisp

Remarquez que nous avons assigné la valeur du paquet à un nom de variable
exportée avec @code{define-public}. Cela assigne en fait le paquet à la
variable @code{my-hello} pour qu'elle puisse être utilisée, par exemple en
dépendance d'un autre paquet.

Si vous utilisez @code{guix package --install-from-file=my-hello.scm} avec
le fichier précédent, la commande échouera car la dernière expression,
@code{define-public}, ne renvoie pas un paquet. Si vous voulez utiliser
@code{define-public} dans ce cas tout de même, assurez-vous que le fichier
termine par une évaluation de @code{my-hello} :

@lisp
;; ...
(define-public my-hello
  ;; ...
  )

my-hello
@end lisp

Ce dernier exemple n'est pas très typique.

Maintenant, comme rendre ce paquet visible pour les commandes @command{guix}
afin de tester vos paquets ? Vous devez ajouter le répertoire au chemin de
recherche avec l'option en ligne de commande @option{-L}, comme dans ces
exemples :

@example
guix show -L ~/mon-canal my-hello
guix build -L ~/mon-canal my-hello
@end example

L'étape finale consiste à transformer @file{~/mon-canal} en un vrai canal,
pour rendre disponible votre collection de paquets via n'importe quelle
commande @command{guix}. Pour cela, vous devez d'abord en faire un dépôt Git
:

@example
cd ~/mon-canal
git init
git add my-hello.scm
git commit -m "Premier commit sur mon canal."
@end example

Et voilà, vous avez un canal ! À partir de maintenant, vous pouvez ajouter
ce canal à votre configuration des canaux dans
@file{~/.config/guix/channels.scm} (@pxref{Spécifier des canaux
supplémentaires,,, guix.fr, le manuel de référence de GNU Guix}). En
supposant que vous gardez votre canal localement pour l'instant, le fichier
@file{channels.scm} ressemblerait à quelque chose comme ça :

@lisp
(append (list (channel
                (name 'mon-canal)
                (url (string-append "file://" (getenv "HOME")
                                    "/mon-canal"))))
        %default-channels)
@end lisp

La prochaine fois que vous exécuterez @command{guix pull}, votre canal sera
récupéré et les paquets qu'il définit seront directement disponibles pour
toutes les commandes @command{guix}, même si vous n'utilisez pas
@option{-L}. La commande @command{guix describe} vous montrera que Guix
utilise effectivement à la fois @code{mon-canal} et les canaux @code{guix}.

@xref{Écrire de nouveaux de canaux,,, guix.fr, le manuel de référence de GNU
Guix} pour des détails.

@node Bidouillage direct dans le dépôt git
@subsubsection Bidouillage direct dans le dépôt git

Nous vous recommandons de travailler directement sur le projet Guix : cela
réduit le travail nécessaire quand vous voudrez soumettre vos changements en
amont pour que la communauté puisse bénéficier de votre dur labeur !

Contrairement à la plupart des distributions logiciels, le dépôt Guix
contient à la fois les outils (dont le gestionnaire de paquets) et les
définitions des paquets. Nous avons fait ce choix pour permettre aux
développeurs et développeuses de profiter de plus de flexibilité pour
changer l'API sans rien casser, en mettant à jour tous les paquets en même
temps. Cela réduit l'inertie dans le développement.

Clonez le dépôt @uref{https://git-scm.com/, Git} officiel :

@example
$ git clone https://git.guix.gnu.org/guix.git
@end example

Dans le reste de cet article, nous utiliserons @samp{$GUIX_CHECKOUT} pour
faire référence à l'emplacement de ce clone.


Suivez les instructions du manuel (@pxref{Contribuer,,, guix.fr, le manuel
de référence de GNU Guix}) pour mettre en place l'environnement du dépôt.

Une fois prêts, vous devriez pouvoir utiliser les définitions des paquets de
l'environnement du dépôt.

N'ayez pas peur de modifier les définitions des paquets que vous trouverez
dans @samp{$GUIX_CHECKOUT/gnu/packages}.

Le script @samp{$GUIX_CHECKOUT/pre-inst-env} vous permet d'utiliser
@samp{guix} sur la collection de paquets du dépôt (@pxref{Lancer Guix avant
qu’il ne soit installé,,, guix.fr, le manuel de référence de GNU Guix}).

@itemize
@item
Recherchez des paquets, comme Ruby :

@example
  $ cd $GUIX_CHECKOUT
  $ ./pre-inst-env guix package --list-available=ruby
      ruby    1.8.7-p374      out     gnu/packages/ruby.scm:119:2
      ruby    2.1.6   out     gnu/packages/ruby.scm:91:2
      ruby    2.2.2   out     gnu/packages/ruby.scm:39:2
@end example

@item
Construisez un paquet, ici Ruby version 2.1 :

@example
  $ ./pre-inst-env guix build --keep-failed ruby@@2.1
  /gnu/store/c13v73jxmj2nir2xjqaz5259zywsa9zi-ruby-2.1.6
@end example

@item
Installez-le dans votre profil utilisateur :

@example
  $ ./pre-inst-env guix package --install ruby@@2.1
@end example

@item
Vérifiez que vous n'avez pas fait l'une des erreurs courantes :

@example
  $ ./pre-inst-env guix lint ruby@@2.1
@end example
@end itemize

Guix essaye de maintenir un bon standard d'empaquetage ; quand vous
contribuez au projet Guix, rappelez-vous de

@itemize
@item
suivre le style de code (@pxref{Style de code,,, guix.fr, le manuel de
référence de GNU Guix}),
@item
et de vérifier la check-list du manuel (@pxref{Envoyer des correctifs,,,
guix.fr, le manuel de référence de GNU Guix}).
@end itemize

Une fois que vous êtes satisfait du résultat, vous pouvez envoyer votre
contribution pour qu'elle rentre dans Guix. Ce processus est aussi détaillé
dans le manuel (@pxref{Contribuer,,, guix.fr, le manuel de référence de GNU
Guix})


Guix est un projet communautaire, donc plus on est de fous, plus on rit !

@node Exemple avancé
@subsection Exemple avancé

L'exemple « Hello World » précédent est le plus simple possible. Les paquets
peuvent devenir plus complexes que cela et Guix peut gérer des scénarios
plus avancés. Voyons un autre paquet plus sophistiqué (légèrement modifié à
partir des sources) :

@lisp
(define-module (gnu packages version-control)
  #:use-module ((guix licenses) #:prefix license:)
  #:use-module (guix utils)
  #:use-module (guix packages)
  #:use-module (guix git-download)
  #:use-module (guix build-system cmake)
  #:use-module (gnu packages)
  #:use-module (gnu packages compression)
  #:use-module (gnu packages pkg-config)
  #:use-module (gnu packages python)
  #:use-module (gnu packages ssh)
  #:use-module (gnu packages tls)
  #:use-module (gnu packages web))

(define-public my-libgit2
  (let ((commit "e98d0a37c93574d2c6107bf7f31140b548c6a7bf")
        (revision "1"))
    (package
      (name "my-libgit2")
      (version (git-version "0.26.6" revision commit))
      (source (origin
                (method git-fetch)
                (uri (git-reference
                      (url "https://github.com/libgit2/libgit2/")
                      (commit commit)))
                (file-name (git-file-name name version))
                (sha256
                 (base32
                  "17pjvprmdrx4h6bb1hhc98w9qi6ki7yl57f090n9kbhswxqfs7s3"))
                (patches (search-patches "libgit2-mtime-0.patch"))
                (modules '((guix build utils)))
                ;; Suppression des logiciels embarqués.
                (snippet '(delete-file-recursively "deps"))))
      (build-system cmake-build-system)
      (outputs '("out" "debug"))
      (arguments
       `(#:tests? #true                         ; Lancer la suite de tests (c'est la valeur par défaut)
         #:configure-flags '("-DUSE_SHA1DC=ON") ; détection de collision SHA-1
         #:phases
         (modify-phases %standard-phases
           (add-after 'unpack 'fix-hardcoded-paths
             (lambda _
               (substitute* "tests/repo/init.c"
                 (("#!/bin/sh") (string-append "#!" (which "sh"))))
               (substitute* "tests/clar/fs.h"
                 (("/bin/cp") (which "cp"))
                 (("/bin/rm") (which "rm")))))
           ;; Lancer les tests avec plus de verbosité.
           (replace 'check
             (lambda* (#:key tests? #:allow-other-keys)
               (when tests?
                 (invoke "./libgit2_clar" "-v" "-Q"))))
           (add-after 'unpack 'make-files-writable-for-tests
             (lambda _ (for-each make-file-writable (find-files ".")))))))
      (inputs
       (list libssh2 http-parser python-wrapper))
      (native-inputs
       (list pkg-config))
      (propagated-inputs
       ;; Ces deux bibliothèques sont dans « Requires.private », dans libgit2.pc.
       (list openssl zlib))
      (home-page "https://libgit2.github.com/")
      (synopsis "Library providing Git core methods")
      (description
       "Libgit2 is a portable, pure C implementation of the Git core methods
provided as a re-entrant linkable library with a solid API, allowing you to
write native speed custom Git applications in any language with bindings.")
      ;; GPLv2 with linking exception
      (license license:gpl2))))
@end lisp

(Dans les cas où vous voulez seulement changer quelques champs d'une
définition de paquets, vous devriez utiliser l'héritage au lieu de tout
copier-coller. Voir plus bas.)

Parlons maintenant de ces champs en détail.

@subsubsection La méthode @code{git-fetch}

Contrairement à la méthode @code{url-fetch}, @code{git-fetch} a besoin d'un
@code{git-reference} qui prend un dépôt Git et un commit. Le commit peut
être n'importe quelle référence Git comme des tags, donc si la
@code{version} a un tag associé, vous pouvez l'utiliser directement. Parfois
le tag est précédé de @code{v}, auquel cas vous pouvez utiliser
@code{(commit (string-append "v" version))}.

Pour vous assurer que le code source du dépôt Git est stocké dans un
répertoire avec un nom descriptif, utilisez @code{(file-name (git-file-name
name version))}.

Vous pouvez utiliser la procédure @code{git-version} pour calculer la
version quand vous empaquetez des programmes pour un commit spécifique, en
suivant le guide de contribution (@pxref{Numéros de version,,, guix.fr, le
manuel de référence de GNU Guix}).

Comment obtenir le hash @code{sha256}, vous demandez-vous ? En invoquant
@command{guix hash} sur un clone du commit voulu, de cette manière :

@example
git clone https://github.com/libgit2/libgit2/
cd libgit2
git checkout v0.26.6
guix hash -rx .
@end example

@command{guix hash -rx} calcul un SHA256 sur le répertoire entier, en
excluant le sous-répertoire @file{.git} (@pxref{Invoquer guix hash,,,
guix.fr, le manuel de référence de GNU Guix}).

Dans le futur, @command{guix download} sera sans doute capable de faire cela
pour vous, comme il le fait pour les téléchargements directs.

@subsubsection Les bouts de code

Les bouts de code (snippet) sont des fragments quotés (c.-à-d. non évalués)
de code Scheme utilisés pour modifier les sources. C'est une alternative aux
fichiers @file{.patch} traditionnels, plus proche de l'esprit de Guix. À
cause de la quote, le code n'est évalué que lorsqu'il est passé au démon
Guix pour la construction. Il peut y avoir autant de bout de code que
nécessaire.

Les bouts de code on parfois besoin de modules Guile supplémentaires qui
peuvent être importés dans le champ @code{modules}.

@subsubsection Entrées

Il y a trois types d'entrées. En résumé :

@table @asis
@item native-inputs
Requis pour construire mais pas à l'exécution -- installer un paquet avec un
substitut n'installera pas ces entrées.
@item inputs
Installées dans le dépôt mais pas dans le profil, et présentes à la
construction.
@item propagated-inputs
Installées dans le dépôt et dans le profil, et présentes à la construction.
@end table

@xref{Référence de package,,, guix.fr, le manuel de référence de GNU Guix}
pour plus de détails.

La différence entre les différents types d'entrées est importante : si une
dépendance peut être utilisée comme @emph{entrée} plutôt que comme
@emph{entrée propagée}, il faut faire ça, sinon elle « polluera » le profil
utilisateur sans raison.

Par exemple, si vous installez un programme graphique qui dépend d'un outil
en ligne de commande, vous êtes probablement intéressé uniquement par la
partie graphique, donc inutile de forcer l'outil en ligne de commande à être
présent dans le profil utilisateur. Les dépendances sont gérés par les
paquets, pas par les utilisateurs et utilisatrices. Les @emph{entrées}
permettent de gérer les dépendances sans ennuyer les utilisateurs et
utilisatrices en ajoutant des fichiers exécutables (ou bibliothèque)
inutiles dans leur profil.

Pareil pour @emph{native-inputs} : une fois le programme installé, les
dépendances à la construction peuvent être supprimées sans problème par le
ramasse-miettes. Lorsqu'un substitut est disponible, seuls les
@emph{entrées} et les @emph{entrées propagées} sont récupérées : les
@emph{entrées natives} ne sont pas requises pour installer un paquet à
partir d'un substitut.

@quotation Remarque
Vous trouverez ici et là des extraits où les entrées des paquets sont
écrites assez différemment, comme ceci :

@lisp
;; « L'ancien style » pour les entrées.
(inputs
 `(("libssh2" ,libssh2)
   ("http-parser" ,http-parser)
   ("python" ,python-wrapper)))
@end lisp

C'est « l'ancien style », où chaque entrée est une liste que donne une
étiquette explicite (une chaine). C'est une méthode prise en charge mais
nous vous recommandons plutôt d'utiliser le style présenté plus
haut. @xref{Référence de package,,, guix.fr, le manuel de référence de GNU
Guix}, pour plus d'informations.
@end quotation

@subsubsection Sorties

De la même manière qu'un paquet peut avoir plusieurs entrées, il peut aussi
avoir plusieurs sorties.

Chaque sortie correspond à un répertoire différent dans le dépôt.

Vous pouvez choisir quelle sortie installer ; c'est utile pour préserver
l'espace disque et éviter de polluer le profil utilisateur avec des
exécutables et des bibliothèques inutiles.

La séparation des sorties est facultative. Lorsque le champ @code{outputs}
n'est pas spécifié, l'unique sortie par défaut (le paquet complet donc) est
@code{"out"}.

Les sorties séparées sont en général @code{debug} et @code{doc}.

Vous devriez séparer les sorties seulement si vous pouvez montrer que c'est
utile : si la taille de la sortie est importante (vous pouvez comparer avec
@code{guix size}) ou si le paquet est modulaire.

@subsubsection Arguments du système de construction

Le champ @code{arguments} est une liste de mot-clés et de valeurs utilisés
pour configurer le processus de construction.

L'argument le plus simple est @code{#:tests?} et on l'utilise pour
désactiver la suite de tests pendant la construction du paquet. C'est
surtout utile si le paquet n'a pas de suite de tests. Nous vous recommandons
fortement de laisser tourner la suite de tests s'il y en a une.

Un autre argument courant est @code{#:make-flags}, qui spécifie une liste de
drapeaux à ajouter en lançant make, comme ce que vous feriez sur la ligne de
commande. Par exemple, les drapeaux suivants

@lisp
#:make-flags (list (string-append "prefix=" (assoc-ref %outputs "out"))
                   "CC=gcc")
@end lisp

se traduisent en

@example
$ make CC=gcc prefix=/gnu/store/...-<out>
@end example

Cela indique que le compilateur C sera @code{gcc} et la variable
@code{prefix} (le répertoire d'installation pour Make) sera @code{(assoc-ref
%outputs "out")}, qui est une variable globale côté construction qui pointe
vers le répertoire de destination dans le dépôt (quelque chose comme
@file{/gnu/store/…-my-libgit2-20180408}).

De manière identique, vous pouvez indiquer les drapeaux de configuration :

@lisp
#:configure-flags '("-DUSE_SHA1DC=ON")
@end lisp

La variable @code{%build-inputs} est aussi générée dans cette portée. C'est
une liste d'association qui fait correspondre les noms des entrées à leur
répertoire dans le dépôt.

Le mot-clé @code{phases} liste la séquence d'étapes du système de
construction. Les phases usuelles sont @code{unpack}, @code{configure},
@code{build}, @code{install} et @code{check}. Pour en savoir plus, vous
devez trouver la bonne définition du système de construction dans
@samp{$GUIX_CHECKOUT/guix/build/gnu-build-system.scm} :

@lisp
(define %standard-phases
  ;; Phases de construction standards, en tant que liste paires de symbole/procédure.
  (let-syntax ((phases (syntax-rules ()
                         ((_ p ...) `((p . ,p) ...)))))
    (phases set-SOURCE-DATE-EPOCH set-paths install-locale unpack
            bootstrap
            patch-usr-bin-file
            patch-source-shebangs configure patch-generated-file-shebangs
            build check install
            patch-shebangs strip
            validate-runpath
            validate-documentation-location
            delete-info-dir-file
            patch-dot-desktop-files
            make-dynamic-linker-cache
            install-license-files
            reset-gzip-timestamps
            compress-documentation)))
@end lisp

Ou depuis la REPL :

@lisp
(add-to-load-path "/path/to/guix/checkout")
,use (guix build gnu-build-system)
(map car %standard-phases)
@result{} (set-SOURCE-DATE-EPOCH set-paths install-locale unpack bootstrap patch-usr-bin-file patch-source-shebangs configure patch-generated-file-shebangs build check install patch-shebangs strip validate-runpath validate-documentation-location delete-info-dir-file patch-dot-desktop-files make-dynamic-linker-cache install-license-files reset-gzip-timestamps compress-documentation)
@end lisp

Si vous voulez en apprendre plus sur ce qui arrive pendant ces phases,
consultez les procédures associées.

Par exemple, au moment d'écrire ces lignes, la définition de @code{unpack}
dans le système de construction de GNU est :

@lisp
(define* (unpack #:key source #:allow-other-keys)
  "Unpack SOURCE in the working directory, and change directory within the
source.  When SOURCE is a directory, copy it in a sub-directory of the current
working directory."
  (if (file-is-directory? source)
      (begin
        (mkdir "source")
        (chdir "source")

        ;; Préserve les horodatages (remis à zéro) sur l'arborescence copiée pour que
        ;; tout fonctionne de manière déterministe.
        (copy-recursively source "."
                          #:keep-mtime? #t)
        ;; Rend les fichiers sources du checkout inscriptibles, pour que ce soit plus pratique
        (for-each (lambda (f)
                    (false-if-exception (make-file-writable f)))
                  (find-files ".")))
      (begin
        (cond
         ((string-suffix? ".zip" source)
          (invoke "unzip" source))
         ((tarball? source)
          (invoke "tar" "xvf" source))
         (else
          (let ((name (strip-store-file-name source))
                (command (compressor source)))
            (copy-file source name)
            (when command
              (invoke command "--decompress" name)))))
        ;; Essaye de se déplacer dans le répertoire enfant.
        (and=> (first-subdirectory ".") chdir))))
@end lisp

Remarquez l'appel à @code{chdir} : il change de répertoire courant vers la
source qui vient d'être décompressée. Ainsi toutes les phases suivantes
utiliseront le répertoire des sources comme répertoire de travail, ce qui
explique qu'on peut travailler directement sur les fichiers sources. Du
moins, tant qu'une phase suivante ne change pas le répertoire de travail.

Nous modifions la liste des @code{%standard-phases} du système de
construction avec la macro @code{modify-phases} qui indique la liste des
modifications, sous cette formes :

@itemize
@item
@code{(add-before @var{phase} @var{nouvelle-phase} @var{procédure})} : Lance
une @var{procédure} nommée @var{nouvelle-phase} avant @var{phase}.
@item
@code{(add-after @var{phase} @var{nouvelle-phase} @var{procédure})} :
Pareil, mais après la @var{phase}.
@item
@code{(replace @var{phase} @var{procédure})}.
@item
@code{(delete @var{phase})}.
@end itemize

La @var{procédure} prend en charge les arguments @code{inputs} et
@code{outputs} sous forme de mot-clés. Les entrées (@emph{natives},
@emph{propagées} et simples) et répertoires de sortie sont référencés par
leur nom dans ces variables. Ainsi @code{(assoc-ref outputs "out")} est le
répertoire du dépôt de la sortie principale du paquet. Une procédure de
phase ressemble à cela :

@lisp
(lambda* (#:key inputs outputs #:allow-other-keys)
  (let ((bash-directory (assoc-ref inputs "bash"))
        (output-directory (assoc-ref outputs "out"))
        (doc-directory (assoc-ref outputs "doc")))
    ;; ... ))
@end lisp

Sa valeur de retour est ignorée.

@subsubsection Échelonnage du code

Si vous avez été attentif, vous aurez remarqué la quasi-quote et la virgule
dans le champ argument. En effet, le code de construction dans la
déclaration du paquet ne doit pas être évalué côté client, mais seulement
après avoir été passé au démon Guix. Ce mécanisme de passage de code entre
deux processus s'appelle @uref{https://arxiv.org/abs/1709.00833,
l'échelonnage de code}.

@subsubsection Fonctions utilitaires

Lorsque vous modifiez les @code{phases}, vous aurez souvent besoin d'écrire
du code qui ressemble aux invocation équivalentes (@code{make},
@code{mkdir}, @code{cp}, etc) couramment utilisées durant une installatio
plus standard dans le monde Unix.

Certaines comme @code{chmod} sont natives dans Guile. @xref{,,, guile, Guile
reference manual} pour une liste complète.

Guix fournit des fonctions utilitaires supplémentaires qui sont
particulièrement utiles pour la gestion des paquets.

Certaines de ces fonctions se trouvent dans
@samp{$GUIX_CHECKOUT/guix/guix/build/utils.scm}. La plupart copient le
comportement des commandes systèmes Unix traditionnelles :

@table @code
@item which
Fonctionne comme la commande système @samp{which}.
@item find-files
Fonctionne un peu comme la commande @samp{find}.
@item mkdir-p
Fonctionne comme @samp{mkdir -p}, qui crée tous les parents si besoin.
@item install-file
Fonctionne comme @samp{install} pour installer un fichier vers un répertoire
(éventuellement non existant). Guile a @code{copy-file} qui fonctionne comme
@samp{cp}.
@item copy-recursively
Fonctionne comme @samp{cp -r}.
@item delete-file-recursively
Fonctionne comme @samp{rm -rf}.
@item invoke
Lance un exécutable. Vous devriez utiliser cela à la place de
@code{system*}.
@item with-directory-excursion
Lance le corps dans un répertoire de travail différent, puis revient au
répertoire de travail précédent.
@item substitute*
Une fonction similaire à @command{sed}.
@end table

@xref{Utilitaires de construction,,, guix.fr, le manuel de référence de GNU
Guix}, pour plus d'informations sur ces utilitaires.

@subsubsection Préfixe de module

La licence dans notre dernier exemple a besoin d'un préfixe à cause de la
manière dont le module @code{licenses} a été importé dans le paquet, avec
@code{#:use-module ((guix licenses) #:prefix license:)}. Le mécanisme
d'import de module de Guile (@pxref{Using Guile Modules,,, guile, Guile
reference manual}) permet de contrôler complètement l'espace de nom. Cela
évite les conflits entre, disons, la variable @samp{zlib} de
@samp{licenses.scm} (un @emph{licence}) et la variable @samp{zlib} de
@samp{compression.scm} (un @emph{paquet}).

@node Autres systèmes de construction
@subsection Autres systèmes de construction

Ce que nous avons vu jusqu'ici couvre la majeur partie des paquets qui
utilisent un système de construction autre que
@code{trivial-build-system}. Ce dernier n'automatise rien et vous laisse
tout construire par vous-même. C'est plus exigeant et nous n'en parlerons
pas pour l'instant, mais heureusement il est rarement nécessaire d'aller
jusqu'à ces extrémités.

Pour les autres systèmes de construction, comme ASDF, Emacs, Perl, Ruby et
bien d'autres, le processus est très similaire à celui du système de
construction de GNU en dehors de quelques arguments spécialisés.

@xref{Systèmes de construction,,, guix.fr, le manuel de référence de GNU
Guix}, pour plus d'informations sur les systèmes de construction, ou voir le
code source dans les répertoires @samp{$GUIX_CHECKOUT/guix/build} et
@samp{$GUIX_CHECKOUT/guix/build-system}.

@node Définition programmable et automatisée
@subsection Définition programmable et automatisée

Nous ne le répéterons jamais assez : avoir un langage de programmation
complet à disposition nous permet de faire bien plus de choses que la
gestion de paquets traditionnelle.

Illustrons cela avec certaines fonctionnalités géniales de Guix !

@menu
* Les importateurs récursifs::  Les importateurs récursifs
* Mise à jour automatique::  Mise à jour automatique
* Héritage::                 Héritage
@end menu

@node Les importateurs récursifs
@subsubsection Les importateurs récursifs

Certains systèmes de constructions sont si bons qu'il n'y a presque rien à
écrire pour créer un paquet, au point que cela devient rapidement répétitif
et pénible. L'une des raisons d'être des ordinateurs est de remplacer les
êtres humains pour ces taches barbantes. Disons donc à Guix de faire cela
pour nous et de créer les définitions de paquets pour un paquet R venant de
CRAN (la sortie est coupée par souci de place) :

@example
$ guix import cran --recursive walrus

(define-public r-mc2d
    ; ...
    (license gpl2+)))

(define-public r-jmvcore
    ; ...
    (license gpl2+)))

(define-public r-wrs2
    ; ...
    (license gpl3)))

(define-public r-walrus
  (package
    (name "r-walrus")
    (version "1.0.3")
    (source
      (origin
        (method url-fetch)
        (uri (cran-uri "walrus" version))
        (sha256
          (base32
            "1nk2glcvy4hyksl5ipq2mz8jy4fss90hx6cq98m3w96kzjni6jjj"))))
    (build-system r-build-system)
    (propagated-inputs
      (list r-ggplot2 r-jmvcore r-r6 r-wrs2))
    (home-page "https://github.com/jamovi/walrus")
    (synopsis "Robust Statistical Methods")
    (description
      "This package provides a toolbox of common robust statistical
tests, including robust descriptives, robust t-tests, and robust ANOVA.
It is also available as a module for 'jamovi' (see
<https://www.jamovi.org> for more information).  Walrus is based on the
WRS2 package by Patrick Mair, which is in turn based on the scripts and
work of Rand Wilcox.  These analyses are described in depth in the book
'Introduction to Robust Estimation & Hypothesis Testing'.")
    (license gpl3)))
@end example

L'importateur récursif n'importera pas les paquets pour lesquels Guix a déjà
une définition, sauf pour le tout premier.

Toutes les applications ne peuvent pas être empaquetées de cette manière,
seules celles qui s'appuient sur un nombre restreint de systèmes pris en
charge le peuvent. Vous trouverez la liste complète des importateurs dans la
section dédiée du manuel (@pxref{Invoquer guix import,,, guix.fr, le manuel
de référence de GNU}).

@node Mise à jour automatique
@subsubsection Mise à jour automatique

Guix peut être assez intelligent pour vérifier s'il y a des mises à jour sur
les systèmes qu'il connaît. Il peut rapporter les paquets anciens avec

@example
$ guix refresh hello
@end example

La plupart du temps, mettre à jour un paquet vers une nouvelle version ne
demande pas beaucoup plus que de changer le numéro de version et la somme de
contrôle. Guix peut aussi le faire automatiquement :

@example
$ guix refresh hello --update
@end example

@node Héritage
@subsubsection Héritage

Si vous avez commencé à regarder des définitions de paquets existantes, vous
avez peut-être remarqué qu'un certain nombre d'entre elles ont un champ
@code{inherit} :

@lisp
(define-public adwaita-icon-theme
  (package (inherit gnome-icon-theme)
    (name "adwaita-icon-theme")
    (version "3.26.1")
    (source (origin
              (method url-fetch)
              (uri (string-append "mirror://gnome/sources/" name "/"
                                  (version-major+minor version) "/"
                                  name "-" version ".tar.xz"))
              (sha256
               (base32
                "17fpahgh5dyckgz7rwqvzgnhx53cx9kr2xw0szprc6bnqy977fi8"))))
    (native-inputs (list `(,gtk+ "bin")))))
@end lisp

Tous les champs non spécifiés héritent du paquet parent. C'est très pratique
pour créer un paquet alternatif, par exemple avec une source, une version ou
des options de compilation différentes.

@node Se faire aider
@subsection Se faire aider

Malheureusement, certaines applications peuvent être difficiles à
empaqueter. Parfois elles ont besoin d'un correctif pour fonctionner avec la
hiérarchie du système de fichiers non standard imposée par de dépôt. Parfois
les tests ne se lancent pas correctement (vous pouvez les passer mais ce
n'est pas recommandé). Parfois le paquet n'est pas reproductible.

Si vous êtes bloqué·e, incapable de trouver comme corriger un problème
d'empaquetage, n'hésitez pas à demander de l'aide à la communauté.

Voir la @uref{https://guix.gnu.org/fr/contact/,page d'accueil de Guix} pour
plus d'informations sur les listes de diffusion, IRC, etc.

@node Conclusion
@subsection Conclusion

Ce didacticiel vous a montré la gestion des paquets sophistiquée dont Guix
se targue. Maintenant, nous avons restreint cette introduction au système
@code{gnu-build-system} qui est un niveau d'abstraction essentiel sur lequel
des niveaux d'abstraction plus avancés se reposent.

Comment continuer ? Nous devrions ensuite disséquer le fonctionnement
interne des systèmes de construction en supprimant toutes les abstractions,
avec le @code{trivial-build-system} : cela vous permettra de bien comprendre
le processus avant de voir des techniques plus avancées et certains cas
particuliers.

D'autres fonctionnalités que vous devriez explorer sont l'édition
interactive et les possibilités de débogage de Guix fournies par la REPL de
Guile.

Ces fonctionnalités avancées sont complètement facultatives et peuvent
attendre ; maintenant vous devriez prendre une pause bien méritée. Avec ce
dont nous venons de parler ici vous devriez être bien armé·e pour empaqueter
de nombreux paquets. Vous pouvez commencer dès maintenant et on espère voir
votre contribution bientôt !

@node Références
@subsection Références

@itemize
@item
La
@uref{https://guix.gnu.org/manual/devel/fr/html_node/Definition-des-paquets.html,
référence des paquets dans le manuel}

@item
@uref{https://gitlab.com/pjotrp/guix-notes/blob/master/HACKING.org, le guide
de bidouillage de GNU Guix de Pjotr}

@item
@uref{https://guix.gnu.org/guix-ghm-andreas-20130823.pdf, « GNU Guix:
Package without a scheme! »}, d'Andreas Enge
@end itemize

@node Fonctionnement de l'empaquetage
@section Fonctionnement de l'empaquetage

Les section suivantes montrent des exemples réels de la manière de
travailler avec des systèmes de construction particuliers, et servent
d'extension au guide d'empaquetage (@pxref{Consignes d'empaquetage,,,
guix.fr, le manuel de référence de GNU Guix}).

@menu
* Empaqueter des crates Rust::  Empaqueter des crates Rust
@end menu

@node Empaqueter des crates Rust
@subsection Empaqueter des crates Rust

Pour commencer, ajoutez les paquets suivants à votre environnement :

@example
$ guix shell rust rust:cargo cargo-audit cargo-license
@end example

@menu
* Méthode de travail habituelle pour l'empaquetage Rust::  Méthode de 
                                                             travail 
                                                             habituelle pour 
                                                             l'empaquetage 
                                                             Rust
* Espaces de travail Cargo et images de développement::  Espaces de travail 
                                                           Cargo et images de 
                                                           développement
* Utiliser les bibliothèques Rust dans d'autres systèmes de construction::  Utiliser les bibliothèques Rust dans d'autres systèmes de construction
@end menu

@node Méthode de travail habituelle pour l'empaquetage Rust
@subsubsection Méthode de travail habituelle pour l'empaquetage Rust

Dans cet exemple, nous empaquetterons @code{cargo-audit}, qui est publié sur
@uref{https://crates.io, crates.io}, le dépôt de paquets Rust. Toutes ses
dépendances sont également sur crates.io.

@enumerate
@item
Comme @code{cargo-audit} est disponible sur crates.io, nous pouvons générer
un modèle avec l'importateur crates.io (@pxref{Invoquer guix import,,,
guix.fr, le manuel de référence de GNU Guix}) :

@example
$ guix import crate cargo-audit
@end example

Après l'avoir modifié manuellement, nous obtiendrons la définition suivante
:

@lisp
(define-public cargo-audit
  (package
    (name "cargo-audit")
    (version "0.21.2")
    (source
     (origin
       (method url-fetch)
       (uri (crate-uri "cargo-audit" version))
       (file-name (string-append name "-" version ".tar.gz"))
       (sha256
        (base32 "1a00yqpckkw86zh2hg7ra82c5fx0ird5766dyynimbvqiwg2ps0n"))))
    (build-system cargo-build-system)
    (arguments (list #:install-source? #f))
    (inputs (cargo-inputs 'cargo-audit))
    (home-page "https://rustsec.org/")
    (synopsis "Audit Cargo.lock for crates with security vulnerabilities")
    (description
     "This package provides a Cargo subcommand, @@command@{cargo audit@}, to
audit @@file@{Cargo.lock@} for crates with security vulnerabilities.")
    (license (list license:asl2.0 license:expat))))
@end lisp

L'identifiant utilisé pour invoquer @code{cargo-inputs}, dans notre cas de
@code{'cargo-audit}, doit être unique, et en général correspond au nom de la
variable du paquet.

@item
Désarchivez les sources du paquet et déplacez-vous dans le répertoire
désarchivé, puis exécutez les commandes suivantes :

@example
$ cargo generate-lockfile
$ cargo audit
$ cargo license
@end example

@command{cargo generate-lockfile} met à jour les dépendances vers des
versions compatibles. L'appliquer à toutes les applications Rust permet de
réduire drastiquement le nombre de bibliothèque Rust que nous devrons
vérifier plus tard. Bien que certaines bibliothèques puissent ne pas suivre
le @uref{https://semver.org/, versionnement sémantique}, cela reste
acceptable.

@command{cargo audit} vérifie les vulnérabilité connues et @command{cargo
license} vérifie les licences, pour toutes les dépendances. Nous devons
obtenir une sortie acceptable de la part de @command{cargo audit} et nous
assurer que toutes les dépendances utilisent l'une des licences que nous
prenons en charge (@pxref{Définition des paquets,,, guix.fr, le manuel de
référence de GNU Guix}).

@item
Importez les dépendances à partir du lockfile généré :

@example
$ guix import --insert=gnu/packages/rust-crates.scm \
      crate --lockfile=/path/to/Cargo.lock cargo-audit

# Ou utilisez les options courtes, dans notre cas le shell traite les noms de fichier
# avant de les passer à Guix, ce qui permet l'expansion du tilde par exemple.
$ guix import -i gnu/packages/rust-crates.scm \
      crate -f /path/to/Cargo.lock cargo-audit
@end example

@code{cargo-audit} ici doit être cohérent avec les identifiants utilisés
pour l'invocation @code{cargo-inputs} dans la définition du paquet.

À cette étape, le paquet @code{cargo-audit} peut être construit.

@item
Finally we'll unbundle the vendored dependencies.  The lockfile importer
inserts @code{TODO REVIEW:} comments for libraries with high probability of
bundled dependencies.  @code{cargo-build-system} also performs additional
check for binary files in its @code{check-for-pregenerated-files} phase,
which usually indicates bundling:

@example
$ ./pre-inst-env guix build cargo-audit
@dots{}
starting phase `check-for-pregenerated-files'
Searching for binary files...
./guix-vendor/rust-async-compression-0.4.21.tar.gz/tests/artifacts/dictionary-rust
./guix-vendor/rust-async-compression-0.4.21.tar.gz/tests/artifacts/dictionary-rust-other
./guix-vendor/rust-async-compression-0.4.21.tar.gz/tests/artifacts/lib.rs.zst
./guix-vendor/rust-async-compression-0.4.21.tar.gz/tests/artifacts/long-window-size-lib.rs.zst
./guix-vendor/rust-winapi-i686-pc-windows-gnu-0.4.0.tar.gz/lib/libwinapi_aclui.a
./guix-vendor/rust-winapi-i686-pc-windows-gnu-0.4.0.tar.gz/lib/libwinapi_activeds.a
./guix-vendor/rust-winapi-i686-pc-windows-gnu-0.4.0.tar.gz/lib/libwinapi_asycfilt.a
./guix-vendor/rust-winapi-i686-pc-windows-gnu-0.4.0.tar.gz/lib/libwinapi_amsi.a
@dots{}
@end example

Bien que les bibliothèques Rust ne soient pas publiquement exportées, nous
pouvons toujours les sélectionner via la ligne de commande Guix à travers
une expression Guile :

@example
$ guix build --expression='(@@@@ (gnu packages rust-crates) rust-ring-0.17.14)'
@end example

Pour retirer les dépendances embarquées, un bout de code est suffisant :

@lisp
(define rust-curl-sys-0.4.80+curl-8.12.1
  (crate-source "curl-sys" "0.4.80+curl-8.12.1"
                "0d7ppx4kq77hc5nyff6jydmfabpgd0i3ppjvn8x0q833mhpdzxsm"
                #:snippet '(delete-file-recursively "curl")))
@end lisp

@lisp
(define rust-bzip2-sys-0.1.13+1.0.8
  (crate-source "bzip2-sys" "0.1.13+1.0.8"
                "056c39pgjh4272bdslv445f5ry64xvb0f7nph3z7860ln8rzynr2"
                #:snippet
                '(begin
                   (delete-file-recursively "bzip2-1.0.8")
                   (delete-file "build.rs")
                   (with-output-to-file "build.rs"
                     (lambda _
                       (format #t "fn main() @{~@@
                        println!(\"cargo:rustc-link-lib=bz2\");~@@
                        @}~%"))))))
@end lisp

Dans un cas plus complexe, où le retrait d'une dépendance embarquée a besoin
d'un processus de construction qui utilise d'autres paquets, nous devrions
d'abord créer un paquet complet dans @code{(gnu packages rust-sources)} et
le référencer dans la définition importée.

Par exemple, nous avons défini @code{rust-ring-0.17} dans @code{(gnu
packages rust-sources)}, puis la définition importée dans @code{(gnu
packages rust-crates)} devrait être modifiée pour le référencer.

@lisp
(define rust-ring-0.17.14 rust-ring-0.17)
@end lisp

Lorsqu'une dépendance peut être supprimée sans problème, modifiez-la en
@code{#f}.

@lisp
(define rust-openssl-src-300.4.2+3.4.1 #f)
@end lisp

Pour faciliter diverses tâches dans le fonctionnement général, plusieurs
scripts sont fournis dans le répertoire @file{etc/teams/rust} de
l'arborescence des sources de Guix.
@end enumerate

@node Espaces de travail Cargo et images de développement
@subsubsection Espaces de travail Cargo et images de développement

Dans cet exemple, nous allons empaqueter @code{niri}, qui dépend d'une image
de développement (et d'espaces de travail Cargo).

Comme nous ne pouvons pas assurer la compatibilité d'une image de
développement, avant d'exécuter @command{cargo generate-lockfile}, nous
devrions modifier @file{Cargo.toml} pour le lier à une révision que l'on
sait fonctionner.

Pour utiliser nos images de développement empaquetées, il est également
nécessaire de modifier @file{Cargo.toml} dans la phase build, avec un motif
de substitution spécifique au paquet.

@lisp
(define-public niri
  (package
   (name "niri")
   (version "25.02")
   (source (origin
             (method git-fetch)
             (uri (git-reference
                   (url "https://github.com/YaLTeR/niri")
                   (commit (string-append "v" version))))
             (file-name (git-file-name name version))
             (sha256
              (base32
               "0vzskaalcz6pcml687n54adjddzgf5r07gggc4fhfsa08h1wfd4r"))))
   (build-system cargo-build-system)
   (arguments
    (list #:install-source? #f
          #:phases
          #~(modify-phases %standard-phases
              (add-after 'unpack 'use-guix-vendored-dependencies
                (lambda _
                  (substitute* "Cargo.toml"
                    (("# version =.*")
                     "version = \"*\"")
                    (("git.*optional")
                     "version = \"*\", optional")
                    (("^git = .*")
                     "")))))))
   (native-inputs
    (list pkg-config))
   (inputs
    (cons* clang
           libdisplay-info
           libinput-minimal
           libseat
           libxkbcommon
           mesa
           pango
           pipewire
           wayland
           (cargo-inputs 'niri)))
   (home-page "https://github.com/YaLTeR/niri")
   (synopsis "Scrollable-tiling Wayland compositor")
   (description
    "Niri is a scrollable-tiling Wayland compositor which arranges windows in a
scrollable format.  It is considered stable for daily use and performs most
functions expected of a Wayland compositor.")
   (license license:gpl3+)))
@end lisp

@code{niri} a des dépendances à un espace de travail Cargo. Lorsque vous
empaquetez une dépendance à un espace de travail Cargo, le paramètre
@code{#:cargo-package-crates} est requis.

@lisp
(define-public rust-pipewire-0.8.0.fd3d8f7
  (let ((commit "fd3d8f7861a29c2eeaa4c393402e013578bb36d9")
        (revision "0"))
    (package
      (name "rust-pipewire")
      (version (git-version "0.8.0" revision commit))
      (source
       (origin
         (method git-fetch)
         (uri (git-reference
               (url "https://gitlab.freedesktop.org/pipewire/pipewire-rs.git")
               (commit commit)))
         (file-name (git-file-name name version))
         (sha256
          (base32 "1hzyhz7xg0mz8a5y9j6yil513p1m610q3j9pzf6q55vdh5mcn79v"))))
      (build-system cargo-build-system)
      (arguments
       (list #:skip-build? #t
             #:cargo-package-crates
             ''("libspa-sys" "libspa" "pipewire-sys" "pipewire")))
      (inputs (cargo-inputs 'rust-pipewire-0.8.0.fd3d8f7))
      (home-page "https://pipewire.org/")
      (synopsis "Rust bindings for PipeWire")
      (description "This package provides Rust bindings for PipeWire.")
      (license license:expat))))
@end lisp

N'oubliez pas de modifier tous les membres de l'espace de travail dans
@code{(gnu packages rust-crates)} :

@lisp
(define rust-pipewire-0.8.0.fd3d8f7 package:rust-pipewire-0.8.0.fd3d8f7)
(define rust-pipewire-sys-0.8.0.fd3d8f7 package:rust-pipewire-0.8.0.fd3d8f7)
@dots{}
(define rust-libspa-0.8.0.fd3d8f7 package:rust-pipewire-0.8.0.fd3d8f7)
(define rust-libspa-sys-0.8.0.fd3d8f7 package:rust-pipewire-0.8.0.fd3d8f7)
@end lisp

@node Utiliser les bibliothèques Rust dans d'autres systèmes de construction
@subsubsection Utiliser les bibliothèques Rust dans d'autres systèmes de construction

Dans cet exemple, nous allons empaqueter @code{libchewing} qui combine deux
systèmes de construction.

Lorsque vous construisez des paquets Rust dans d'autres systèmes de
construction, nous avons besoin d'ajouter @code{rust} et @code{rust:cargo}
au @code{native-inputs}, d'importer et d'utiliser des modules des deux
systèmes de constructions, et d'appliquer les phases de construction
nécessaires du @code{cargo-build-system}.

Pour la prise en charge de la compilation croisée, nous allons également
ajouter @code{rust-sysroot} créé avec @code{(make-rust-sysroot
(%current-target-system))} aux @code{native-inputs}, et indiquer
@code{#:cargo-target} pour les phases de construction du
@code{cargo-build-system}.

@lisp
(define-public libchewing
  (package
    (name "libchewing")
    (version "0.9.1")
    (source
     (origin
       (method git-fetch)
       (uri (git-reference
             (url "https://github.com/chewing/libchewing")
             (commit (string-append "v" version))))
       (file-name (git-file-name name version))
       (sha256
        (base32 "0gh64wvrk5pn0fhmpvj1j99d5g7f7697rk96zbkc8l72yjr819z5"))))
    (build-system cmake-build-system)
    (arguments
     (list #:imported-modules
           (append %cmake-build-system-modules
                   %cargo-build-system-modules)
           #:modules
           '(((guix build cargo-build-system) #:prefix cargo:)
             (guix build cmake-build-system)
             (guix build utils))
           #:phases
           #~(modify-phases %standard-phases
               (add-after 'unpack 'prepare-cargo-build-system
                 (lambda args
                   (for-each
                    (lambda (phase)
                      (format #t "Running cargo phase: ~a~%" phase)
                      (apply (assoc-ref cargo:%standard-phases phase)
                             ;; Pour la compilation croisée.
                             #:cargo-target #$(cargo-triplet)
                             args))
                    '(unpack-rust-crates
                      configure
                      check-for-pregenerated-files
                      patch-cargo-checksums)))))))
    (native-inputs
     (append
      (list rust `(,rust "cargo") )
      ;; Pour la compilation croisée.
      (or (and=> (%current-target-system)
                 (compose list make-rust-sysroot))
          '())))
    (inputs
     (cons* corrosion ncurses sqlite (cargo-inputs 'libchewing)))
    (synopsis "Chinese phonetic input method")
    (description "Chewing is an intelligent phonetic (Zhuyin/Bopomofo) input
method, one of the most popular choices for Traditional Chinese users.")
    (home-page "https://chewing.im/")
    (license license:lgpl2.1+)))
@end lisp


@c *********************************************************************
@node Configuration du système
@chapter Configuration du système

Guix propose un langage flexible pour déclarer la configuration de votre
système Guix. Cette flexibilité peut parfois paraître écrasante. Le but de
ce chapitre est de vous montrer quelques concepts de configuration avancés.

@pxref{Configuration du système,,, guix.fr, le manuel de référence de GNU
Guix} pour une référence complète.

@menu
* Connexion automatique à un TTY donné::  Connecter automatiquement un 
                                            utilisateur sur un TTY donné
* Personnalisation du noyau::  Créer et utiliser un noyau Linux personnalisé 
                                 sur le système Guix.
* L'API de création d'images du système Guix::  Personnaliser des images 
                                                  pour des plateformes 
                                                  particulières.
* Utiliser des clés de sécurité::  Comment utiliser des clés de sécurité 
                                     avec le système Guix.
* Tâche mcron pour le DNS dynamique::  Tâche pour mettre à jour l'adresse IP 
                                         derrière un nom d'hôte DuckDNS.
* Se connecter à un VPN Wireguard::  Se connecter à un VPN Wireguard.
* Personnaliser un gestionnaire de fenêtres::  Gérer la personnalisation 
                                                 d'un gestionnaire de fenêtres 
                                                 sur le système Guix.
* Lancer Guix sur un serveur Linode::  Lancer Guix sur un serveur Linode.
* Lancer Guix sur un serveur Kimsufi::  Lancer Guix sur un serveur Kimsufi.
* Mettre en place un montage dupliqué::  Mettre en place un montage dupliqué 
                                           dans une définition de système de 
                                           fichiers.
* Récupérer des substituts via Tor::  Configurer le démon Guix pour 
                                        récupérer les substituts via Tor.
* Configurer NGINX avec Lua::  Configurer le serveur web NGINX pour qu'il 
                                 charge des modules Lua.
* Serveur de musique avec l'audio bluetooth::  Lecteur de musique sans 
                                                 affichage avec entrée 
                                                 bluetooth.
@end menu

@node Connexion automatique à un TTY donné
@section Connexion automatique à un TTY donné

Tandis que le manuel de Guix explique comment connecter automatiquement un
utilisateur sur @emph{tous} les TTY (@pxref{connexion automatique à un
TTY,,, guix.fr, le manuel de référence de Guix}), vous pourriez préférer
avoir un utilisateur connecté sur un TTY et configurer les autres TTY pour
connecter d'autres utilisateurs ou personne. Remarquez que vous pouvez
connecter automatiquement un utilisateur sur n'importe quel TTY, mais il est
recommandé d'éviter @code{tty1}, car par défaut, il est utilisé pour
afficher les avertissements et les erreurs des journaux systèmes.

Voici comment on peut configurer la connexion d'un utilisateur sur un tty :

@lisp
(define (auto-login-to-tty config tty user)
  (if (string=? tty (mingetty-configuration-tty config))
        (mingetty-configuration
         (inherit config)
         (auto-login user))
        config))

(define %my-services
  (modify-services %base-services
    ;; @dots{}
    (mingetty-service-type config =>
                           (auto-login-to-tty
                            config "tty3" "alice"))))

(operating-system
  ;; @dots{}
  (services %my-services))
@end lisp

On peut aussi utiliser @code{compose} (@pxref{Higher-Order Functions,,,
guile, The Guile Reference Manual}) avec @code{auto-login-to-tty} pour
connecter plusieurs utilisateurs sur différents ttys.

Enfin, une mise en garde. Configurer la connexion automatique à un TTY
signifie que n'importe qui peut allumer votre ordinateur et lancer des
commandes avec votre utilisateur normal. Cependant, si vous avez une
partition racine chiffrée, et donc qu'il faut déjà saisir une phrase de
passe au démarrage du système, la connexion automatique peut être un choix
pratique.


@node Personnalisation du noyau
@section Personnalisation du noyau

Guix est, en son cœur, une distribution source avec des substituts
(@pxref{Substituts,,, guix.fr, le manuel de référence de GNU Guix}), et donc
construire des paquets à partir de leur code source est normal pendant les
installations et les mis à jour de paquets. Malgré tout, c'est aussi normal
d'essayer de réduire le temps passé à compiler des paquets, et les
changements récents et futurs concernant la construction et la distribution
des substituts continue d'être un sujet de discussion dans le projet Guix.

Le noyau, bien qu'il ne demande pas énormément de RAM pour être construit,
prend assez long à construire sur une machine usuelle. La configuration du
noyau officielle, comme avec la plupart des autres distributions GNU/Linux,
penche du côté de l'inclusivité, et c'est vraiment ça qui rend la
construction aussi longue à partir des sources.

Le noyau Linux, cependant, peut aussi être décrit comme un simple paquet
comme les autres, et peut donc être personnalisé comme n'importe quel autre
paquet. La procédure est un peu différente, même si c'est surtout dû à la
nature de la définition du paquet.

Le paquet du noyau @code{linux-libre} est en fait une procédure qui crée un
paquet.

@lisp
(define* (make-linux-libre* version gnu-revision source supported-systems
                            #:key
                            (extra-version #f)
                            ;; Une fonction qui prend une architecture et une variante
                            ;; Voir kernel-config si vous voulez un exemple.
                            (configuration-file #f)
                            (defconfig "defconfig")
                            (extra-options (default-extra-linux-options version)))
  ...)
@end lisp

Le paquet @code{linux-libre} actuel pour la série 5.15.x, est déclaré comme
ceci :

@lisp
(define-public linux-libre-5.15
  (make-linux-libre* linux-libre-5.15-version
                     linux-libre-5.15-gnu-revision
                     linux-libre-5.15-source
                     '("x86_64-linux" "i686-linux" "armhf-linux"
                       "aarch64-linux" "riscv64-linux")
                     #:configuration-file kernel-config))
@end lisp

Les clés qui n'ont pas de valeur associée prennent leur valeur par défaut
dans la définition de @code{make-linux-libre}. Lorsque vous comparez les
deux bouts de code ci-dessus, remarquez le commentaire qui correspond à
@code{#:configuration-file}. À cause de cela, il n'est pas facile d'inclure
une configuration personnalisée du noyau à partir de la définition, mais ne
vous inquiétez pas, il y a d'autres moyens de travailler avec ce qu'on a.

Il existe deux manières de créer un noyau avec une configuration
personnalisée. La première consiste à donner un fichier @file{defconfig}
standard à la procédure @code{customize-linux}. Pour produire un tel
fichier, vous devez obtenir le code source du noyau Linux, créer un fichier
@file{.config} (avec la commande @code{make menuconfig}, par exemple) ou en
fournir un que vous avez déjà. Ensuite, vous devrez exécuter la commande
@code{make savedefconfig} dans l'arborescence des sources du noyau. Cela
produira le fichier @file{defconfig} que nous utiliserons pour construire un
noyau personnalisé.

Vous trouverez ci-dessous un paquet de noyau d'exemple.

@lisp
(define-public my-linux-libre
  (package
    (inherit (customize-linux
              #:linux linux-libre
              #:defconfig
              (local-file "defconfig")))
    (name "mon-linux-libre")))
@end lisp

Dans le même répertoire que le fichier définissant @code{mon-linux-libre} se
trouve un fichier nommé @file{defconfig}. De cette manière, vous pouvez
fournir les options du noyau que vous souhaitez avoir dans le noyau qui en
résulte.

La deuxième manière de créer un noyau personnalisé est de passer une
nouvelle valeur au mot-clé @code{extra-options} de la procédure
@code{make-linux-libre}. Le mot-clé @code{extra-options} fonctionne avec une
autre fonction définie juste en dessous :

@lisp
(define (default-extra-linux-options version)
  `(;; https://lists.gnu.org/archive/html/guix-devel/2014-04/msg00039.html
   ("CONFIG_DEVPTS_MULTIPLE_INSTANCES" . #true)
   ;; Modules requis pour initrd :
   ("CONFIG_NET_9P" . m)
   ("CONFIG_NET_9P_VIRTIO" . m)
   ("CONFIG_VIRTIO_BLK" . m)
   ("CONFIG_VIRTIO_NET" . m)
   ("CONFIG_VIRTIO_PCI" . m)
   ("CONFIG_VIRTIO_BALLOON" . m)
   ("CONFIG_VIRTIO_MMIO" . m)
   ("CONFIG_FUSE_FS" . m)
   ("CONFIG_CIFS" . m)
   ("CONFIG_9P_FS" . m)))

(define (config->string options)
  (string-join (map (match-lambda
                      ((option . 'm)
                       (string-append option "=m"))
                      ((option . #true)
                       (string-append option "=y"))
                      ((option . #false)
                       (string-append option "=n")))
                    options)
               "\n"))
@end lisp

Et dans le script configure personnalisé du paquet « make-linux-libre » :

@lisp
;; Appending works even when the option wasn't in the
;; file.  The last one prevails if duplicated.
(let ((port (open-file ".config" "a"))
      (extra-configuration ,(config->string extra-options)))
  (display extra-configuration port)
  (close-port port))

(invoke "make" "oldconfig")
@end lisp

Donc, en ne fournissant pas de fichier de configuration le fichier
@file{.config} est au départ vide et on écrit ensuite l'ensemble des
drapeaux que l'on veut. Voici un autre noyau personnalisé :

@lisp
(define %macbook41-full-config
  (append %macbook41-config-options
          %file-systems
          %efi-support
          %emulation
          ((@@@@ (gnu packages linux) default-extra-linux-options) version)))

(define-public linux-libre-macbook41
  ;; XXX: Accède à la procédure interne « make-linux-libre* », qui est privée
  ;; et n'est pas exportée, et pourrait changer dans le futur.
  ((@@@@ (gnu packages linux) make-linux-libre*)
   (@@@@ (gnu packages linux) linux-libre-version)
   (@@@@ (gnu packages linux) linux-libre-gnu-revision)
   (@@@@ (gnu packages linux) linux-libre-source)
   '("x86_64-linux")
   #:extra-version "macbook41"
   #:extra-options %macbook41-config-options))
@end lisp

Dans l'exemple ci-dessus @code{%file-systems} est un ensemble de drapeaux
qui activent la prise en charge de différents systèmes de fichiers,
@code{%efi-support} active la prise en charge de l'EFI et @code{%emulation}
permet à une machine x86_64-linux de fonctionner aussi en mode 32-bits. La
procédure @code{default-extra-linux-options} est définie plus haut, et elles
devaient être utilisée pour éviter de perdre les options de configuration
par défaut dans le mot-clé @code{extra-options}.

Tout ça est bien beau, mais comment savoir quels modules sont requis pour un
système en particulier ? Il y a deux ressources qui peuvent être utiles pour
répondre à cette question : le
@uref{https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Kernel,
manuel de Gentoo} et la
@uref{https://www.kernel.org/doc/html/latest/admin-guide/README.html?highlight=localmodconfig,
documentation du noyau}. D'après la documentation du noyau, il semble que la
commande @code{make localmodconfig} soit la bonne.

Pour lancer @code{make localmodconfig} on doit d'abord récupérer et
décompresser le code source du noyau :

@example shell
tar xf $(guix build linux-libre --source)
@end example

Une fois dans le répertoire contenant le code source lancez @code{touch
.config} pour créer un fichier @file{.config} initialement vide pour
commencer. @code{make localmodconfig} fonctionne en remarquant que avec déjà
un @file{.config} et en vous disant ce qu'il vous manque. Si le fichier est
vide, il vous manquera tout ce qui est nécessaire. L'étape suivante consiste
à lancer :

@example shell
guix shell -D linux-libre -- make localmodconfig
@end example

et regardez la sortie. Remarquez que le fichier @file{.config} est toujours
vide. La sortie contient en général deux types d'avertissements. Le premier
commence par « WARNING » et peut être ignoré dans notre cas. Le deuxième dit
:

@example shell
module pcspkr did not have configs CONFIG_INPUT_PCSPKR
@end example

Pour chacune de ces lignes, copiez la partie @code{CONFIG_XXXX_XXXX} dans le
@file{.config} du répertoire et ajoutez @code{=m} pour qu'à la fin il
ressemble à cela :

@example shell
CONFIG_INPUT_PCSPKR=m
CONFIG_VIRTIO=m
@end example

Après avoir copié toutes les options de configuration, lancez @code{make
localmodconfig} de nouveau pour vous assurer que vous n'avez pas de sortie
commençant par « module ». Après tous ces modules spécifiques à la machine,
il y en a encore quelques uns que nous devons aussi
définir. @code{CONFIG_MODULES} est nécessaire pour que nous puissions
construire et charger les modules séparément et ne pas tout construire dans
le noyau. @code{CONFIG_BLK_DEV_SD} est requis pour lire les disques durs. Il
est possible que vous aillez besoin de quelques autres modules.

Cet article n'a pas pour but de vous guider dans la configuration de votre
propre noyau cependant, donc si vous décidez de construire un noyau
personnalisé vous devrez chercher d'autres guides pour créer un noyau qui
vous convient.

La deuxième manière de configurer le noyau utilise un peu plus les
fonctionnalités de Guix et vous permettent de partager des bouts de
configuration entre différents noyaux. Par exemple, toutes les machines avec
un démarrage EFI ont besoin d'un certain nombre de configurations. Tous les
noyaux vont probablement partager une liste de systèmes de fichiers à
prendre en charge. En utilisant des variables il est facile de voir du
premier coup quelles fonctionnalités sont activées pour vous assurer que
vous n'avez pas des fonctionnalités dans un noyau qui manquent dans un
autre.

Cependant, nous ne parlons pas de la personnalisation du disque de ram
initial. Vous devrez sans doute modifier le disque de ram initial sur les
machines qui utilisent un noyau personnalisé, puisque certains modules
attendus peuvent ne pas être disponibles.

@node L'API de création d'images du système Guix
@section L'API de création d'images du système Guix

Historiquement, le système Guix est centré sur une structure
@code{operating-system}. Cette structure contient divers champs qui vont du
chargeur d'amorçage et à la déclaration du noyau aux services à installer.

Les contraintes sur l'image peuvent grandement varier en fonction de la
machine cible : une machine @code{x86_64} standard n'a pas les mêmes
contraintes qu'un petit ordinateur monocarte ARM comme le Pine64. Les
fabricants de matériel imposent donc des formats d'image distincts avec des
tailles et des emplacements de partition variables.

Pour créer des images convenables pour toutes ces machines, une nouvelle
abstraction est nécessaire : c'est le but de l'enregistrement
@code{image}. Cet enregistrement contient toutes les informations requises
pour être transformé en une image complète, qui peut être directement
démarrée sur une machine cible.

@lisp
(define-record-type* <image>
  image make-image
  image?
  (name               image-name ;symbol
                      (default #f))
  (format             image-format) ;symbol
  (target             image-target
                      (default #f))
  (size               image-size  ;size in bytes as integer
                      (default 'guess))
  (operating-system   image-operating-system  ;<operating-system>
                      (default #f))
  (partitions         image-partitions ;list of <partition>
                      (default '()))
  (compression?       image-compression? ;boolean
                      (default #t))
  (volatile-root?     image-volatile-root? ;boolean
                      (default #t))
  (substitutable?     image-substitutable? ;boolean
                      (default #t)))
@end lisp

Cet enregistrement contient le système d'exploitation à instancier. Le champ
@code{format} défini le type d'image et peut être @code{efi-raw},
@code{qcow2} ou @code{iso9660} par exemple. Plus tard, on prévoit de
l'étendre à @code{docker} et aux autres types d'images.

Un nouveau répertoire dans les sources de Guix est dédié aux définitions des
images. Pour l'instant il y a quatre fichiers :

@itemize @bullet
@item @file{gnu/system/images/hurd.scm}
@item @file{gnu/system/images/pine64.scm}
@item @file{gnu/system/images/novena.scm}
@item @file{gnu/system/images/pinebook-pro.scm}
@end itemize

Regardons le fichier @file{pine64.scm}. Il contient la variable
@code{pine64-barebones-os} qui est une définition minimale d'un système
d'exploitation dédié à la carte @b{Pine A64 LTS}.

@lisp
(define pine64-barebones-os
  (operating-system
   (host-name "vignemale")
   (timezone "Europe/Paris")
   (locale "en_US.utf8")
   (bootloader (bootloader-configuration
                (bootloader u-boot-pine64-lts-bootloader)
                (targets '("/dev/vda"))))
   (initrd-modules '())
   (kernel linux-libre-arm64-generic)
   (file-systems (cons (file-system
                        (device (file-system-label "my-root"))
                        (mount-point "/")
                        (type "ext4"))
                       %base-file-systems))
   (services (cons (service agetty-service-type
                            (agetty-configuration
                             (extra-options '("-L")) ; no carrier detect
                             (baud-rate "115200")
                             (term "vt100")
                             (tty "ttyS0")))
                   %base-services))))
@end lisp

Les champs @code{kernel} et @code{bootloader} pointent vers les paquets
dédiés à cette carte.

Ci-dessous, la variable @code{pine64-image-type} est ainsi définie.

@lisp
(define pine64-image-type
  (image-type
   (name 'pine64-raw)
   (constructor (cut image-with-os arm64-disk-image <>))))
@end lisp

Elle utilise un enregistrement dont nous n'avons pas encore parlé,
l'enregistrement @code{image-type}, défini de cette façon :

@lisp
(define-record-type* <image-type>
  image-type make-image-type
  image-type?
  (name           image-type-name) ;symbol
  (constructor    image-type-constructor)) ;<operating-system> -> <image>
@end lisp

Le but principal de cet enregistrement est d'associer un nom à une procédure
transformant un @code{operating-system} en une image. Pour comprendre
pourquoi c'est nécessaire, voyons la commande produisant une image à partir
d'un fichier de configuration de type @code{operating-system} :

@example
guix system image my-os.scm
@end example

Cette commande demande une configuration de type @code{operating-system}
mais comment indiquer que l'on veut cibler une carte Pine64 ? Nous devons
fournir l'information supplémentaire, @code{image-type}, en passant le
drapeau @code{--image-type} ou @code{-t}, de cette manière :

@example
guix system image --image-type=pine64-raw my-os.scm
@end example

Ce paramètre @code{image-type} pointe vers le @code{pine64-image-type}
défini plus haut. Ainsi, la déclaration @code{operating-system} dans
@code{my-os.scm} se verra appliquée la procédure @code{[cut image-with-os
arm64-disk-image <>)} pour la transformer en une image.

L'image qui en résulte ressemble à ceci :

@lisp
(image
 (format 'disk-image)
 (target "aarch64-linux-gnu")
 (operating-system my-os)
 (partitions
  (list (partition
         (inherit root-partition)
         (offset root-offset)))))
@end lisp

qui ajoute l'objet @code{operating-system} défini dans @code{my-os.scm} à
l'enregistrement @code{arm64-disk-image}.

Mais assez de cette folie. Qu'est-ce que cette API pour les images apporte
aux utilisateurs et utilisatrices ?

On peut lancer :

@example
mathieu@@cervin:~$ guix system --list-image-types
Les types d'image disponibles sont :

   - unmatched-raw
   - rock64-raw
   - pinebook-pro-raw
   - pine64-raw
   - novena-raw
   - hurd-raw
   - hurd-qcow2
   - qcow2
   - iso9660
   - uncompressed-iso9660
   - tarball
   - efi-raw
   - mbr-raw
   - docker
   - wsl2
   - raw-with-offset
   - efi32-raw
@end example

et en écrivant un fichier de type @code{operating-system} basé sur
@code{pine64-barebones-os}, vous pouvez personnaliser votre image selon vos
préférences dasn un fichier (@file{my-pine-os.scm}) de cette manière :

@lisp
(use-modules (gnu services linux)
             (gnu system images pine64))

(let ((base-os pine64-barebones-os))
  (operating-system
    (inherit base-os)
    (timezone "America/Indiana/Indianapolis")
    (services
     (cons
      (service earlyoom-service-type
               (earlyoom-configuration
                (prefer-regexp "icecat|chromium")))
      (operating-system-user-services base-os)))))
@end lisp

lancez :

@example
guix system image --image-type=pine64-raw my-pine-os.scm
@end example

ou bien,

@example
guix system image --image-type=hurd-raw my-hurd-os.scm
@end example

pour récupérer une image que vous pouvez écrire sur un disque dur pour
démarrer dessus.

Sans rien changer à @code{my-hurd-os.scm}, en appelant :

@example
guix system image --image-type=hurd-qcow2 my-hurd-os.scm
@end example

vous aurez une image QEMU pour le Hurd à la place.

@node Utiliser des clés de sécurité
@section Utiliser des clés de sécurité
@cindex 2FA, authentification à double facteur
@cindex U2F, second facteur universel
@cindex clé de sécurité, configuration

L'utilisation de clés de sécurité peut améliorer votre sécurité en
fournissant une seconde source d'authentification qui ne peut pas être
facilement volée ni copiée, au moins pour les adversaires à distance
(quelque chose que vous possédez) de votre secret principal (une phrase de
passe — quelque chose que vous connaissez), ce qui réduit les risques de vol
d'identité.

L'exemple de configuration détaillée plus bas montre la configuration
minimale dont vous avez besoin sur votre système Guix pour permettre
l'utilisation d'une clé de sécurité Yubico. Nous espérons que la
configuration puisse être utile pour d'autres clés de sécurité aussi, avec
quelques ajustements.

@subsection Configuration pour l'utiliser comme authentification à double facteur (2FA)

Pour être utilisable, les règles udev du systèmes doivent être étendues avec
des règles spécifiques à la clé. Ce qui suit montre comment étendre vos
règles udev avec le fichier de règles @file{lib/udev/rules.d/70-u2f.rules}
fournit par le paquet @code{libfido2} du module @code{(gnu packages
security-token)} et ajouter votre utilisateur au groupe @samp{"plugdev"}
qu'il utilise :

@lisp
(use-package-modules ... security-token ...)
...
(operating-system
 ...
 (users (cons* (user-account
               (name "your-user")
               (group "users")
               (supplementary-groups
		'("wheel" "netdev" "audio" "video"
                  "plugdev"))           ;<- added system group
               (home-directory "/home/your-user"))
              %base-user-accounts))
 ...
 (services
  (cons*
   ...
   (udev-rules-service 'fido2 libfido2 #:groups '("plugdev")))))
@end lisp

Après la reconfiguration de votre système et vous être authentifié dans
votre session graphique pour que le nouveau groupe prenne effet pour votre
utilisateur, vous pouvez vérifier que la clé est utilisable en exécutant :

@example
guix shell ungoogled-chromium -- chromium chrome://settings/securityKeys
@end example

et en validant que la clé de sécurité peut être remise à zéro via le menu «
réinitialiser votre clé de sécurité ». Si cela fonctionne, bravo, votre clé
de sécurité est prête à être utilisée avec les applications qui prennent en
charge l'authentification à double facteur (2FA).

@subsection Désactiver la génération de code OTP pour une Yubikey
@cindex désactiver l'OTP d'une yubikey
Si vous utilisez une clé de sécurité Yubikey et que vous n'aimez pas les
mauvais codes OTP qu'il génère lorsque vous touchez la clé par accident
(p. ex. en vous faisant passer pour un spammer dans le canal @samp{#guix}
alors que vous discutez depuis votre client IRC préféré !), vous pouvez les
désactiver avec la commande @command{ykman} suivante :

@example
guix shell python-yubikey-manager -- ykman config usb --force --disable OTP
@end example

Autrement, vous pouvez utiliser la commande @command{ykman-gui} fournie par
le paquet @code{yubikey-manager-qt} et soit désactiver complètement
l'application @samp{OTP} pour l'interface USB ou, depuis la vue
@samp{Applications -> OTP}, supprimer la configuration du slot 1, qui est
pré-configuré avec l'application Yubico OTP.

@subsection Demander une Yubikey pour ouvrir une base de données KeePassXC
@cindex yubikey, intégration avec keepassxc
Le gestionnaire de mots de passe KeePassXC prend en charge les Yubikeys,
mais cela nécessite d'installer des règles udev pour votre système Guix et
de configurer l'application OTP Yubico sur la clé.

Le fichier de règles udev nécessaire provient du paquet
@code{yubikey-personalization} et peut être installé de cette manière :

@lisp
(use-package-modules ... security-token ...)
...
(operating-system
 ...
 (services
  (cons*
   ...
   (udev-rules-service 'yubikey yubikey-personalization))))
@end lisp

Après avoir reconfiguré votre système (et avoir reconnecté votre Yubikey),
vous voudrez ensuite configurer l'application de défi/réponse OTP do votre
Yubikey sur le slot 2, qui est utilisé par KeePassXC. C'est facile à faire
via l'outil de configuration graphique Yubikey Manager, qui peut s'invoquer
de cette manière :

@example
guix shell yubikey-manager-qt -- ykman-gui
@end example

Tout d'abord, assurez-vous d'avoir activé @samp{OTP} dans l'onglet
@samp{Interfaces}, puis rendez-vous dans @samp{Applications -> OTP} et
cliquez sur le bouton @samp{Configure} sous la section @samp{Long Touch
(Slot 2)}. Choisissez @samp{Challenge-response}, saisissez ou générez une
clé secrète, puis cliquez sur le bouton @samp{Finish}. Si vous avez une
seconde Yubikey que vous voulez utiliser en réserve, vous devriez la
configurer de la même manière avec la @emph{même} clé secrète.

Votre Yubikey devrait maintenant être détectée par KeePassXC. Elle peut être
ajoutée à la base de données en se rendant dans le menu @samp{Base de
données -> Sécurité de la base de données...} de KeePassXC, puis en cliquant
sur le bouton @samp{Ajouter une autre protection...} puis @samp{Ajouter une
question-réponse}, en choisissant la clé de sécurité dans le menu déroulant
et en cliquant sur le bouton @samp{OK} pour terminer la configuration.

@node Tâche mcron pour le DNS dynamique
@section Tâche mcron pour le DNS dynamique

@cindex DNS dynamique, DDNS
Si votre @acronym{FAI, fournisseur d'accès à internet} ne fournit que des
adresse IP dynamiques, il peut être utile de mettre en place un service
@acronym{DNS, Domain Name System} dynamique (aussi appelé @acronym{DDNS,
Dynamic DNS}) pour associer un nom d'hôte statique à une adresse IP publique
mais dynamique (qui change souvent). Il y a plusieurs services existants qui
peuvent être utilisés pour cela ; dans la tâche mcron suivante, nous
utilisons @url{https://duckdns.org, DuckDNS}. Cela devrait aussi fonctionner
avec d'autres services DNS dynamiques qui fournissent une interface
similaire pour mettre à jour l'adresse IP, comme
@url{https://freedns.afraid.org/}, avec quelques petits ajustements.

La tâche mcron est fournie plus bas, où @var{DOMAINE} doit être remplacé par
votre propre préfixe de domaine, et le jeton fournit par DuckDNS associé à
@var{DOMAINE} est à ajouter au fichier
@file{/etc/duckdns/@var{DOMAINE}.token}.

@lisp
(define duckdns-job
  ;; Met à jour l'IP du domaine toutes les 5 minutes.
  #~(job '(next-minute (range 0 60 5))
	 #$(program-file
            "duckdns-update"
            (with-extensions (list guile-gnutls) ;requis par (web client)
              #~(begin
                  (use-modules (ice-9 textual-ports)
                               (web client))
                  (let ((token (string-trim-both
                                (call-with-input-file "/etc/duckdns/@var{DOMAINE}.token"
                                  get-string-all)))
                        (query-template (string-append "https://www.duckdns.org/"
                                                       "update?domains=@var{DOMAINE}"
                                                       "&token=~a&ip=")))
                    (http-get (format #f query-template token))))))
         "duckdns-update"
         #:user "nobody"))
@end lisp

La tâche a ensuite besoin d'être ajoutée à la liste des tâches mcron de
votre système, avec quelque chose comme cela :

@lisp
(operating-system
 (services
  (cons* (service mcron-service-type
           (mcron-configuration
             (jobs (list duckdns-job ...))))
         ...
         %base-services)))
@end lisp

@node Se connecter à un VPN Wireguard
@section Se connecter à un VPN Wireguard

Pour se connecter à un serveur VPN Wireguard, il faut que le module du noyau
soit chargé en mémoire et qu'un paquet fournissant des outils de réseau le
prenne en charge (par exemple, @code{wireguard-tools} ou
@code{network-manager}).

Voici un exemple de configuration pour Linux-Libre < 5.6, où le module est
hors de l'arborescence des sources et doit être chargé manuellement--les
révisions suivantes du noyau l'ont intégré et n'ont donc pas besoin d'une
telle configuration :

@lisp
(use-modules (gnu))
(use-service-modules desktop)
(use-package-modules vpn)

(operating-system
  ;; …
  (services (cons (simple-service 'wireguard-module
                                  kernel-module-loader-service-type
                                  '("wireguard"))
                  %desktop-services))
  (packages (cons wireguard-tools %base-packages))
  (kernel-loadable-modules (list wireguard-linux-compat)))
@end lisp

Après avoir reconfiguré et redémarré votre système, vous pouvez utiliser les
outils Wireguard ou NetworkManager pour vous connecter à un serveur VPN.

@subsection Utilisation des outils Wireguard

Pour tester votre configuration Wireguard, vous pouvez utiliser
@command{wg-quick}. Donnez-lui simplement un fichier de configuration :
@command{wg-quick up ./wg0.conf}, ou placez ce fichier dans
@file{/etc/wireguard} et lancez @command{wg-quick up wg0} à la place.

@quotation Remarque
Soyez averti que l'auteur a décrit cette commande comme un : « [...] script
bash écrit à la va-vite [...] ».
@end quotation

@subsection En utilisant NetworkManager

Grâce à la prise en charge de NetworkManager pour Wireguard, nous pouvons
nous connecter à notre VPN en utilisant la commande @command{nmcli}.
Jusqu'ici, ce guide suppose que vous utilisez le service Network Manager
fourni par @code{%desktop-services}.  Dans le cas contraire, vous devez
ajuster votre liste de services pour charger
@code{network-manager-service-type} et reconfigurer votre système Guix.

Pour importer votre configuration VPN, exécutez la commande d'import de
nmcli :

@example shell
# nmcli connection import type wireguard file wg0.conf
Connection 'wg0' (edbee261-aa5a-42db-b032-6c7757c60fde) successfully added
@end example

Cela va créer un fichier de configuration dans
@file{/etc/NetworkManager/wg0.nmconnection}. Ensuite connectez-vous au
serveur Wireguard :

@example shell
$ nmcli connection up wg0
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/6)
@end example

Par défaut, NetworkManager se connectera automatiquement au démarrage du
système. Pour changer ce comportement vous devez modifier votre
configuration :

@example shell
# nmcli connection modify wg0 connection.autoconnect no
@end example

Pour des informations plus spécifiques sur NetworkManager et wireguard
@uref{https://blogs.gnome.org/thaller/2019/03/15/wireguard-in-networkmanager/,voir
ce billet par thaller}.

@node Personnaliser un gestionnaire de fenêtres
@section Personnaliser un gestionnaire de fenêtres
@cindex wm

@menu
* StumpWM::                  StumpWM
* Verrouillage de session::  Verrouillage de session
@end menu

@node StumpWM
@subsection StumpWM
@cindex stumpwm

Vous pouvez installer StumpWM sur un système Guix en ajoutant @code{stumpwm}
et éventuellement @code{`(,stumpwm "lib")} dans les paquets du fichier de
système d'exploitation, p.@: ex.@: @file{/etc/config.scm}.

Voici un exemple de configuration :

@lisp
(use-modules (gnu))
(use-package-modules wm)

(operating-system
  ;; …
  (packages (append (list sbcl stumpwm `(,stumpwm "lib"))
                    %base-packages)))
@end lisp

@cindex polices stumpwm
Par défaut StumpWM utilise les polices X11, qui peuvent être petites ou
pixelisées sur votre système. Vous pouvez corriger cela en installant le
module Lisp pour StumpWM @code{sbcl-ttf-fonts}, en l'ajoutant aux paquets de
votre système :

@lisp
(use-modules (gnu))
(use-package-modules fonts wm)

(operating-system
  ;; …
  (packages (append (list sbcl stumpwm `(,stumpwm "lib"))
                    sbcl-ttf-fonts font-dejavu %base-packages)))
@end lisp

Ensuite vous devrez ajouter le code suivant à au fichier de configuration de
StumpWM @file{~/.stumpwm.d/init.lisp} :

@lisp
(require :ttf-fonts)
(setf xft:*font-dirs* '("/run/current-system/profile/share/fonts/"))
(setf clx-truetype:+font-cache-filename+ (concat (getenv "HOME")
                                                 "/.fonts/font-cache.sexp"))
(xft:cache-fonts)
(set-font (make-instance 'xft:font :family "DejaVu Sans Mono"
                                   :subfamily "Book" :size 11))
@end lisp

@node Verrouillage de session
@subsection Verrouillage de session
@cindex verrouillage de session

En fonction de votre environnement, le verrouillage de l'écran peut être
inclus, ou vous devrez le configurer vous-même. La fonctionnalité est
souvent intégrée aux environnements de bureau comme GNOME ou KDE. Si vous
utilisez un gestionnaire de fenêtre comme StumpWM ou EXWM, vous devrez sans
doute le configurer vous-même.

@menu
* Xorg::                     Xorg
@end menu

@node Xorg
@subsubsection Xorg

Si vous utilisez Xorg, vous pouvez utiliser l'utilitaire
@uref{https://www.mankier.com/1/xss-lock, xss-lock} pour verrouiller votre
session. xss-lock est lancé par DPMS qui est détecté et activé
automatiquement par Xorg 1.8 si ACPI est aussi activé à l'exécution dans le
noyau.

Pour utiliser xss-lock, vous pouvez simplement l'exécuter et le laisser
tourner en tache de fond avant de démarrer votre gestionnaire de fenêtre,
par exemple dans votre @file{~/.xsession} :

@example
xss-lock -- slock &
exec stumpwm
@end example

Dans cet exemple, xss-lock utilise @code{slock} pour effectivement
verrouiller l'écran quand il pense que c'est nécessaire, comme lorsque vous
mettez votre machine en veille.

Pour que slock puisse verrouiller l'écran de la session graphique, il doit
être en setuid-root pour qu'il puisse authentifier les utilisateurs, et il a
besoin d'un service PAM. On peut y arriver en ajoutant le service suivant
dans notre @file{config.scm} :

@lisp
(service screen-locker-service-type
         (screen-locker-configuration
          (name "slock")
          (program (file-append slock "/bin/slock"))))
@end lisp

Si vous verrouillez l'écran manuellement, p.@: ex.@: en appelant slock
directement si vous voulez verrouiller l'écran sans mettre l'ordinateur en
veille, il vaut mieux notifier xss-lock pour éviter la confusion. Vous
pouvez faire cela en exécutant @code{xset s activate} juste avant d'exécuter
slock.

@node Lancer Guix sur un serveur Linode
@section Lancer Guix sur un serveur Linode
@cindex linode, Linode

Pour lancer Guix sur un serveur hébergé par @uref{https://www.linode.com,
Linode}, commencez par un serveur Debian recommandé. Nous vous recommandons
d'utiliser la distribution par défaut pour amorcer Guix. Créez vos clés SSH.

@example
ssh-keygen
@end example

Assurez-vous d'ajouter votre clé SSH pour vous connecter facilement sur le
serveur distant. C'est facilité par l'interface graphique de Linode. Allez
sur votre profil et cliquez sur le bouton pour ajouter une clé SSH. Copiez
la sortie de :

@example
cat ~/.ssh/<username>_rsa.pub
@end example

Éteignez votre Linode.

Dans l'onglet de stockage du Linode, modifiez la taille du disque Debian
pour qu'il soit plus petit. Nous recommandons 30 Go d'espace libre. Ensuite,
cliquez sur « Ajouter un disque » et remplissez le formulaire de cette
manière :

@itemize @bullet
@item
Label: "Guix"

@item
Filesystem: ext4

@item
Donnez-lui la taille restante
@end itemize

Dans l'onglet de configuration, cliquez sur « modifier » sur le profil
Debian par défaut. Dans « Block Device Assignment » cliquez sur « Add a
Device ». Il devrait apparaître en tant que @file{/dev/sdc} et vous pouvez
sélectionner le disque « Guix ». Sauvegardez les changements.

Maintenant « Add a Configuration », avec ce qui suit :
@itemize @bullet
@item
Label: Guix

@item
Kernel: GRUB 2 (c'est à la toute fin ! Cette étape est @b{IMPORTANTE !})

@item
Périphériques blocs assignés :

@item
@file{/dev/sda} : Guix

@item
@file{/dev/sdb} : swap

@item
Périphérique racine : @file{/dev/sda}

@item
Désactivez tous les programmes d'aide pour les systèmes de fichiers et le
démarrage
@end itemize

Maintenant démarrez le serveur avec la configuration Debian. Une fois lancé,
connectez vous en ssh au serveur avec @code{ssh
root@@@var{<IP-de-votre-serveur-ici>}}. (Vous pouvez trouver l'adresse IP de
votre serveur dans la section résumé de Linode). Maintenant vous pouvez
lancer les étapes d'installation de @pxref{Installation binaire,,, guix.fr,
GNU Guix} :

@example
sudo apt-get install gpg
wget https://sv.gnu.org/people/viewgpg.php?user_id=15145 -qO - | gpg --import -
wget https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh
chmod +x guix-install.sh
./guix-install.sh
guix pull
@end example

Maintenant il est temps d'écrire une configuration pour le serveur. Voici ce
que vous devrez obligatoirement écrire, en plus de vos propres
configurations. Enregistrez le fichier avec le nom @file{guix-config.scm}.

@lisp
(use-modules (gnu)
             (guix modules))
(use-service-modules networking
                     ssh)
(use-package-modules admin
                     package-management
                     ssh
                     tls)

(operating-system
  (host-name "my-server")
  (timezone "America/New_York")
  (locale "en_US.UTF-8")
  ;; Ce code va générer un grub.cfg
  ;; sans installer le chargeur d'amorçage grub sur le disque.
  (bootloader (bootloader-configuration
               (bootloader
                (bootloader
                 (inherit grub-bootloader)
                 (installer #~(const #true))))))
  (file-systems (cons (file-system
                        (device "/dev/sda")
                        (mount-point "/")
                        (type "ext4"))
                      %base-file-systems))


  (swap-devices (list "/dev/sdb"))


  (initrd-modules (cons "virtio_scsi"    ; Requis pour trouver le disque
                        %base-initrd-modules))

  (users (cons (user-account
                (name "janedoe")
                (group "users")
                ;; Ajoute le compte au groupe « wheel »
                ;; pour en faire un sudoer.
                (supplementary-groups '("wheel"))
                (home-directory "/home/janedoe"))
               %base-user-accounts))

  (packages (cons* openssh-sans-x
                   %base-packages))

  (services (cons*
             (service dhcp-client-service-type)
             (service openssh-service-type
                      (openssh-configuration
                       (openssh openssh-sans-x)
                       (password-authentication? #false)
                       (authorized-keys
                        `(("janedoe" ,(local-file "janedoe_rsa.pub"))
                          ("root" ,(local-file "janedoe_rsa.pub"))))))
             %base-services)))
@end lisp

Remplacez les champs suivants dans la configuration ci-dessus :
@lisp
(host-name "my-server")       ; remplacez avec le nom de votre serveur
; si vous avez choisi un serveur Linode en dehors des U.S.,
; utilisez tzselect pour trouver le bon fuseau horaire
(timezone "America/New_York") ; remplacez le fuseau horaire si besoin
(name "janedoe")              ; remplacez avec votre nom d'utilisateur
("janedoe" ,(local-file "janedoe_rsa.pub")) ; remplacez par votre clé ssh
("root" ,(local-file "janedoe_rsa.pub")) ; remplacez par votre clé ssh
@end lisp

Cette dernière ligne vous permet de vous connecter au serveur en root et de
créer le mot de passe initial de root (voir la note à la fin de cette
recette sur la connexion en root). Après avoir fait cela, vous pouvez
supprimer cette ligne de votre configuration et reconfigurer pour empêcher
la connexion directe en root.

Copiez votre clé ssh publique (ex : @file{~/.ssh/id_rsa.pub}) dans
@file{@var{<votre-nom-d'utilisateur>}_rsa.pub} et ajoutez votre
@file{guix-config.scm} au même répertoire. Dans un nouveau terminal lancez
ces commandes.

@example
sftp root@@<adresse IP du serveur distant>
put /path/to/files/<username>_rsa.pub .
put /path/to/files/guix-config.scm .
@end example

Dans votre premier terminal, montez le disque guix :

@example
mkdir /mnt/guix
mount /dev/sdc /mnt/guix
@end example

À cause de la manière dont nous avons paramétré la section du chargeur
d'amorçage dans le fichier guix-config.scm, nous installons seulement notre
fichier de configuration grub. Donc on doit copier certains fichiers GRUB
déjà installés sur le système Debian :

@example
mkdir -p /mnt/guix/boot/grub
cp -r /boot/grub/* /mnt/guix/boot/grub/
@end example

Maintenant initialisez l'installation de Guix :

@example
guix system init guix-config.scm /mnt/guix
@end example

Ok, éteignez maintenant le serveur ! Depuis la console Linode, démarrez et
choisissez « Guix ».

Une fois démarré, vous devriez pouvoir vous connecter en SSH ! (La
configuration du serveur aura cependant changé). Vous pouvez rencontrer une
erreur de ce type :

@example
$ ssh root@@<server ip address>
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:0B+wp33w57AnKQuHCvQP0+ZdKaqYrI/kyU7CfVbS7R4.
Please contact your system administrator.
Add correct host key in /home/joshua/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /home/joshua/.ssh/known_hosts:3
ECDSA host key for 198.58.98.76 has changed and you have requested strict checking.
Host key verification failed.
@end example

Vous pouvez soit supprimer @file{~/.ssh/known_hosts}, soit supprimer la
ligne qui pose problème, qui commence par l'adresse IP de votre serveur.

Assurez-vous de configurer votre mot de passe et celui de root.

@example
ssh root@@<remote ip address>
passwd  ; pour le mot de passe root
passwd <username> ; pour le mot de passe utilisateur
@end example

Il se peut que vous ne puissiez pas lancer les commandes précédentes si vous
n'arrivez pas à vous connecter à distance via SSH, auquel cas vous devrez
peut-être configurer vos mot de passes utilisateurs et root en cliquant sur
« Launch Console » dans votre espace Linode. Choisissez « Glish » au lieu de
« Weblish ». Maintenant vous devriez pouvoir vous connecter en ssh à la
machine.

Hourra ! Maintenant vous pouvez étendre le serveur, supprimer le disque
Debian et redimensionner celui de Guix. Bravo !

Au fait, si vous sauvegardez le résultat dans une image disque maintenant,
vous pourrez plus facilement démarrer de nouvelles images de guix ! Vous
devrez peut-être réduire la taille de l'image Guix à 6144 Mo, pour la
sauvegarder en tant qu'image. Ensuite vous pouvez redimensionner la
partition à la taille maximum.

@node Lancer Guix sur un serveur Kimsufi
@section Lancer Guix sur un serveur Kimsufi
@cindex kimsufi, Kimsufi, OVH

Pour lancer Guix sur un serveur hébergé par @uref{https://www.kimsufi.com/,
Kimsufi}, cliquez sur l'onglet netboot puis choisissez rescue64-pro et
redémarrez.

OVH vous enverra un courriel avec les identifiants requis pour vous
connecter en ssh à un système Debian.

Maintenant vous pouvez exécuter les étapes de « installer guix de
@pxref{Installation binaire,,, guix.fr, GNU Guix} » :

@example
wget https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh
chmod +x guix-install.sh
./guix-install.sh
guix pull
@end example

Partitionnez les lecteurs et formatez-les, en commençant par arrêter la
grappe raid :

@example
mdadm --stop /dev/md127
mdadm --zero-superblock /dev/sda2 /dev/sdb2
@end example

Ensuite, effacez les disques et créez les partitions. Nous allons créer une
grappe RAID 1.

@example
wipefs -a /dev/sda
wipefs -a /dev/sdb

parted /dev/sda --align=opt -s -m -- mklabel gpt
parted /dev/sda --align=opt -s -m -- \
 mkpart bios_grub 1049kb 512MiB \
 set 1 bios_grub on
parted /dev/sda --align=opt -s -m -- \
 mkpart primary 512MiB -512MiB
 set 2 raid on
parted /dev/sda --align=opt -s -m -- mkpart primary linux-swap 512MiB 100%

parted /dev/sdb --align=opt -s -m -- mklabel gpt
parted /dev/sdb --align=opt -s -m -- \
     mkpart bios_grub 1049kb 512MiB \
     set 1 bios_grub on
parted /dev/sdb --align=opt -s -m -- \
     mkpart primary 512MiB -512MiB \
     set 2 raid on
parted /dev/sdb --align=opt -s -m -- mkpart primary linux-swap 512MiB 100%
@end example

Créez la grappe :

@example
mdadm --create /dev/md127 --level=1 --raid-disks=2 \
  --metadata=0.90 /dev/sda2 /dev/sdb2
@end example

Maintenant, créez les systèmes de fichiers sur les bonnes partitions, en
commençant par la partition de démarrage :

@example
mkfs.ext4  /dev/sda1
mkfs.ext4  /dev/sdb1
@end example

Puis la partition racine :

@example
mkfs.ext4 /dev/md127
@end example

Initialisez les partitions d'échange :

@example
mkswap /dev/sda3
swapon /dev/sda3
mkswap /dev/sdb3
swapon /dev/sdb3
@end example

Montez le disque guix :

@example
mkdir /mnt/guix
mount /dev/md127 /mnt/guix
@end example

Maintenant vous pouvez écrire une déclaration de système d'exploitation dans
un fichier @file{os.scm}. Voici un exemple :

@lisp
(use-modules (gnu) (guix))
(use-service-modules networking ssh vpn virtualization sysctl admin mcron)
(use-package-modules ssh tls tmux vpn virtualization)

(operating-system
  (host-name "kimsufi")

  (bootloader (bootloader-configuration
	       (bootloader grub-bootloader)
	       (targets (list "/dev/sda" "/dev/sdb"))
	       (terminal-outputs '(console))))

  ;; Ajouter un module noyau pour le RAID-1 (appelé « miroir »).
  (initrd-modules (cons* "raid1"  %base-initrd-modules))

  (mapped-devices
   (list (mapped-device
          (source (list "/dev/sda2" "/dev/sdb2"))
          (target "/dev/md127")
          (type raid-device-mapping))))

  (swap-devices
   (list (swap-space
          (target "/dev/sda3"))
         (swap-space
          (target "/dev/sdb3"))))

  (issue
   ;; Contenu par défaut de /etc/issue.
   "\
Ceci est le système GNU sur Kimsufi.  Bienvenue.\n")

  (file-systems (cons* (file-system
		         (mount-point "/")
		         (device "/dev/md127")
		         (type "ext4")
		         (dependencies mapped-devices))
		       %base-file-systems))

  (users (cons (user-account
	        (name "guix")
	        (comment "guix")
	        (group "users")
	        (supplementary-groups '("wheel"))
	        (home-directory "/home/guix"))
	       %base-user-accounts))

  (sudoers-file
   (plain-file "sudoers" "\
root ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
guix ALL=(ALL) NOPASSWD:ALL\n"))

  ;; Paquets installés sur tout le système.
  (packages (cons* tmux gnutls wireguard-tools %base-packages))
  (services
   (cons*
    (service static-networking-service-type
	     (list (static-networking
		    (addresses (list (network-address
				      (device "enp3s0")
				      (value "@var{adresse-ip-du-serveur}/24"))))
		    (routes (list (network-route
				   (destination "default")
				   (gateway "@var{passerelle-du-serveur}"))))
		    (name-servers '("213.186.33.99")))))

    (service unattended-upgrade-service-type)

    (service openssh-service-type
	     (openssh-configuration
	      (openssh openssh-sans-x)
	      (permit-root-login #f)
	      (authorized-keys
	       `(("guix" ,(plain-file "@var{nom-de-clé-ssh.pub}"
                                      "@var{contenu-de-clé-ssh}"))))))
    (modify-services %base-services
      (sysctl-service-type
       config =>
       (sysctl-configuration
	(settings (append '(("net.ipv6.conf.all.autoconf" . "0")
			    ("net.ipv6.conf.all.accept_ra" . "0"))
			  %default-sysctl-settings))))))))
@end lisp

N'oubliez pas de modifier les variables @var{adresse-ip-du-serveur},
@var{passerelle-du-serveur}, @var{nom-de-clé-ssh} et
@var{contenu-de-clé-ssh} avec vos propres valeurs.

La passerelle est la dernière IP utilisable dans votre bloc donc si vous
avez un serveur avec l'IP @samp{37.187.79.10}, sa passerelle sera
@samp{37.187.79.254}.

Transférez votre déclaration de système d'exploitation @file{os.scm} sur le
serveur via les commandes @command{scp} ou @command{sftp}.

Maintenant, tout ce qu'il reste à faire est d'installer Guix avec @code{guix
system init} et redémarrer.

Cependant nous devons d'abord paramétrer un chroot, car la partition racine
du système de secours est monté sur une partition aufs et si vous essayez
d'installer Guix cela ne marchera pas à l'étape d'installation de GRUB. Il
se plaindra du chemin canonique de « aufs ».

Installez les paquets qui seront utilisés dans le chroot :

@example
guix install bash-static parted util-linux-with-udev coreutils guix
@end example

Ensuite, exécutez ce qui suit pour créer les répertoires nécessaires au
chroot :

@example
cd /mnt && \
mkdir -p bin etc gnu/store root/.guix-profile/ root/.config/guix/current \
  var/guix proc sys dev
@end example

Copiez le resolv.conf hôte dans le chroot :

@example
cp /etc/resolv.conf etc/
@end example

Montez les périphériques de type bloc, le dépôt et sa base de données et la
configuration guix actuelle :

@example
mount --rbind /proc /mnt/proc
mount --rbind /sys /mnt/sys
mount --rbind /dev /mnt/dev
mount --rbind /var/guix/ var/guix/
mount --rbind /gnu/store gnu/store/
mount --rbind /root/.config/ root/.config/
mount --rbind /root/.guix-profile/bin/ bin
mount --rbind /root/.guix-profile root/.guix-profile/
@end example

Entrez dans le chroot sur /mnt et installez le système :

@example
chroot /mnt/ /bin/bash

guix system init /root/os.scm /guix
@end example

Enfin, à partir de l'interface utilisateur web, modifiez @samp{netboot} en
@samp{boot to disk} et redémarrez (également depuis l'interface web).

Attendez quelques minutes et essayez de vous connecter en ssh avec @code{ssh
guix@@@var{adresse-ip-du-serveur>} -i @var{chemin-vers-votre-clé-ssh}}

Votre système Guix devrait être opérationnel sur Kimsufi. Félicitations !

@node Mettre en place un montage dupliqué
@section Mettre en place un montage dupliqué

Pour dupliquer le montage d'un système de fichier (@i{bind mount}), on doit
d'abord ajouter quelques définitions avant la section
@code{operating-system} de la définition de système d'exploitation. Dans cet
exemple nous allons dupliquer le montage d'un dossier d'un disque dur vers
@file{/tmp}, pour éviter d'épuiser le SSD principal, sans dédier une
partition entière à @file{/tmp}.

Déjà, le disque source qui héberge de dossier dont nous voulons dupliquer le
montage doit être défini, pour que le montage dupliqué puisse en dépendre.

@lisp
(define source-drive ;; vous pouvez nommer « source-drive » comme vous le souhaitez.
   (file-system
    (device (uuid "indiquez l'UUID ici"))
    (mount-point "/chemin-vers-le-disque-dur")
    (type "ext4"))) ;Assurez-vous d'indiquer le bon type pour la partition
@end lisp

Le dossier source doit aussi être défini, pour que guix sache qu'il ne
s'agit pas d'un périphérique bloc, mais d'un dossier.
@lisp
;; vous pouvez nommer « source-directory » comme vous le souhaitez.
(define (%source-directory) "/chemin-vers-le-disque-dur/tmp")
@end lisp

Enfin, dans la définition @code{file-systems}, on doit ajouter le montage
lui-même.

@lisp
(file-systems (cons*

                ...<d'autres montages omis pour rester concis>...

                ;; Doit correspondre au nom que vous avez donné au disque source
                ;; dans la définition précédente.
                source-drive

                (file-system
                 ;; Assurez-vous que « source-directory » corresponde à
                 ;; la définition précédente.
                 (device (%source-directory))
                 (mount-point "/tmp")
                 ;; On monte un dossier, pas une partition, donc le montage est
                 ;; de type « none »
                 (type "none")
                 (flags '(bind-mount))
                 ;; Assurez-vous que « source-drive » corresponde au nom de
                 ;; la variable pour le disque.
                 (dependencies (list source-drive))
                 )

                 ...<d'autres montages omis pour rester concis>...

                ))
@end lisp

@node Récupérer des substituts via Tor
@section Récupérer des substituts via Tor

Le démon Guix peut utiliser un mandataire HTTP pour récupérer des
substituts. Nous le configurons ici pour les récupérer par Tor.

@quotation Attention
@emph{Tout} le trafic du démon de passera @emph{pas} par Tor ! Seuls
HTTP/HTTPS passer par le mandataire ; les connexions FTP, avec le protocol
Git, SSH, etc, passeront toujours par le réseau en clair. De nouveau, cette
configuration n'est pas parfaite et une partie de votre trafic ne sera pas
routé par Tor du tout. Utilisez-la à vos risques et périls.

Remarquez aussi que la procédure décrite ici ne s'applique qu'à la
substitution de paquets. Lorsque vous mettez à jour la distribution avec
@command{guix pull}, vous aurez encore besoin de @command{torsocks} si vous
voulez router la connexion vers les serveurs de dépôts git à travers Tor.
@end quotation

Le serveur de substitut de Guix est disponible sur un service Onion. Si vous
voulez l'utiliser pour récupérer des substituts par Tor, configurez votre
système de cette manière :

@lisp
(use-modules (gnu))
(use-service-module base networking)

(operating-system
  …
  (services
    (cons
      (service tor-service-type
              (tor-configuration
                (config-file (plain-file "tor-config"
                                         "HTTPTunnelPort 127.0.0.1:9250"))))
      (modify-services %base-services
        (guix-service-type
          config => (guix-configuration
                      (inherit config)
                      ;; sevice Onion de ci.guix.gnu.org
                      (substitute-urls "\
@value{SUBSTITUTE-TOR-URL}")
                      (http-proxy "http://localhost:9250")))))))
@end lisp

Cela fera tourner le processus tor et fournira un tunnel HTTP CONNECT qui
sera utilisé par @command{guix-daemon}. Le démon peut utiliser d'autres
protocoles que HTTP(S) pour récupérer des ressources distantes. Les requêtes
utilisant ces protocoles ne passeront pas par Tor puisqu'il s'agit d'un
tunnel HTTP uniquement. Remarquez que @code{substitutes-urls} doit utiliser
HTTPS et non HTTP, sinon ça ne fonctionne pas. C'est une limite du tunnel de
Tor ; vous voudrez peut-être utiliser @command{privoxy} à la place pour
éviter ces limites.

Si vous ne voulez pas toute le temps récupérer des substituts à travers Tor
mais l'utiliser seulement de temps en temps, alors ne modifiez pas l'objet
@code{guix-configuration}. Lorsque vous voulez récupérer un substitut par le
tunnel Tor, lancez :

@example
sudo herd set-http-proxy guix-daemon http://localhost:9250
guix build \
  --substitute-urls=@value{SUBSTITUTE-TOR-URL} @dots{}
@end example

@node Configurer NGINX avec Lua
@section Configurer NGINX avec Lua
@cindex nginx, lua, openresty, resty

Les fonctionnalités de NGINX peuvent être étendues avec des scripts Lua.

Guix fournit un service NGINX qui est capable de charger des modules et des
paquets Lua spécifiques, et de répondre aux requêtes en évaluant des scripts
Lua.

L'exemple suivant montre une définition de système avec une configuration
qui évalue le script Lua @file{index.lua} lors d'une requête HTTP à
@uref{http://localhost/hello} :

@example
local shell = require "resty.shell"

local stdin = ""
local timeout = 1000  -- ms
local max_size = 4096  -- byte

local ok, stdout, stderr, reason, status =
   shell.run([[/run/current-system/profile/bin/ls /tmp]], stdin, timeout, max_size)

ngx.say(stdout)
@end example

@lisp
(use-modules (gnu))
(use-service-modules #;… web)
(use-package-modules #;… lua)
(operating-system
  ;; …
  (services
   ;; …
   (service nginx-service-type
            (nginx-configuration
             (modules
              (list
               (file-append nginx-lua-module "/etc/nginx/modules/ngx_http_lua_module.so")))
             (lua-package-path (list lua-resty-core
                                     lua-resty-lrucache
                                     lua-resty-signal
                                     lua-tablepool
                                     lua-resty-shell))
             (lua-package-cpath (list lua-resty-signal))
             (server-blocks
              (list (nginx-server-configuration
                     (server-name '("localhost"))
                     (listen '("80"))
                     (root "/etc")
                     (locations (list
                                 (nginx-location-configuration
                                  (uri "/hello")
                                  (body (list #~(format #f "content_by_lua_file ~s;"
                                                        #$(local-file "index.lua"))))))))))))))
@end lisp

@node Serveur de musique avec l'audio bluetooth
@section Serveur de musique avec l'audio bluetooth
@cindex mpd
@cindex serveur de musique, sans affichage
@cindex bluetooth, configuration ALSA

MPD, le démon lecteur de musique, est une application serveur flexible pour
jouer de la musique. Les programmes clients sur différentes machines du
réseau — un téléphone portable, un ordinateur portable, un ordinateur de
bureau — peuvent s'y connecter pour contrôler la lecture de fichiers audio
de votre collection musicale locale. MPD décode les fichiers audio et les
joue sur une ou plusieurs sorties.

Par défaut MPD jouera sur le périphérique audio par défaut. Dans l'exemple
ci-dessous nous rendons les choses un peu plus intéressantes en configurant
un serveur de musique sans affichage. Il n'y aura pas d'interface graphique,
pas de démon Pulseaudio, ni de sortie audio locale. Au lieu de cela, nous
configurons MPD avec deux sorties : un haut-parleur bluetooth et un serveur
web pour servir des flux audio à n'importe quel lecture de musique.

Le Bluetooth est souvent frustrant à configurer. Vous devrez appairer vos
périphériques Bluetooth et vous assurer que le périphérique se connecte
automatiquement dès qu'il est branché. Le service système Bluetooth renvoyé
par la procédure @code{bluetooth-service} fournit l'infrastructure requise
pour cette configuration.

Reconfigurez votre système avec au moins les services et les paquets
suivants :

@lisp
(operating-system
  ;; …
  (packages (cons* bluez bluez-alsa
                   %base-packages))
  (services
   ;; …
   (dbus-service #:services (list bluez-alsa))
   (bluetooth-service #:auto-enable? #t)))
@end lisp

Démarrez le service @code{bluetooth} puis utilisez @command{bluetoothctl}
pour scanner les périphériques Bluetooth. Essayez d'identifier votre
haut-parleur Bluetooth et choisissez son ID de périphérique dans la liste de
périphériques qui est indubitablement polluée par une armée de gadgets
connectés chez votre voisin. Vous ne devrez le faire qu'une seule fois :

@example
$ bluetoothctl 
[NEW] Controller 00:11:22:33:95:7F BlueZ 5.40 [default]

[bluetooth]# power on
[bluetooth]# Changing power on succeeded

[bluetooth]# agent on
[bluetooth]# Agent registered

[bluetooth]# default-agent
[bluetooth]# Default agent request successful

[bluetooth]# scan on
[bluetooth]# Discovery started
[CHG] Controller 00:11:22:33:95:7F Discovering: yes
[NEW] Device AA:BB:CC:A4:AA:CD My Bluetooth Speaker
[NEW] Device 44:44:FF:2A:20:DC My Neighbor's TV
@dots{}

[bluetooth]# pair AA:BB:CC:A4:AA:CD
Attempting to pair with AA:BB:CC:A4:AA:CD
[CHG] Device AA:BB:CC:A4:AA:CD Connected: yes

[My Bluetooth Speaker]# [CHG] Device AA:BB:CC:A4:AA:CD UUIDs: 0000110b-0000-1000-8000-00xxxxxxxxxx
[CHG] Device AA:BB:CC:A4:AA:CD UUIDs: 0000110c-0000-1000-8000-00xxxxxxxxxx
[CHG] Device AA:BB:CC:A4:AA:CD UUIDs: 0000110e-0000-1000-8000-00xxxxxxxxxx
[CHG] Device AA:BB:CC:A4:AA:CD Paired: yes
Pairing successful

[CHG] Device AA:BB:CC:A4:AA:CD Connected: no

[bluetooth]# 
[bluetooth]# trust AA:BB:CC:A4:AA:CD
[bluetooth]# [CHG] Device AA:BB:CC:A4:AA:CD Trusted: yes
Changing AA:BB:CC:A4:AA:CD trust succeeded

[bluetooth]# 
[bluetooth]# connect AA:BB:CC:A4:AA:CD
Attempting to connect to AA:BB:CC:A4:AA:CD
[bluetooth]# [CHG] Device AA:BB:CC:A4:AA:CD RSSI: -63
[CHG] Device AA:BB:CC:A4:AA:CD Connected: yes
Connection successful

[My Bluetooth Speaker]# scan off
[CHG] Device AA:BB:CC:A4:AA:CD RSSI is nil
Discovery stopped
[CHG] Controller 00:11:22:33:95:7F Discovering: no
@end example

Félicitations, vous pouvez maintenant vous connecter automatiquement à votre
haut-parleur Bluetooth !

Il est maintenant temps de configurer ALSA pour utiliser le module Bluetooth
@emph{bluealsa}, pour que vous puissiez définir un périphérique pcm ALSA
correspondant à votre haut-parleur Bluetooth. Un serveur sans affichage
utilisant @emph{bluealsa} avec un périphérique fixe est probablement plus
facile à configurer que Pulseaudio et son comportement de changement de
flux. Nous configurons ALSA en créant un @code{alsa-configuration}
personnalisé pour le service @code{alsa-service-type}. La configuration
déclarera un type @code{pcm} @code{bluealsa} du module @code{bluealsa}
fournit par le paquet @code{bluez-alsa}, puis définira un périphérique
@code{pcm} de ce type pour le haut-parleur Bluetooth.

Tout ce qui reste à faire est de faire en sorte que MPD envoie les données
audio à ce périphérique ALSA. Nous ajoutons aussi une sortie MPD secondaire
qui rend les fichiers audio en lecture disponibles en streaming sur un
serveur web sur le port 8080. Lorsque la sortie est activée, un appareil sur
le réseau peut écouter le flux audio en se connectant avec n'importe quel
lecteur multimédia au serveur HTTP sur le port 8080, indépendamment du
statut du haut-parleur Bluetooth.

Voici les grandes lignes d'une déclaration @code{operating-system} qui
devrait accomplir les tâches sus-mentionnées :

@lisp
(use-modules (gnu))
(use-service-modules audio dbus sound #;… etc)
(use-package-modules audio linux #;… etc)
(operating-system
  ;; …
  (packages (cons* bluez bluez-alsa
                   %base-packages))
  (services
   ;; …
   (service mpd-service-type
            (mpd-configuration
             (user "your-username")
             (music-dir "/path/to/your/music")
             (address "192.168.178.20")
             (outputs (list (mpd-output
                             (type "alsa")
                             (name "MPD")
                             (extra-options
                              ;; Utilisez le même nom que dans la configuration
                              ;; ALSA plus bas.
                              '((device . "pcm.btspeaker"))))
                            (mpd-output
                             (type "httpd")
                             (name "streaming")
                             (enabled? #false)
                             (always-on? #true)
                             (tags? #true)
                             (mixer-type 'null)
                             (extra-options
                              '((encoder . "vorbis")
                                (port    . "8080")
                                (bind-to-address . "192.168.178.20")
                                (max-clients . "0") ;no limit
                                (quality . "5.0")
                                (format  . "44100:16:1"))))))))
   (dbus-service #:services (list bluez-alsa))
   (bluetooth-service #:auto-enable? #t)
   (service alsa-service-type
            (alsa-configuration
             (pulseaudio? #false) ;we don't need it
             (extra-options
              #~(string-append "\
# Déclare un type de périphérique audio Bluetooth \"bluealsa\" du module bluealsa
pcm_type.bluealsa @{
    lib \""
#$(file-append bluez-alsa "/lib/alsa-lib/libasound_module_pcm_bluealsa.so") "\"
@}

# Déclare un type de périphérique de contrôle \"bluealsa\" du même module
ctl_type.bluealsa @{
    lib \""
#$(file-append bluez-alsa "/lib/alsa-lib/libasound_module_ctl_bluealsa.so") "\"
@}

# Définie le périphérique Bluetooth audio.
pcm.btspeaker @{
    type bluealsa
    device \"AA:BB:CC:A4:AA:CD\" # unique device identifier
    profile \"a2dp\"
@}

# Définie un contrôleur associé.
ctl.btspeaker @{
    type bluealsa
@}
"))))))
@end lisp

Profitez de votre musique avec le client MPD de votre choix ou un lecteur
multimédia capable de streamer en HTTP !


@c *********************************************************************
@node Conteneurs
@chapter Conteneurs

Le noyau Linux fournit un certain nombre de dispositifs partagés qui sont
disponibles pour les processus du système. Ces dispositifs sont entre autres
un vue partagée du système de fichiers, des autres processus, des
périphériques réseau, des identités de groupe et d'utilisateur et quelques
autres. Depuis Linux 3.19 vous pouvez choisir de @emph{départager} certains
de ces dispositifs partagés pour des processus choisis, en leur fournissant
(ainsi qu'à leurs processus enfant) une vue différente du système.

Un processus avec un espace de nom de montage (@code{mount}) départagé par
exemple, a sa propre vue du système de fichier — il ne pourra voir que les
répertoires qui ont été explicitement liés à son espace de nom de
montage. Un processus avec son propre espace de nom de processus
(@code{proc}) considérera qu'il est le seul processus lancé sur le système,
avec le PID 1.

Guix utilise ces fonctionnalités du noyau pour fournir des environnement
complètement isolés e même des conteneurs complets pour le système Guix, des
machines virtuelles légères qui partagent le noyau de l'hôte. Cette
fonctionnalité est particulièrement pratique si vous utilise Guix sur une
distribution externe pour éviter les interférence avec les bibliothèques ou
les fichiers de configuration externes disponibles sur l'ensemble du
système.

@menu
* Conteneurs Guix::          Environnements parfaitement isolés
* Conteneurs pour le système Guix::  Un système dans votre système
@end menu

@node Conteneurs Guix
@section Conteneurs Guix

La manière la plus simple de démarrer est d'utiliser @command{guix shell}
avec l'option @option{--container}. @xref{Invoquer guix shell,,, guix.fr, le
manuel de référence de GNU Guix} pour la référence des options valides.

Le bout de code suivant démarre un shell minimal avec la plupart des espaces
de noms départagés du système. Le répertoire de travail actuel est visible
pour le processus, mais tout le reste du système de fichiers est
indisponible. Cette isolation extrême peut être très utile si vous voulez
écarter toute interférence des variables d'environnement, des bibliothèques
installées globalement ou des fichiers de configuration.

@example
guix shell --container
@end example

C'est un environnement vierge, aride et désolé. Vous trouverez que même GNU
coreutils n'y est pas disponible, donc pour explorer cet environnement
désert vous devrez utiliser les commandes internes au shell. Même le
répertoire @file{/gnu/store} habituellement énorme est réduit à peau de
chagrin.

@example sh
$ echo /gnu/store/*
/gnu/store/@dots{}-gcc-10.3.0-lib
/gnu/store/@dots{}-glibc-2.33
/gnu/store/@dots{}-bash-static-5.1.8
/gnu/store/@dots{}-ncurses-6.2.20210619
/gnu/store/@dots{}-bash-5.1.8
/gnu/store/@dots{}-profile
/gnu/store/@dots{}-readline-8.1.1
@end example

@cindex quitter un conteneur
Vous ne pouvez pas faire grand chose de plus dans un environnement comme
celui-ci à part en sortir. Vous pouvez utiliser @key{Ctrl-D} ou
@command{exit} pour quitter cet environnement shell limité.

@cindex exposer des répertoires, conteneur
@cindex partager des répertoires, conteneur
@cindex faire correspondre des emplacement, conteneur
Vous pouvez rendre d'autres répertoires disponibles à l'intérieur de
l'environnement du conteneur. Utilisez @option{--expose=RÉPERTOIRE} pour
créer un montage lié au répertoire donné en lecture-seule à l'intérieur du
conteneur, ou utilisez @option{--share=RÉPERTOIRE} pour rendre cet
emplacement inscriptible. Avec un argument de correspondance supplémentaire
après le nom du répertoire vous pouvez contrôler le nom du répertoire lié
dans le conteneur. Dans l'exemple suivant nous faisons correspondre
@file{/etc} du système hôte à @file{/l/hôte/etc} dans un conteneur dans
lequel GNU coreutils est installé.

@example sh
$ guix shell --container --share=/etc=/l/hôte/etc coreutils
$ ls /l/hôte/etc
@end example

De même, vous pouvez empêcher le répertoire actuel d'être ajouté au
conteneur avec l'option @option{--no-cwd}. Une autre bonne idée est de créer
un répertoire dédié qui servira de répertoire personnel dans le conteneur et
de démarrer le shell du conteneur dans ce répertoire.

@cindex cacher les bibliothèques système, conteneur
@cindex éviter les incompatibilités d'ABI, conteneur
Sur un système externe un environnement de conteneur peut être utilisé pour
compiler un logiciel qui ne peut pas être lié aux bibliothèques du système
ou avec la chaine d'outils du système. Un cas d'utilisation courant dans le
contexte de la recherche est l'installation de paquets à partir d'une
session R. En dehors de l'environnement du conteneur il est fort probable
que la chaine de compilation externe et les bibliothèques systèmes
incompatibles soient trouvés en premier, ce qui créer des binaires
incompatibles qui ne peuvent pas être utilisés dans R. Dans un conteneur ce
problème disparait car les bibliothèques du système et les executables ne
sont simplement pas disponibles à cause de l'espace de nom @code{mount}
départagé.

Prenons un manifeste complet pour fournir un environnement de développement
confortable pour R :

@lisp
(specifications->manifest
  (list "r-minimal"

        ;; paquets de base
        "bash-minimal"
        "glibc-locales"
        "nss-certs"

        ;; Outils en ligne de commande usuels, sans lesquels le conteneur est trop vide.
        "coreutils"
        "grep"
        "which"
        "wget"
        "sed"

        ;; outils markdown pour R
        "pandoc"

        ;; Chaine d'outils et bibliothèques courantes pour « install.packages »
        "gcc-toolchain@@10"
        "gfortran-toolchain"
        "gawk"
        "tar"
        "gzip"
        "unzip"
        "make"
        "cmake"
        "pkg-config"
        "cairo"
        "libxt"
        "openssl"
        "curl"
        "zlib"))
@end lisp

Utilisons cela pour lancer R dans un environnement de conteneur. Pour se
simplifier la vie, nous partageons l'espace de nom @code{net} pour utiliser
les interfaces réseau du système hôte. Maintenant nous pouvons construire
des paquets R à partir des sources de la manière traditionnelle sans avoir à
se soucier des incompatibilités d'ABI.

@example sh
$ guix shell --container --network --manifest=manifest.scm -- R

R version 4.2.1 (2022-06-23) -- "Funny-Looking Kid"
Copyright (C) 2022 The R Foundation for Statistical Computing
@dots{}
> e <- Sys.getenv("GUIX_ENVIRONMENT")
> Sys.setenv(GIT_SSL_CAINFO=paste0(e, "/etc/ssl/certs/ca-certificates.crt"))
> Sys.setenv(SSL_CERT_FILE=paste0(e, "/etc/ssl/certs/ca-certificates.crt"))
> Sys.setenv(SSL_CERT_DIR=paste0(e, "/etc/ssl/certs"))
> install.packages("Cairo", lib=paste0(getwd()))
@dots{}
* installing *source* package 'Cairo' ...
@dots{}
* DONE (Cairo)

The downloaded source packages are in
	'/tmp/RtmpCuwdwM/downloaded_packages'
> library("Cairo", lib=getwd())
> # success!
@end example

Utiliser des shells conteneurs est amusant, mais ils peuvent devenir un peu
embêtant quand vous voulez plus qu'un seul processus interactif. Certaines
tâches deviennent plus faciles lorsqu'elles se reposent sur les fondations
solides d'un système Guix et de son riche ensemble de services systèmes. La
section suivante vous montre comment lancer un système Guix complet à
l'intérieur d'un conteneur.


@node Conteneurs pour le système Guix
@section Conteneurs pour le système Guix

Le système Guix fournit un large éventail de services systèmes
interconnectés configurés déclarativement pour former les fondations d'un
système GNU fiable et sans état pour n'importe quelle tâche que vous lui
donnez. Même lorsque vous utilisez Guix sur une distribution externe, vous
pouvez bénéficier de la conception du système Guix en lançant une instance
du système dans un conteneur. Avec les mêmes fonctionnalités d'espaces de
noms départagés mentionnés dans la section précédente, l'instance du système
Guix qui en résulte est isolée du système hôte et ne partage que les
emplacements de fichiers que vous avez explicitement déclarés.

Un conteneur du système Guix est différent du processus shell créé par
@command{guix shell --container} de plusieurs façons importantes. Dans un
shell conteneur le processus de conteneurisation est le processus de shell
Bash alors qu'une conteneur du système Guix fait tourner le Shepherd en PID
1. Dans un conteneur système tous les services systèmes (@pxref{Services,,,
guix.fr, le manuel de référence de GNU Guix}) sont paramétrés de la même
manière que sur un système Guix dans une machine virtuelle ou directement
sur le matériel. Cela comprend les démons gérés par le GNU@tie{}Shepherd
(@pxref{Services Shepherd,,, guix.fr, le manuel de référence de GNU Guix})
ainsi que d'autres types d'extensions du système d'exploitation
(@pxref{Composition de services,,, guix.fr, le manuel de référence de GNU
Guix}).

La complexité perçue comme croissante d'un conteneur du système Guix est
facilement justifiée lorsque vous devez traiter avec des applications plus
complexes qui ont des prérequis plus grands ou plus rigides sur leur
contexte d'exécution — des fichiers de configuration, des comptes
utilisateurs dédiés, des répertoires pour les le cache ou les fichiers
journaux, etc. Sur le système Guix, la demande de ce genre de logiciels est
satisfaite en déployant des services systèmes.


@menu
* Un conteneur de base de données::  Un conteneur de base de données
* Utilisation du réseau dans le conteneur::  Utilisation du réseau dans le 
                                               conteneur
@end menu

@node Un conteneur de base de données
@subsection Un conteneur de base de données

Un bon exemple pourrait être un serveur de base de données PostgreSQL. La
majeure partie de la complexité de configuration d'un tel serveur de base de
données est encapsulée dans cette déclaration trompeusement courtes :

@lisp
(service postgresql-service-type
         (postgresql-configuration
          (postgresql postgresql-14)))
@end lisp

Une déclaration de système d'exploitation complète utilisable avec un
conteneur du système Guix ressemblerait à ceci :

@lisp
(use-modules (gnu))
(use-package-modules databases)
(use-service-modules databases)

(operating-system
  (host-name "container")
  (timezone "Europe/Berlin")
  (file-systems (cons (file-system
                        (device (file-system-label "does-not-matter"))
                        (mount-point "/")
                        (type "ext4"))
                      %base-file-systems))
  (bootloader (bootloader-configuration
               (bootloader grub-bootloader)
               (targets '("/dev/sdX"))))
  (services
   (cons* (service postgresql-service-type
                   (postgresql-configuration
                    (postgresql postgresql-14)
                    (config-file
                     (postgresql-config-file
                      (log-destination "stderr")
                      (hba-file
                       (plain-file "pg_hba.conf"
                                   "\
local	all	all			trust
host	all	all	10.0.0.1/32 	trust"))
                      (extra-config
                       '(("listen_addresses" "*")
                         ("log_directory"    "/var/log/postgresql")))))))
          (service postgresql-role-service-type
                   (postgresql-role-configuration
                    (roles
                     (list (postgresql-role
                            (name "test")
                            (create-database? #t))))))
          %base-services)))
@end lisp

Avec @code{postgresql-role-service-type} nous définissons un rôle « test »
et créons une base de données correspondante, pour que nous puissions le
tester immédiatement sans autre paramétrage manuel. Les paramètres de
@code{postgresql-config-file} permettent à un client de se connecter à
partir de l'adresse IP 10.0.0.1 sans authentification — une mauvaise idée en
production, mais pratique pour cet exemple.

Construisons un script qui exécutera une instance de ce système Guix dans un
conteneur. Écrivez la déclaration @code{operating-system} ci-dessus dans un
fichier @file{os.scm} puis utilisez @command{guix system container} pour
construire le lanceur (@pxref{Invoquer guix system,,, guix.fr, le manuel de
référence de GNU Guix}).

@example
$ guix system container os.scm
Les dérivations suivantes seront construites :
  /gnu/store/@dots{}-run-container.drv
  @dots{}
construction de /gnu/store/@dots{}-run-container.drv...
/gnu/store/@dots{}-run-container
@end example

Maintenant que nous avons le script de lancement nous pouvons le lancer pour
démarrer le nouveau système avec un service PostgreSQL. Remarquez qu'à cause
de limites non encore résolues, nous devons lancer le lanceur en root, par
exemple avec @command{sudo}.

@example
$ sudo /gnu/store/@dots{}-run-container
le conteneur système tourne avec le PID 5983
@dots{}
@end example

Mettez le processus en fond avec @key{Ctrl-z} suivie de @command{bg}. Prenez
note de l'ID du processus dans la sortie ; nous en aurons besoin pour nous
connecter au conteneur plus tard. Vous savez quoi ? Essayons de nous
attacher au conteneur immédiatement. Nous utiliserons @command{nsenter}, un
outil fournit par le paquet @code{util-linux} :

@example
$ guix shell util-linux
$ sudo nsenter -a -t 5983
root@@container /# pgrep -a postgres
49 /gnu/store/@dots{}-postgresql-14.4/bin/postgres -D /var/lib/postgresql/data --config-file=/gnu/store/@dots{}-postgresql.conf -p 5432
51 postgres: checkpointer
52 postgres: background writer
53 postgres: walwriter
54 postgres: autovacuum launcher
55 postgres: stats collector
56 postgres: logical replication launcher
root@@container /# exit
@end example

Le service PostgreSQL tourne dans le conteneur !


@node Utilisation du réseau dans le conteneur
@subsection Utilisation du réseau dans le conteneur
@cindex configuration réseau d'un conteneur

Que vaut un système Guix dans lequel tourne un service de bases de données
PostgreSQL dans un conteneur si nous ne pouvons lui parler qu'avec des
processus originaire de ce conteneur ? Il serait bien mieux de pouvoir
parler à la base de données via le réseau.

La manière la plus simple de faire cela est de créer une paire de
périphérique Ethernet virtuels (connus sous le nom de @code{veth}). Nous
déplaçons l'un des périphériques (@code{ceth-test}) dans l'espace de nom
@code{net} du conteneur et laissons l'autre côté (@code{veth-test}) de la
connexion sur le système hôte.

@example
pid=5983
ns="guix-test"
host="veth-test"
client="ceth-test"

# Attache le nouvel espace de nom réseau « guix-test » au PID du conteneur.
sudo ip netns attach $ns $pid

# Crée une paire de périphériques
sudo ip link add $host type veth peer name $client

# Déplace le périphérique client dans l'espace de nom réseau du conteneur
sudo ip link set $client netns $ns
@end example

Puis nous configurons le côté de l'hôte :

@example
sudo ip link set $host up
sudo ip addr add 10.0.0.1/24 dev $host
@end example

@dots{}puis nous configurons le côté client :

@example
sudo ip netns exec $ns  ip link set lo up
sudo ip netns exec $ns  ip link set $client up
sudo ip netns exec $ns  ip addr add 10.0.0.2/24 dev $client
@end example

Maintenant l'hôte peut atteindre le conteneur à l'adresse IP 10.0.0.2 et le
conteneur peut atteindre l'hôte à l'adresse IP 10.0.0.1. C'est tout ce dont
nous avons besoin pour communiquer avec le serveur de bases de données dans
le conteneur à partir du système hôte, à l'extérieur.

@example
$ psql -h 10.0.0.2 -U test
psql (14.4)
Type "help" for help.

test=> CREATE TABLE hello (who TEXT NOT NULL);
CREATE TABLE
test=> INSERT INTO hello (who) VALUES ('world');
INSERT 0 1
test=> SELECT * FROM hello;
  who
-------
 world
(1 row)
@end example

Maintenant que nous avons fini cette petite démonstration, nettoyons tout ça
:

@example
sudo kill $pid
sudo ip netns del $ns
sudo ip link del $host
@end example

@c *********************************************************************
@node Machines virtuelles
@chapter Machines virtuelles

Guix peut produire des images disque (@pxref{Invoquer guix system,,,
guix.fr, le manuel de référence de GNU Guix}) qui peuvent être utilisées
avec des solutions de machines virtuelles telles que virt-manager, GNOME
Boxes ou les plus simples QEMU, entre autres.

Ce chapitre a pour but de fournir des exemples pratiques relatifs à
l'utilisation et à la configuration de machines virtuelles sur un système
Guix.

@menu
* Pont réseau pour QEMU::    Pont réseau pour QEMU
* Réseau routé pour libvirt::  Réseau routé pour libvirt
* Réseau personnalisé basé sur le NAT pour libvirt::  Réseau personnalisé 
                                                        basé sur le NAT pour 
                                                        libvirt
* Références : Références sur les VM.  De la lecture à propose des machines 
                                         virtuelles.
@end menu

@node Pont réseau pour QEMU
@section Pont réseau pour QEMU
@cindex Interface de pont réseau
@cindex réseau, pont
@cindex qemu, pont réseau

Par défaut, QEMU utilise un moteur réseau hôte en « mode utilisateur », qui
est pratique comme il n'a pas besoin de configuration. Malheureusement, il
est aussi très limité. Dans ce mode, la @abbr{VM, machine virtuelle} invitée
peut accéder au réseau de la même manière que l'hôte, mais elle ne peut pas
être atteinte depuis l'hôte. De plus, comme le mode de réseau utilisateur de
QEMU se base sur ICMP, les outils de réseau basés sur ICMP comme
@command{ping} ne fonctionnent @emph{pas} dans ce mode. Ainsi, il est
souvent préférable de configurer un pont réseau, qui permet à l'invité de
participer pleinement au réseau. Cela est nécessaire, par exemple, lorsque
l'invité est utilisé comme serveur.

@subsection Créer une interface réseau pont

Il y a plusieurs manières de créer un pont réseau. La commande suivante
indique comment utiliser NetworkManager et son outil en ligne de commande
@command{nmcli}, qui devrait déjà être disponible si votre déclaration de
système d'exploitation se base sur l'un des modèles de systèmes de bureau :

@example sh
# nmcli con add type bridge con-name br0 ifname br0
@end example

Pour que ce pont fasse partie de votre réseau, vous devez associer votre
pont réseau à l’interface Ethernet utilisée pour vous connecter au
réseau. En supposant que cette interface est nommée @samp{enp2s0}, la
commande suivante peut être utilisée pour cela :

@example sh
# nmcli con add type bridge-slave ifname enp2s0 master br0
@end example

@quotation Important
Seules les interfaces Ethernet peut être ajoutées à un pont. Pour les
interfaces sans fil, envisagez l'approche par réseau routé détaillée dans
@xref{Réseau routé pour libvirt}.
@end quotation

Par défaut, le pont réseau permet à vos invités d’obtenir leur adresse IP
via DHCP s’il est disponible sur votre réseau local. Par souci de
simplicité, c’est ce que nous utiliserons ici. Pour retrouver facilement les
invités, vous pouvez les configurer pour que leurs noms d’hôte soient
annoncés via mDNS.

@subsection Configurer le script auxiliaire du pont QEMU

QEMU possède un programme auxiliaire qui permet aux utilisateurs non
privilégiés d’utiliser facilement une interface de pont réseau
@pxref{Network options,,, QEMU, QEMU Documentation}. Pour son bon
fonctionnement, le binaire doit être en setuid-root. Cela se fait en
l’ajoutant au champ @code{privileged-programs} de votre définition (hôte)
@code{operating-system}, comme indiqué ci-dessous :

@example lisp
(privileged-programs
 (cons (privileged-program
         (program (file-append qemu "/libexec/qemu-bridge-helper"))
         (setuid? #t))
       %default-privileged-programs))
@end example

Vous devez configurer le fichier @file{/etc/qemu/bridge.conf} pour qu’il
autorise l’interface de pont réseau, car il la refuse par défaut. Pour ce
faire, ajoutez l’élément suivant à votre liste de services :

@example lisp
(extra-special-file "/etc/qemu/host.conf"
                    (plain-file "host.conf" "allow br0\n"))
@end example

@subsection Invoquer QEMU avec les bonnes options de la ligne de commande

Lorsque vous invoquez QEMU, après avoir sélectionné une adresse MAC unique
pour l’invité, vous devez fournir les options suivantes pour que le pont
réseau soit utilisé.

@quotation Important
Par défaut, une seule adresse MAC est utilisée pour tous les invités, sauf
si plusieurs adresses MAC ont été fournies. Si vous ne fournissez pas des
adresses MAC différentes à chaque machine virtuelle qui utilise le pont,
vous risquez d’avoir des problèmes de réseau.
@end quotation

@example sh
$ qemu-system-x86_64 [...] \
    -device virtio-net-pci,netdev=user0,mac=XX:XX:XX:XX:XX:XX \
    -netdev bridge,id=user0,br=br0 \
    [...]
@end example

Pour générer des adresses MAC qui commencent par le préfixe enregistrée par
QEMU, vous pouvez utiliser l'extrait de code suivant :

@example sh
mac_address="52:54:00:$(dd if=/dev/urandom bs=512 count=1 2>/dev/null \
                           | md5sum \
                           | sed -E 's/^(..)(..)(..).*$/\1:\2:\3/')"
echo $mac_address
@end example

@subsection Problèmes de réseau causés par Docker

Si vous utilisez Docker sur votre machine, vous pouvez rencontrer des
problèmes de connectivité lorsque vous tentez d’utiliser un pont réseau. Ces
problèmes sont causés par le fait que Docker s’appuie également sur des
ponts réseau et configure ses propres règles de routage. Pour résoudre ce
problème, vous pouvez ajouter le bout de code @code{iptables} suivant à
votre déclaration @code{operating-system} :

@example lisp
(service iptables-service-type
             (iptables-configuration
              (ipv4-rules (plain-file "iptables.rules" "\
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A FORWARD -i br0 -o br0 -j ACCEPT
COMMIT
"))
@end example

@node Réseau routé pour libvirt
@section Réseau routé pour libvirt
@cindex Interface de pont réseau virtuel
@cindex réseau, pont virtuel
@cindex libvirt, commutateur réseau virtuel

Si la machine qui héberge vos machines virtuelles est connectée à un réseau
sans fil, vous ne pourrez pas utiliser un vrai pont réseau comme expliqué
dans la section précédente (@pxref{Pont réseau pour QEMU}). Dans ce cas,
la deuxième meilleure option est d'utiliser un @emph{commutateur réseau
virtuel} avec un routage statique et de configurer une machine virtuelle
dans libvirt pour l'utiliser (via l'interface graphique
@command{virt-manager} par exemple). Cela est semblable au mode de
fonctionnement par défaut de QEMU/libvirt, sauf qu'au lieu d'utiliser le
@abbr{NAT, Network Address Translation (traduction d'adresse réseau)}, il se
repose sur des routes statiques pour relier l'adresse IP de la @abbr{VM,
machine virtuelle} au @abbr{LAN, réseau local}. Cela fournit une
connectivité bidirectionnelle depuis et vers la machine virtuelle, ce qui
est requis pour exposer des services hébergés sur la machine virtuelle.

@subsection Créer un commutateur réseau virtuel

Un commutateur réseau virtuel consiste en un périphérique réseau virtuel
appelé un « pont virtuel », un serveur DHCP (dnsmasq) et de règles de
pare-feu (iptables). Consultez
@url{https://wiki.libvirt.org/VirtualNetworking.html, l'article Wiki de
libvirt sur la virtualisation du réseau} pour plus de détails sur les modes
de fonctionnement, la gestion et l'implémentation des commutateurs réseau
virtuels.

La commande @command{virsh}, fournie par le paquet @code{libvirt}, simplifie
grandement la création d'un commutateur virtuel. Vous devez d'abord choisir
un sous-réseau pour votre commutateur virtuel. Si votre réseau local est
dans le réseau @samp{192.168.1.0/24}, vous pourriez choisir par exemple
@samp{192.168.2.0/24}. Définissez un fichier XML, par exemple
@file{/tmp/virbr0.xml}, avec le contenu suivant :

@example
<network>
  <name>virbr0</name>
  <bridge name="virbr0" />
  <forward mode="route"/>
  <ip address="192.168.2.0" netmask="255.255.255.0">
    <dhcp>
      <range start="192.168.2.1" end="192.168.2.254"/>
    </dhcp>
  </ip>
</network>
@end example

Ensuite, créez et configurez l’interface en utilisant la commande
@command{virsh}, en tant qu'utilisateur root :

@example
virsh net-define /tmp/virbr0.xml
virsh net-autostart virbr0
virsh net-start virbr0
@end example

L’interface @samp{virbr0} devrait à présent être visible, par exemple via la
commande @samp{ip address}. Elle sera automatiquement lancée chaque fois que
la machine virtuelle libvirt est démarrée.

@subsection Configurer les routes statiques pour votre pont virtuel

Si vous avez configuré votre machine virtuelle pour utiliser votre interface
de pont virtuel nouvellement créée @samp{virbr0}, elle devrait déjà recevoir
une adresse IP via DHCP, telle que @samp{192.168.2.15}, et être accessible
depuis le serveur qui l'héberge, par exemple via @samp{ping
192.168.2.15}. Il reste une dernière configuration à effectuer pour que la
VM puisse atteindre le réseau externe : ajouter des routages statiques au
routeur du réseau.

Dans cet exemple, le réseau LAN est @samp{192.168.1.0/24} et la page de
configuration du routeur peut être accessible, par exemple, via la page
@url{http://192.168.1.1}. Sur un routeur exécutant le micrologiciel
@url{https://librecmc.org/, libreCMC}, vous navigueriez vers la page
@clicksequence{Network @click{} Static Routes}
(@url{https://192.168.1.1/cgi-bin/luci/admin/network/routes}), et vous
ajouteriez une nouvelle entrée aux @samp{Static IPv4 Routes} avec les
informations suivantes :

@table @samp
@item Interface
lan
@item Target
192.168.2.0
@item IPv4-Netmask
255.255.255.0
@item IPv4-Gateway
@var{ip-du-serveur}
@item Route type
unicast
@end table

où @var{ip-du-serveur} est l'adresse IP de la machine hébergeant les VM, qui
devrait être statique.

Après avoir sauvegardé/appliqué cette nouvelle route statique, la
connectivité externe devrait fonctionner depuis votre VM ; vous pouvez, par
exemple, exécuter la commande @samp{ping gnu.org} pour vérifier que cela
fonctionne correctement.

@node Réseau personnalisé basé sur le NAT pour libvirt
@section Réseau personnalisé basé sur le NAT pour libvirt

Comme indiqué dans la section précédente (@pxref{Réseau routé pour libvirt}), libvirt permet, avec des fichiers XML, de définir des réseaux
virtuels gérés par la commande @command{virsh}. Les détails de la création
et de la suppression des commutateurs réseau virtuels sont gérés par
libvirt, si bien que vous n'avez pas besoin de vous en occuper.

Cependant, la gestion des commutateurs réseau virtuels par libvirt peut
parfois entrer en conflit avec des configurations réseaux plus complexes. En
particulier, les règles iptables insérées par libvirt pour le fonctionnement
des commutateurs en mode NAT peuvent entrer en conflit avec des règles
iptables/nftables existantes, ce qui peut casser la sécurité du filtrage de
paquets.

Dans ce cas, la seule solution est de configurer manuellement le commutateur
réseau virtuel. Cette section fournit des instructions sur la manière de
faire avec les services du système Guix.

@subsection Créer le pont réseau virtuel

Le type @code{static-networking-service-type} peut être utilisé pour créer
un pont réseau virtuel et lui assigner une adresse IP :

@example lisp
(service static-networking-service-type
         (list (static-networking
                ;; La fourniture par défaut est 'networking ; si vous utilisez
                ;; un autre service qui fournit ce nom, comme
                ;; « network-manager-service-type », vous devrez changer
                ;; la valeur par défaut.
                (provision '(static-networking))
                (links
                 (list (network-link
                        (name "virbr0")
                        (type 'bridge)
                        (arguments '()))))
                (addresses
                 (list (network-address
                        (device "virbr0")
                        (value "192.168.10.1/24")))))))
@end example

@subsection Exécuter dnsmasq pour le pont réseau virtuel

Le type @code{dnsmasq-service-type} peut être utilisé pour fournir le DNS et
le DHCP aux invités connectés à ce commutateur réseau virtuel :

@example lisp
(service dnsmasq-service-type
         (dnsmasq-configuration
          ;; Vous pouvez avoir plusieurs instances de « dnsmasq-service-type »
          ;; tant qu'elles ont chacun une fourniture shepherd différente.
          (shepherd-provision '(dnsmasq-virbr0))
          (extra-options (list
                          ;; Ne se lier qu'au pont virtuel. Cela
                          ;; évite les conflits avec les autres instance
                          ;; de dnsmasq.
                          "--except-interface=lo"
                          "--interface=virbr0"
                          "--bind-dynamic"
                          ;; adresses IPv4 à fournir aux VM. Cela devrait
                          ;; correspondre au sous-réseau choisi.
                          "--dhcp-range=192.168.10.2,192.168.10.254"))))
@end example

@subsection Configurer le NAT pour le commutateur réseau virtuel

Si vous avez l'intention d'utiliser le commutateur réseau virtuel en mode
NAT, vous devrez utiliser des règles nftables (ou iptables) pour mettre en
place le masque d'IP. L'exemple suivant montre comment utiliser
@code{nftables-service-type} pour cela :

@example lisp
(service nftables-service-type
         (nftables-configuration
          (ruleset
           (plain-file "nftables.conf"
                       "\
table inet filter @{

  chain input @{
    type filter hook input priority filter; policy drop;
    # Ajoutez vos règles de filtrage de paquets existantes ici...
    iifname virbr0 udp dport 67 counter accept comment \"allow dhcp on virbr0\"
    iifname virbr0 meta l4proto @{tcp, udp@} th dport 53 accept \\
        comment \"allow dns on virbr0\"
  @}

  chain forward @{
    type filter hook forward priority filter; policy drop;
    # Ajoutez vos règles de transfert de paquets existantes ici...
    iifname virbr0 accept comment \"allow outbound traffic from virbr0\"
    oifname virbr0 ct state @{established, related @} accept \\
        comment \"allow established traffic to virbr0\"
  @}

@}

table inet nat @{
  chain postrouting @{
    type nat hook postrouting priority srcnat; policy accept;
    # Ajoutez vos règles nat existantes ici...
    iifname virbr0 ip daddr @{ 224.0.0.0/24, 255.255.255.255/32 @} return \\
        comment \"don't masquerade to reserved address blocks\"
    iifname virbr0 oifname != virbr0 masquerade \\
        comment \"masquerade all outgoing traffic from VMs\"
  @}
@}
"))))
@end example

Assurez-vous que vous avez activé le transfert d'IPv4 (vous pouvez utiliser
@code{sysctl-service-type} pour cela).

@node Références sur les VM
@section Références

@itemize
@item
@uref{https://jamielinux.com/docs/libvirt-networking-handbook/index.html, Le
manuel (non officiel) réseau de libvirt}@* Remarquez que cette ressource est
plutôt datée au moment d'écrire ces lignes (en mars 2025, il a été mis à
jour pour la dernière fois en 2015). Cependant, les auteurs de ce chapitre
pensent qu'il s'agit toujours d'une précieuse source d'information.
@end itemize

@c *********************************************************************
@node Gestion avancée des paquets
@chapter Gestion avancée des paquets

Guix est un gestionnaire de paquets fonctionnel qui propose de nombreuses
fonctionnalités en plus de ce que les gestionnaires de paquets traditionnels
peuvent faire. Pour quelqu'un qui n'est pas initié, ces fonctionnalités
peuvent ne pas paraître utiles au premier coup d'œil. Le but de ce chapitre
est de vous montrer certains concepts avancés en gestion de paquets.

@pxref{Gestion des paquets,,, guix.fr, le manuel de référence de GNU Guix}
pour une référence complète.

@menu
* Les profils Guix en pratique::  Stratégies pour gérer plusieurs profils et 
                                    manifestes.
@end menu

@node Les profils Guix en pratique
@section Les profils Guix en pratique

Guix fournit une fonctionnalité utile que peut être plutôt étrange pour les
débutants et débutantes : les @dfn{profils}. C'est une manière de regrouper
l'installation de paquets ensemble et chaque utilisateur ou utilisatrice du
même système peuvent avoir autant de profils que souhaité.

Que vous programmiez ou non, vous trouverez sans doute plus de flexibilité
et de possibilité avec plusieurs profils. Bien qu'ils changent un peu du
paradigme des @emph{gestionnaires de paquets traditionnels}, ils sont
pratiques à utiliser une fois que vous avec saisi comment les configurer.

@quotation Remarque
Cette section est un guide aux opinions bien arrêtées sur l'utilisation de
plusieurs profils. Il a été écrit avant l'introduction de @command{guix
shell} et son cache de profil rapide (@pxref{Invoquer guix shell,,, guix.fr,
le manuel de référence de GNU Guix}).

Dans de nombreux cas, vous trouverez qu'utiliser @command{guix shell} pour
configurer l'environnement dont vous avez besoin, quand vous en avez besoin,
représente moins de travail que de maintenir un profil dédié. À vous de
choisir !
@end quotation

Si vous connaissez @samp{virtualenv} de Python, vous pouvez conceptualiser
un profil comme une sorte de @samp{virtualenv} universel qui peut contenir
n'importe quel sorte de logiciel, pas seulement du code Python. En plus, les
profils sont auto-suffisants : ils capturent toutes les dépendances à
l'exécution qui garantissent que tous les programmes d'un profil
fonctionneront toujours à tout instant.

Avoir plusieurs profils présente de nombreux intérêts :

@itemize
@item
Une séparation sémantique claire des divers paquets dont vous avez besoin
pour différents contextes.

@item
On peut rendre plusieurs profils disponibles dans l'environnement soit à la
connexion, soit dans un shell dédié.

@item
Les profils peuvent être chargés à la demande. Par exemple, vous pouvez
utiliser plusieurs shells, chacun dans un profil différent.

@item
L'isolation : les programmes d'un profil n'utiliseront pas ceux d'un autre,
et vous pouvez même installe plusieurs versions d'un même programme dans
deux profils différents, sans conflit.

@item
Déduplication : les profils partagent les dépendances qui sont exactement
les mêmes. Avoir plusieurs profils ne gâche donc pas d'espace.

@item
Reproductible : lorsque vous utilisez des manifestes déclaratifs, un profil
peut être entièrement spécifié par le commit Guix qui a été utilisé pour le
créer. Cela signifie que vous pouvez
@uref{https://guix.gnu.org/blog/2018/multi-dimensional-transactions-and-rollbacks-oh-my/,
recréer n'importe où et à n'importe quel moment} exactement le même profil,
avec juste l'information du numéro de commit. Voir la section sur les
@ref{Profils reproductibles}.

@item
Des mises à jours et une maintenance plus faciles : avoir plusieurs profils
facilite la gestion des listes de paquets à la main.
@end itemize

Concrètement voici des profils courants :

@itemize
@item
Les dépendances d'un projet sur lequel vous travaillez.

@item
Des bibliothèques de votre langage de programmation favori.

@item
Des programmes spécifiques pour les ordinateurs portables (comme
@samp{powertop}) dont vous n'avez pas besoin sur un ordinateur de bureau.

@item
@TeX{}live (il peut être bien pratique si vous avez besoin d'installer un
seul paquet pour un document que vous avez reçu par courriel).

@item
Jeux.
@end itemize

Voyons cela de plus près !

@menu
* Utilisation de base avec des manifestes::  Utilisation de base avec des 
                                               manifestes
* Paquets requis::           Paquets requis
* Profil par défaut::        Profil par défaut
* Les avantages des manifestes::  Les avantages des manifestes
* Profils reproductibles::   Profils reproductibles
@end menu

@node Utilisation de base avec des manifestes
@subsection Utilisation de base avec des manifestes

Un profil Guix peut être paramétré par un @dfn{manifeste}. Un manifeste est
un bout de code Scheme qui spécifie l'ensemble des paquets que vous voulez
avoir dans votre profil ; il ressemble à ceci :

@lisp
(specifications->manifest
  '("package-1"
    ;; Version 1.3 de package-2.
    "package-2@@1.3"
    ;; La sortie « lib » de package-3.
    "package-3:lib"
    ; ...
    "package-N"))
@end lisp

@xref{Écrire un manifeste,,, guix.fr, le manuel de référence de GNU Guix},
pour plus d'informations sur la syntaxe.

On peut créer une spécification de manifeste par profil et les installer de
cette manière :

@example
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
mkdir -p "$GUIX_EXTRA_PROFILES"/my-project # s'il n'existe pas encore
guix package --manifest=/path/to/guix-my-project-manifest.scm \
 --profile="$GUIX_EXTRA_PROFILES"/my-project/my-project
@end example

On spécifie ici une variable arbitraire @samp{GUIX_EXTRA_PROFILES} pour
pointer vers le répertoire où seront stockés nos profils dans le reste de
cet article.

C'est un peu plus propre de placer tous vos profils dans un répertoire
unique, où chaque profil a son propre sous-répertoire. De cette manière,
chaque sous-répertoire contiendra tous les liens symboliques pour exactement
un profil. En plus, il devient facile d'énumérer les profils depuis
n'importe quel langage de programmation (p.@: ex.@: un script shell) en
énumérant simplement les sous-répertoires de @samp{$GUIX_EXTRA_PROFILES}.

Remarquez qu'il est aussi possible d'utiliser la sortie de

@example
guix package --list-profiles
@end example

même si vous devrez sans doute enlever @file{~/.config/guix/current}.

Pour activer tous les profils à la connexion, ajoutez cela à votre
@file{~/.bash_profile} (ou similaire) :

@example
for i in $GUIX_EXTRA_PROFILES/*; do
  profile=$i/$(basename "$i")
  if [ -f "$profile"/etc/profile ]; then
    GUIX_PROFILE="$profile"
    . "$GUIX_PROFILE"/etc/profile
  fi
  unset profile
done
@end example

Remarque pour les utilisateurs du système Guix : ce qui précède ressemble à
la manière dont votre profil par défaut @file{~/.guix-profile} est activé
dans @file{/etc/profile}, ce dernier étant chargé par défaut par
@file{~/.bashrc}.

Vous pouvez évidemment choisir de n'en activer qu'une partie :

@example
for i in "$GUIX_EXTRA_PROFILES"/my-project-1 "$GUIX_EXTRA_PROFILES"/my-project-2; do
  profile=$i/$(basename "$i")
  if [ -f "$profile"/etc/profile ]; then
    GUIX_PROFILE="$profile"
    . "$GUIX_PROFILE"/etc/profile
  fi
  unset profile
done
@end example

Lorsqu'un profil est désactivé, il est facile de l'activer pour un shell
individuel sans « polluer » le reste de la session :

@example
GUIX_PROFILE="path/to/my-project" ; . "$GUIX_PROFILE"/etc/profile
@end example

Le secret pour activer un profil est de @emph{sourcer} son fichier
@samp{etc/profile}. Ce fichier contient du code shell qui exporte les bonnes
variables d'environnement nécessaires à activer les logiciels présents dans
le profil. Il est créé automatiquement par Guix et doit être sourcé. Il
contient les mêmes variables que ce que vous obtiendrez en lançant :

@example
guix package --search-paths=prefix --profile=$my_profile"
@end example

Encore une fois, voir @ref{Invoquer guix package,,, guix.fr, le manuel de
référence de GNU Guix} pour les options de la ligne de commande.

Pour mettre à jour un profil, installez de nouveau le manifeste :

@example
guix package -m /path/to/guix-my-project-manifest.scm \
 -p "$GUIX_EXTRA_PROFILES"/my-project/my-project
@end example

Pour mettre à jour tous les profils, vous pouvez simplement les
énumérer. Par exemple, en supposant que vous spécifications sont dans
@file{~/.guix-manifests/guix-$profile-manifest.scm}, où @samp{$profile} est
le nom du profil (p.@: ex@: « projet1 »), vous pouvez utiliser ce qui suit
dans le shell :

@example
for profile in "$GUIX_EXTRA_PROFILES"/*; do
  guix package --profile="$profile" \
   --manifest="$HOME/.guix-manifests/guix-$profile-manifest.scm"
done
@end example

Chaque profil a ses propres générations :

@example
guix package -p "$GUIX_EXTRA_PROFILES"/my-project/my-project --list-generations
@end example

Vous pouvez revenir à n'importe quelle génération d'un profil donné :

@example
guix package -p "$GUIX_EXTRA_PROFILES"/my-project/my-project --switch-generations=17
@end example

Enfin, si vous voulez passer à un profil sans hériter l'environnement
actuel, vous pouvez l'activer dans un shell vide :

@example
env -i $(which bash) --login --noprofile --norc
. my-project/etc/profile
@end example

@node Paquets requis
@subsection Paquets requis

Activer un profil consiste en substance à exporter un ensemble de variables
d'environnement. C'est le rôle de @samp{etc/profile} dans le profil.

@emph{Remarque : seules les variables d'environnement des paquets qui les
utilisent seront modifiées.}

Par exemple, @samp{MANPATH} ne sera pas modifié s'il n'y a pas d'application
qui utilise les pages de manuel dans le profil. Donc si vous voulez pouvoir
accéder aux pages de manuel facilement une fois le profil chargé, vous avez
deux possibilités :

@itemize
@item
Exporter la variable manuellement, par exemple :
@example
export MANPATH=/path/to/profile$@{MANPATH:+:@}$MANPATH
@end example

@item
Inclure @samp{man-db} dans le manifeste du profil.
@end itemize

Il en va de même pour @samp{INFOPATH} (vous pouvez installer
@samp{info-reader}), @samp{PKG_CONFIG_PATH} (installer @samp{pkg-config}),
etc.

@node Profil par défaut
@subsection Profil par défaut

Que faire du profil par défaut que Guix garder dans @file{~/.guix-profile} ?

Vous pouvez lui assigner le rôle que vous souhaitez. Habituellement, vous y
installerez un manifeste des paquets que vous voulez pouvoir utiliser dans
toutes les situations.

Autrement, vous pouvez en faire un profil sans manifeste pour des paquets
sans importance que vous voulez juste garder quelques jours. C'est une
manière de pouvoir facilement lancer

@example
guix install package-foo
guix upgrade package-bar
@end example

sans avoir à spécifier un profil.

@node Les avantages des manifestes
@subsection Les avantages des manifestes

Les manifestes vous permettent de @dfn{déclarer} l'ensemble de paquets que
vous souhaitez voir dans un profil (@pxref{Écrire un manifeste,,, guix.fr,
le manuel de référence de GNU Guix}). Ils sont pratiques pour garder la
liste des paquets et, par exemple, les synchroniser entre plusieurs machines
avec un système de gestion de versions.

Les gens se plaignent souvent que les manifestes sont lents à installer
quand ils contiennent beaucoup de paquets. C'est particulièrement embêtant
quand vous voulez juste mettre à jour un paquet dans un gros manifeste.

C'est une raison de plus d'utiliser plusieurs profils, qui sont bien
pratiques pour diviser les manifestes en plusieurs ensembles de paquets de
même type. Plusieurs petits profils sont plus flexibles et plus maniables.

Les manifestes ont de nombreux avantages. En particulier, ils facilitent la
maintenance :

@itemize
@item
Lorsqu'un profil est créé à partir d'un manifeste, le manifeste lui-même est
suffisant pour garder la liste des paquets sous le coude et réinstaller le
profil plus tard sur un autre système. Pour les profils ad-hoc, il faudrait
générer une spécification de manifeste à la main et noter les versions de
paquets pour les paquets qui n'utilisent pas la version par défaut.

@item
@code{guix package --upgrade} essaye toujours de mettre à jour les paquets
qui ont des entrées propagées, même s'il n'y à rien à faire. Les manifestes
de Guix résolvent ce problème.

@item
Lorsque vous mettez partiellement à jour un profil, des conflits peuvent
survenir (à cause des dépendances différentes entre les paquets à jour et
ceux qui ne le sont pas) et ça peut être embêtant à corriger à la main. Les
manifestes suppriment ce problème puisque tous les paquets sont toujours mis
à jour en même temps.

@item
Comme on l'a mentionné plus haut, les manifestes permettent d'avoir des
profils reproductibles, alors que les commandes impératives @code{guix
install}, @code{guix upgrade}, etc, ne le peuvent pas, puisqu'elles
produisent un profil différent à chaque fois qu'elles sont lancées, même
avec les même paquets. Voir @uref{https://issues.guix.gnu.org/issue/33285,
la discussion sur ce problème}.

@item
Les spécifications de manifestes sont utilisables par les autres commandes
@samp{guix}. Par exemple, vous pouvez lancer @code{guix weather -m manifest}
pour voir combien de substituts sont disponibles, ce qui peut vous aider à
décider si vous voulez faire la mise à jour maintenant ou un peu plus
tard. Un autre exemple : vous pouvez lancer @code{guix package -m
manifest.scm} pour créer un lot contenant tous les paquets du manifeste (et
leurs références transitives).

@item
Enfin, les manifestes ont une représentation Scheme, le type
d'enregistrement @samp{<manifest>}. Vous pouvez les manipuler en Scheme et
les passer aux diverses @uref{https://fr.wikipedia.org/wiki/Api, API} de
Guix.
@end itemize

Vous devez bien comprendre que même si vous pouvez utiliser les manifestes
pour déclarer des profils, les deux ne sont pas strictement équivalents :
les profils pour l'effet de bord « d'épingler » les paquets dans le dépôt,
ce qui évite qu'ils ne soient nettoyés (@pxref{Invoquer guix gc,,, guix.fr,
le manuel de référence de GNU Guix}) et s'assure qu'ils seront toujours
disponibles à n'importe quel moment dans le futur. La commande @command{guix
shell} protège également les profils récemment utilisés du ramasse-miettes :
les profiles qui n'ont pas été utilisé pendant un certain temps peuvent être
cependant être nettoyés, avec les paquets auxquels ils se réfèrent.

Pour être sûr à 100 % qu'un profil donné ne sera pas nettoyé, installez le
manifeste dans un profil et d'utiliser @code{GUIX_PROFILE=/le/profil;
. "$GUIX_PROFILE"/etc/profile} comme on l'a expliqué plus haut : cela
garantie que l'environnement de bidouillage sera toujours disponible.

@emph{Avertissement de sécurité :} bien que garder d'anciens profils soit
pratique, gardez à l'esprit que les anciens paquets n'ont pas forcément reçu
les dernières corrections de sécurité.

@node Profils reproductibles
@subsection Profils reproductibles

Pour reproduire un profil bit-à-bit, on a besoin de deux informations :

@itemize
@item
un manifeste (@pxref{Écrire un manifeste,,, guix.fr, le manuel de référence
de GNU Guix}) ;
@item
une spécification de canal Guix (@pxref{Répliquer Guix,,, guix.fr, le manuel
de référence de GNU Guix}).
@end itemize

En effet, les manifestes seuls ne sont pas forcément suffisants :
différentes versions de Guix (ou différents canaux) peuvent produire des
sorties différentes avec le même manifeste.

Vous pouvez afficher la spécification de canaux Guix avec @samp{guix
describe --format=channels} (@pxref{Invoquer guix describe,,, guix.fr, le
manuel de référence de GNU Guix}). Enregistrez-la dans un fichier, par
exemple @samp{channel-specs.scm}.

Sur un autre ordinateur, vous pouvez utiliser le fichier de spécification de
canaux et le manifeste pour reproduire exactement le même profil :

@example
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
GUIX_EXTRA=$HOME/.guix-extra

mkdir -p "$GUIX_EXTRA"/my-project
guix pull --channels=channel-specs.scm --profile="$GUIX_EXTRA/my-project/guix"

mkdir -p "$GUIX_EXTRA_PROFILES/my-project"
"$GUIX_EXTRA"/my-project/guix/bin/guix package \
 --manifest=/path/to/guix-my-project-manifest.scm \
 --profile="$GUIX_EXTRA_PROFILES"/my-project/my-project
@end example

Vous pouvez supprimer le profil des canaux Guix que vous venez d'installer
avec la spécification de canaux, le profil du projet n'en dépend pas.

@node Développement logiciel
@chapter Développement logiciel

@cindex développement, avec Guix
@cindex développement logiciel, avec Guix
Guix est un outil pratique pour les développeurs ; @command{guix shell},
notamment, fournit un environnement de développement autonome et complet
pour votre paquet peu importe le langage dans lequel il est écrit
(@pxref{Invoquer guix shell,,, guix.fr, Manuel de référence GNU Guix}).
Pour en bénéficier, vous devez écrire une définition de paquet accessible
depuis Guix officiel, un canal, ou bien depuis les sources de votre projet
dans le fichier @file{guix.scm}.  Cette dernière option est particulièrement
alléchante : la seule chose à faire pour initialiser son environnement de
travail est d’exécuter @command{guix shell} sans argument dans le dépôt du
projet.

Les besoins des projets logiciels ne se limitent cependant pas à
l’environnement d’exécution.  Comment effectuer l’intégration continue du
code dans des environnements de compilation Guix ? Comment livrer le code
directement aux utilisatrices aventureuses ? Ce chapitre décrit l’ensemble
des fichiers qu’une développeuse peut ajouter à son dépôt pour créer un
environnement de développement, d’intégration continue et de livraison
continue basé sur Guix uniquement@footnote{Ce chapitre est une adaptation de
@uref{https://guix.gnu.org/fr/blog/2023/from-development-environments-to-continuous-integrationthe-ultimate-guide-to-software-development-with-guix/,
cet article de blog} (en anglais) publié en juin 2023 sur le site de Guix.}.

@menu
* Guide de démarrage::       Étape 0 : Utiliser `guix shell'.
* Compilation avec Guix::    Étape 1 : compiler votre code.
* Le dépôt comme canal::     Étape 2 : Transformer le dépôt en canal.
* Variantes de paquets::     Bonus : Définir des variants.
* Mettre en place une intégration continue::  Étape 3 : intégration 
                                                continue.
* Manifeste de construction::  Bonus : Manifeste.
* Récapitulatif::            Récapitulatif
@end menu

@node Guide de démarrage
@section Guide de démarrage

Comment entreprendre la ``Guixification'' d’un dépôt ? La première étape,
comme vu précédemment, consiste à ajouter un fichier @file{guix.scm} à la
racine du dépôt. Nous prendrons comme exemple @uref{,Guile} au long de
chapitre. Guile est (principalement) écrit en Scheme et en C, possède
plusieurs dépendances---Une suite d’outils de compilation C, des
bibliothèques C, Autoconf et sa clique, LaTeX, et ainsi de suite… Le fichier
@file{guix.scm} ressemblera à une définition de paquet traditionnelle
(@pxref{Définition des paquets,,, guix.fr, Manuel de référence de GNU Guix})
sans le @code{define-public} :

@lisp
;; Le fichier « guix.scm » pour Guile, à utiliser avec « guix shell ».

(use-modules (guix)
             (guix build-system gnu)
             ((guix licenses) #:prefix license:)
             (gnu packages autotools)
             (gnu packages base)
             (gnu packages bash)
             (gnu packages bdw-gc)
             (gnu packages compression)
             (gnu packages flex)
             (gnu packages gdb)
             (gnu packages gettext)
             (gnu packages gperf)
             (gnu packages libffi)
             (gnu packages libunistring)
             (gnu packages linux)
             (gnu packages pkg-config)
             (gnu packages readline)
             (gnu packages tex)
             (gnu packages texinfo)
             (gnu packages version-control))

(package
  (name "guile")
  (version "3.0.99-git")                          ; un numéro de version cool
  (source #f)                                     ; pas de source
  (build-system gnu-build-system)
  (native-inputs
   (append (list autoconf
                 automake
                 libtool
                 gnu-gettext
                 flex
                 texinfo
                 texlive-base                 ; pour « make pdf »
                 texlive-epsf
                 gperf
                 git
                 gdb
                 strace
                 readline
                 lzip
                 pkg-config)

           ;; Lors de la compilation croisée, une version native de Guile est
           ;; nécessaire.
           (if (%current-target-system)
               (list this-package)
               '())))
  (inputs
   (list libffi bash-minimal))
  (propagated-inputs
   (list libunistring libgc))

  (native-search-paths
   (list (search-path-specification
          (variable "GUILE_LOAD_PATH")
          (files '("share/guile/site/3.0")))
         (search-path-specification
          (variable "GUILE_LOAD_COMPILED_PATH")
          (files '("lib/guile/3.0/site-ccache")))))
  (synopsis "Scheme implementation intended especially for extensions")
  (description
   "Guile is the GNU Ubiquitous Intelligent Language for Extensions,
and it's actually a full-blown Scheme implementation!")
  (home-page "https://www.gnu.org/software/guile/")
  (license license:lgpl3+))
@end lisp

Même si ça peut paraître fastidieux, une personne souhaitant bidouiller
Guile n’aura désormais qu’à exécuter :

@lisp
guix shell
@end lisp

pour obtenir un environnement d’exécution avec toutes les dépendances de
Guile : celle listées ci-dessus, mais également les @emph{dépendances
implicites} : la suite d’outils GCC, GNU Make, sed, grep, etc.
@xref{Invoquer guix shell,,, guix.fr, Manuel de référence de GNU Guix} pour
plus d’informations sur @command{guix shell}.

@quotation La recommandation du chef
Nous suggérons la création d’environnements de développement comme suit :

@example
guix shell --container --link-profile
@end example

@noindent
… ou plus brièvement :

@example
guix shell -CP
@end example

Nous obtenons un conteneur avec toutes les dépendances qui apparaissent dans
@code{$HOME/.guix-profile} qui permet de profiter des caches comme le
@file{config.cache} (@pxref{Cache Files,,, autoconf, Autoconf}) ainsi que
des noms de fichier absolus stockés dans les éventuels @code{Makefile}
autogénérés ou autres. L’exécution de l’environnement logiciel dans un
conteneur apporte la garantie que seuls les dépendances de Guix et le
dossier courant sont accessibles ; aucun élément du système ne peut
interférer avec l’environnement de développement ainsi créé.
@end quotation

@node Compilation avec Guix
@section Niveau 1 : Compiler avec Guix

Maintenant que nous avons notre définition de paquet (@pxref{Guide de démarrage}), pour ne pas en profiter pour compiler Guile avec Guix ? Nous
avions omis le champ @code{source} car @command{guix shell} ne se préoccupe
que des @emph{dépendances} du paquet---afin de mettre en place
l’environnement de développement.

Pour compiler le paquet il nous faut remplir le champ @code{source} avec
quelque chose du style :

@lisp
(use-modules (guix)
             (guix git-download)  ; pour « git-predicate »
             @dots{})

(define vcs-file?
  ;; Renvoie vrai lorsque le fichier donné est sous contrôle de version.
  (or (git-predicate (current-source-directory))
      (const #t)))                                ; pas dans un dépôt Git

(package
  (name "guile")
  (version "3.0.99-git")                          ; un numéro de version cool
  (source (local-file "." "guile-checkout"
                      #:recursive? #t
                      #:select? vcs-file?))
  @dots{})
@end lisp

Voici la différence entre la version actuelle et la section précédente :

@enumerate
@item
Nous avons ajouté @code{(guix git-download)} à la liste des modules importés
pour la procédure @code{git-predicate}.
@item
Nous avons défini la procédure @code{vcs-file?} qui nous permet de savoir si
un fichier est versionné. Pour faire bonne mesure nous répondons toujours
oui lorsque nous ne sommes pas dans un dépôt Git.
@item
Nous remplissons la @code{source} avec un
@uref{https://guix.gnu.org/manual/devel/fr/html_node/G_002dExpressions.html#index-local_002dfile,@code{local-file}}---une
copie récursive des fichiers versionnés (avec le @code{#:select?}) du
dossier courant (@code{"."}).
@end enumerate

À partir de là, notre @file{guix.scm} prend un second rôle : il nous permet
de compiler le logiciel avec Guix. L’intérêt réside en ce que cette
compilation est ``pure''---nous sommes certains qu’aucun élément de notre
copie de travail ou du système n’interfère avec la compilation--et de tester
plein de choses. Premièrement, nous pouvons réaliser une compilation native
standard :

@example
guix build -f guix.scm
@end example

Mais nous pouvons également compiler pour un autre système (après avoir mis
en place @pxref{Réglages du déchargement du démon,,, guix.fr, Manuel de
référence de GNU Guix} ou @pxref{Services de virtualisation, Émulation
transparente avec QEMU,, guix.fr, Manuel de référence de GNU Guix}) :

@lisp
guix build -f guix.scm -s aarch64-linux -s riscv64-linux
@end lisp

@noindent
… ou en compilation croisée :

@lisp
guix build -f guix.scm --target=x86_64-w64-mingw32
@end lisp

Nous pouvons également utiliser les @dfn{transformations de paquets} pour
tester des variantes (@pxref{Options de transformation de paquets,,,
guix.fr, Manuel de référence de GNU Guix}) :

@example
# Et si nous construisions avec Clang au lieu de GCC ?
guix build -f guix.scm \
  --with-c-toolchain=guile@@3.0.99-git=clang-toolchain

# Qu'en est-il de ce drapeau de configuration rarement testé ?
guix build -f guix.scm \
  --with-configure-flag=guile@@3.0.99-git=--disable-networking
@end example

Pratique !

@node Le dépôt comme canal
@section Niveau 2 : Le dépôt comme canal

Nous avons maintenant un dépôt Git contenant (entre autres) une définition
de paquet (@pxref{Compilation avec Guix}).  Ne pourrions-nous pas l’utiliser
comme @dfn{canal} (@pxref{Canaux,,, guix.fr, Manuel de référence de GNU
Guix}) ? Après tout, les canaux sont conçues pour livrer des définitions de
paquets aux utilisateurs, et c’est ce que nous faisons avec notre
@file{guix.scm}.

Il s’avère que nous pouvons effectivement faire de notre dépôt un canal,
mais à une condition : nous devons créer un dossier à part pour le(s)
fichier(s) @code{.scm} du canal, afin que @command{guix pull} ne télécharge
pas des @code{.scm} liées au dépôt lors d’une mise à jour --- il y en a
beaucoup dans Guile ! Voici donc notre disposition initiale, en conservant
un lien symbolique à la racine vers @file{guix.scm} pour la commande
@command{guix shell} :

@lisp
mkdir -p .guix/modules
mv guix.scm .guix/modules/guile-package.scm
ln -s .guix/modules/guile-package.scm guix.scm
@end lisp

Pour rendre notre fichier @file{guix.scm} utilisable dans le canal, nous
devons l’adapter en @dfn{module de paquets} (@pxref{Modules de paquets,,,
guix.fr, Manuel de référence de GNU Guix}) en utilisant @code{define-module}
à la place de @code{use-modules} au début. Nous devons également
@emph{exporter} une variable de paquet avec @code{define-public} tout en
retournant le paquet à la fin du fichier pour qu’il reste utilisable avec
@command{guix shell} et @command{guix build -f guix.scm}. Le résultat final
ressemble à ça (en omettant les parties identiques) :

@lisp
(define-module (guile-package)
  #:use-module (guix)
  #:use-module (guix git-download)   ; pour « git-predicate »
  @dots{})

(define vcs-file?
  ;; Renvoie vrai lorsque le fichier donné est sous contrôle de version.
  (or (git-predicate (dirname (dirname (current-source-directory))))
      (const #t)))                                ; pas dans un dépôt Git

(define-public guile
  (package
    (name "guile")
    (version "3.0.99-git")                          ; un numéro de version cool
    (source (local-file "../.." "guile-checkout"
                        #:recursive? #t
                        #:select? vcs-file?))
    @dots{}))

;; Renvoie l’objet du paquet défini ci-dessus en fin de module.
guile
@end lisp

Il nous manque plus que le dernier élément : le
@uref{https://guix.gnu.org/manual/devel/fr/html_node/Modules-de-paquets-dans-un-sous_002drepertoire.html,
fichier @code{.guix-channel}} qui permet à Guix de trouver les modules de
paquets de notre dépôt :

@lisp
;; Ce fichier nous permet de présenter ce dépôt comme un canal Guix.

(channel
  (version 0)
  (directory ".guix/modules"))  ; rechercher les modules de paquets dans .guix/modules/
@end lisp

Pour récapituler, nous avons :

@lisp
.
├── .guix-channel
├── guix.scm → .guix/modules/guile-package.scm
└── .guix
    └── modules
       └── guile-package.scm
@end lisp

Et voilà, nous avons un canal ! (Nous pourrions faire mieux en ajoutant le
support de
@uref{https://guix.gnu.org/manual/devel/fr/html_node/Specifier-les-autorisations-des-canaux.html,@emph{l’authentification
de canal}} pour garantir l’authenticité du code. Nous n’entrerons pas dans
les détails mais il peut être important de le considérer!) Les utilisateurs
peuvent récupérer notre canal avec le fichier
@uref{https://guix.gnu.org/manual/devel/fr/html_node/Specifier-des-canaux-supplementaires.html,@file{~/.config/guix/channels.scm}}
en y ajoutant quelque chose du genre :

@lisp
(append (list (channel
                (name 'guile)
                (url "https://git.savannah.gnu.org/git/guile.git")
                (branch "main")))
        %default-channels)
@end lisp

Après un @command{guix pull}, nous pouvons voir les nouveaux paquets :

@example
$ guix describe
Génération 264  26 mai 2023 16:00:35    (actuelle)
  guile 36fd2b4
    URL du dépôt : https://git.savannah.gnu.org/git/guile.git
    branche : main
    commit : 36fd2b4920ae926c79b936c29e739e71a6dff2bc
  guix c5bc698
    URL du dépôt : https://git.guix.gnu.org/guix.git
    commit : c5bc698e8922d78ed85989985cc2ceb034de2f23
$ guix package -A ^guile$
guile   3.0.99-git      out,debug       guile-package.scm:51:4
guile   3.0.9           out,debug       gnu/packages/guile.scm:317:2
guile   2.2.7           out,debug       gnu/packages/guile.scm:258:2
guile   2.2.4           out,debug       gnu/packages/guile.scm:304:2
guile   2.0.14          out,debug       gnu/packages/guile.scm:148:2
guile   1.8.8           out             gnu/packages/guile.scm:77:2
$ guix build guile@@3.0.99-git
[@dots{}]
/gnu/store/axnzbl89yz7ld78bmx72vpqp802dwsar-guile-3.0.99-git-debug
/gnu/store/r34gsij7f0glg2fbakcmmk0zn4v62s5w-guile-3.0.99-git
@end example

Voici comment, en tant que développeuse, vous pouvez livrer votre logiciel
directement aux utilisateurs ! Sans aucun intermédiaire, sans perte de
transparence et un suivi de la provenance.

De plus, il devient également trivial de créer des images Docker, des
paquets Deb/RPM ou une archive avec @command{guix pack} (@pxref{Invoquer
guix pack,,, guix.fr, Manuel de référence de GNU Guix}) :

@example
# Que diriez-vous d’une image Docker de notre archive Guile ?
guix pack -f docker -S /bin=bin guile@@3.0.99-git

# Et d’un RPM repositionnable ?
guix pack -f rpm -R -S /bin=bin guile@@3.0.99-git
@end example

@node Variantes de paquets
@section Bonus : Variantes de paquets

Nous avons maintenant un vrai canal, mais celui-ci ne contient qu’un seul
paquet (@pxref{Le dépôt comme canal}). Pendant que nous y sommes,
nous pouvons définir des @dfn{variantes de paquets} (@pxref{Définition de
variantes de paquets,,, guix.fr, le manuel de référence de GNU Guix}) dans
notre fichier @file{guile-package.scm}. Ce sont des variantes que nous
voulons pouvoir tester en tant que développeurs Guile, exactement comme nous
avons fait ci-dessus avec les options de transformation. Nous pouvons les
ajouter comme suit :

@lisp
;; Voici le fichier « .guix/modules/guile-package.scm ».

(define-module (guile-package)
  @dots{})

(define-public guile
  @dots{})

(define (package-with-configure-flags p flags)
  "Renvoie P avec FLAGS en tant que drapeaux supplémentaires pour « configure »."
  (package/inherit p
    (arguments
     (substitute-keyword-arguments (package-arguments p)
       ((#:configure-flags original-flags #~(list))
        #~(append #$original-flags #$flags))))))

(define-public guile-without-threads
  (package
    (inherit (package-with-configure-flags guile
                                           #~(list "--without-threads")))
    (name "guile-without-threads")))

(define-public guile-without-networking
  (package
    (inherit (package-with-configure-flags guile
                                           #~(list "--disable-networking")))
    (name "guile-without-networking")))


;; Renvoie l’objet du paquet défini ci-dessus à la fin du module.
guile
@end lisp

Une fois le canal récupéré, nous pouvons construire ces variantes en tant
que paquets ordinaires. Autrement, à partir d'un checkout de Guile, nous
pouvons exécuter une commande comme celle-ci depuis le niveau supérieur :

@lisp
guix build -L $PWD/.guix/modules guile-without-threads
@end lisp

@node Mettre en place une intégration continue
@section Niveau 3 : Mettre en place une intégration continue

@cindex intégration continue (CI)
Le canal défini ci-dessus (@pxref{Le dépôt comme canal}) devient
encore plus intéressant lorsqu’on installe
@uref{https://fr.wikipedia.org/wiki/Int%C3%A9gration_continue, une
@dfn{intégration continue}} (CI). Il existe plusieurs manière de le faire.

Vous pouvez utiliser un des outils d’intégration continue standards, comme
GitLab-CI. Pour ce faire, assurez-vous de lancer vos tâches sur une image
Docker ou sur une machine virtuelle sur laquelle Guix est installé. Si nous
faisions ça pour Guile, nous aurions une tâche qui exécute une commande
shell comme celle-ci :

@lisp
guix build -L $PWD/.guix/modules guile@@3.0.99-git
@end lisp

Cette manière de procéder fonctionne bien et a l’avantage d’être facile à
réaliser sur votre plateforme CI préférée.

Ceci dit, vous en tirerez un maximum en utilisant
@uref{https://guix.gnu.org/fr/cuirass,Cuirass}, un outil de Cl créé pour
Guix et bien intégré à ce dernier. S’en servir nécessite plus de travail
qu’un outil de Cl en ligne, parce qu’il faut d’abord l’installer, mais cette
phase d’installation est grandement simplifiée si vous utilisez le service
du Système Guix (@pxref{Intégration continue,,, guix.fr, le manuel de
référence de GNU Guix}). Revenons à notre exemple dans lequel on donne à
Cuirass un fichier de spécifications comme celui-ci :

@lisp
;; Fichier de spécification Cuirass pour construire tous les paquets du canal ‘guile’.
(list (specification
        (name "guile")
        (build '(channels guile))
        (channels
         (append (list (channel
                         (name 'guile)
                         (url "https://git.savannah.gnu.org/git/guile.git")
                         (branch "main")))
                 %default-channels))))
@end lisp

Cela diffère de ce que vous feriez avec d'autres outils de CI de deux
manières principales :

@itemize
@item
Cuirass sait qu’il suit @emph{deux} canaux, @code{guile} et @code{guix}. En
effet, notre paquet @code{guile} dépend de nombreux paquets fournis par le
canal @code{guix} : GCC, GNU libc, libffi et ainsi de suite. Des changements
dans les paquets du canal @code{guix} peuvent potentiellement influencer
notre build @code{guile} et c’est quelque chose qu’on aimerait voir arriver
le plus vite possible en tant que développeurs Guile.
@item
Les résultats des constructions ne sont pas supprimés : ils peuvent être
distribués en tant que @dfn{substituts} pour que les utilisateurs de notre
canal @code{guile} puissent avoir des fichiers binaires pré-construits de
manière transparente. (@pxref{Substituts,,, guix.fr, le manuel de référence
de GNU Guix} pour plus d’informations sur les substituts).
@end itemize

Pour le développeur, le résultat est cette
@uref{https://ci.guix.gnu.org/jobset/guile,page de statut} qui liste les
@emph{évaluations} : chacune est une combinaison des commits des canaux
@code{guix} et @code{guile} qui donnent un certain nombre de tâches ; une
tâche par paquet défini dans @file{guile-package.scm} multipliée par le
nombre d’architectures cibles.

Quant aux substituts, ils sont gratuits ! À titre d’exemple, puisque notre
jobset @code{guile} est construit sur ci.guix.gnu.org, qui fait tourner
@command{guix publish} (@pxref{Invoquer guix publish,,, guix.fr, le manuel
de référence de GNU Guix}) en plus de Cuirass, on peut récupérer
automatiquement des substituts pour les constructions de @code{guile} à
partir de ci.guix.gnu.org. Aucune action supplémentaire n’est nécessaire.

@node Manifeste de construction
@section Bonus : Manifeste de construction

La spécification de Cuirass ci-dessus est pratique : il construit chaque
paquet dans notre canal, y compris quelques variantes (@pxref{Mettre en place une intégration continue}). Cependant, cela pourrait être trop peu expressif
dans certains cas : il se peut quon veuille des opérations de compilation
croisée ou des transformations spécifiques, des images Docker, des paquets
RPM/Deb, ou même des tests système.

Pour ce faire, vous pouvez écrire un @dfn{manifeste} (@pxref{Écrire un
manifeste,,, guix.fr, le manuel de référence de GNU Guix}). Celui que nous
avons pour Guile comporte des entrées pour les variantes de paquets que nous
avons définis plus haut, ainsi que des variantes et des compilations
croisées supplémentaires :

@lisp
;; Ceci correspond à « .guix/manifest.scm ».

(use-modules (guix)
             (guix profiles)
             (guile-package))   ; importe notre propre module de paquets

(define* (package->manifest-entry* package system
                                   #:key target)
  "Renvoie une entrée de manifeste pour PACKAGE sur le SYSTEM, éventuellement compilé
de manière croisée pour TARGET."
  (manifest-entry
    (inherit (package->manifest-entry package))
    (name (string-append (package-name package) "." system
                         (if target
                             (string-append "." target)
                             "")))
    (item (with-parameters ((%current-system system)
                            (%current-target-system target))
            package))))

(define native-builds
  (manifest
   (append (map (lambda (system)
                  (package->manifest-entry* guile system))

                '("x86_64-linux" "i686-linux"
                  "aarch64-linux" "armhf-linux"
                  "powerpc64le-linux"))
           (map (lambda (guile)
                  (package->manifest-entry* guile "x86_64-linux"))
                (cons (package
                        (inherit (package-with-c-toolchain
                                  guile
                                  `(("clang-toolchain"
                                     ,(specification->package
                                       "clang-toolchain")))))
                        (name "guile-clang"))
                      (list guile-without-threads
                            guile-without-networking
                            guile-debug
                            guile-strict-typing))))))

(define cross-builds
  (manifest
   (map (lambda (target)
          (package->manifest-entry* guile "x86_64-linux"
                                    #:target target))
        '("i586-pc-gnu"
          "aarch64-linux-gnu"
          "riscv64-linux-gnu"
          "i686-w64-mingw32"
          "x86_64-linux-gnu"))))

(concatenate-manifests (list native-builds cross-builds))
@end lisp

Nous n’allons pas entrer dans les détails du manifeste ; il suffit de dire
qu'il apporte une flexibilité supplémentaire. Nous devons maintenant
demander à Cuirass de construire ce manifeste, chose faite à l'aide d'une
spécification légèrement différente de la précédente :

@lisp
;; Fichier de spécification Cuirass pour construire tous les paquets du canal « guile ».
(list (specification
        (name "guile")
        (build '(manifest ".guix/manifest.scm"))
        (channels
         (append (list (channel
                         (name 'guile)
                         (url "https://git.savannah.gnu.org/git/guile.git")
                         (branch "main")))
                 %default-channels))))
@end lisp

Nous avons changé la partie du fichier de spécification @code{(build
@dots{})} en @code{'(manifest ".guix/manifest.scm")}, pour qu'il choisisse
notre manifeste, et c'est tout !

@node Récapitulatif
@section Récapitulatif

Nous avons choisi Guile en tant qu’exemple dans ce chapitre et vous pouvez
voir le résultat ici :

@itemize
@item
@uref{https://git.savannah.gnu.org/cgit/guile.git/tree/.guix-channel?id=cd57379b3df636198d8cd8e76c1bfbc523762e79,@code{.guix-channel}} ;
@item
@uref{https://git.savannah.gnu.org/cgit/guile.git/tree/.guix/modules/guile-package.scm?id=cd57379b3df636198d8cd8e76c1bfbc523762e79,@code{.guix/modules/guile-package.scm}}
avec le lien symbolique de plus haut niveau @file{guix.scm} ;
@item
@uref{https://git.savannah.gnu.org/cgit/guile.git/tree/.guix/manifest.scm?id=cd57379b3df636198d8cd8e76c1bfbc523762e79,@code{.guix/manifest.scm}}.
@end itemize

De nos jours, les dépôts sont souvent remplis de fichiers de configuration
pour divers outils : @code{.envrc}, @code{.gitlab-ci.yml},
@code{.github/workflows}, @code{Dockerfile}, @code{.buildpacks},
@code{Aptfile}, @code{requirements.txt}, etc. Nous pouvons avoir
l’impression de proposer un ensemble de fichiers @emph{supplémentaires},
mais en réalité ces fichiers sont suffisamment expressifs pour
@emph{remplacer} la plupart ou la totalité des éléments listés ci-dessus.

Avec quelques fichiers, nous obtenons une prise en charge pour :

@itemize
@item
des environnements de développement (@command{guix shell}) ;
@item
des constructions de test vierges, y compris pour les variantes de paquets
et pour la compilation croisée (@command{guix build}) ;
@item
une intégration continue (à l’aide de Cuirass ou d’un autre outil) ;
@item
une livraison continue aux utilisateurs (@emph{via} le canal et avec des
binaires pré-construits) ;
@item
une génération d’artefacts de construction dérivés tels que les images
Docker ou les paquets Deb/RPM (@command{guix pack}).
@end itemize

Voilà un ensemble d'outils correctement (à notre avis !) unifiés pour un
déploiement de logiciels reproductible, ainsi qu'un exemple de la manière
dont vous pouvez en profiter en tant que développeur !


@c *********************************************************************
@node Gestion de l'environnement
@chapter Gestion de l'environnement

Guix fournit plusieurs outils pour gérer l'environnement. Ce chapitre vous
montre ces outils.

@menu
* Environnement Guix avec direnv::  Créer un environnement Guix avec direnv
@end menu

@node Environnement Guix avec direnv
@section Environnement Guix avec direnv

Guix fournit un paquet @samp{direnv}, qui peut étendre le shell après avoir
changé de répertoire de travail. Vous pouvez utiliser cet outil pour
préparer un environnement Guix pur.

L'exemple suivant fournit une fonction shell dans @file{~/.direnvrc}, qui
peut être utilisée dans le dépôt Git de Guix dans @file{~/src/guix/.envrc}
pour créer un environnement de construction similaire à celui décrit dans
@ref{Construire depuis Git,,, guix.fr, le manuel de référence de GNU Guix}.

Créez un fichier @file{~/.direnv} avec le code Bash suivant :

@example
# Grâce à <https://github.com/direnv/direnv/issues/73#issuecomment-152284914>
export_function()
@{
  local name=$1
  local alias_dir=$PWD/.direnv/aliases
  mkdir -p "$alias_dir"
  PATH_add "$alias_dir"
  local target="$alias_dir/$name"
  if declare -f "$name" >/dev/null; then
    echo "#!$SHELL" > "$target"
    declare -f "$name" >> "$target" 2>/dev/null
    # Remarquez qu'on ajoute des variables shells au déclencheur de la fonction.
    echo "$name \$*" >> "$target"
    chmod +x "$target"
  fi
@}

use_guix()
@{
    # indique un jeton Github.
    export GUIX_GITHUB_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

    # Nettoie « GUIX_PACKAGE_PATH ».
    export GUIX_PACKAGE_PATH=""

    # Recrée une racine du ramasse miettes.
    gcroots="$HOME/.config/guix/gcroots"
    mkdir -p "$gcroots"
    gcroot="$gcroots/guix"
    if [ -L "$gcroot" ]
    then
        rm -v "$gcroot"
    fi

    # Divers paquets.
    PACKAGES_MAINTENANCE=(
        direnv
        git
        git:send-email
        git-cal
        gnupg
        guile-colorized
        guile-readline
        less
        ncurses
        openssh
        xdot
    )

    # Paquets de l'environnement.
    PACKAGES=(help2man guile-sqlite3 guile-gcrypt)

    # Merci <https://lists.gnu.org/archive/html/guix-devel/2016-09/msg00859.html>
    eval "$(guix shell --search-paths --root="$gcroot" --pure \
     --development guix $@{PACKAGES[@@]@} $@{PACKAGES_MAINTENANCE[@@]@} "$@@")"

    # Défini les drapeaux configure à l'avance.
    configure()
    @{
        ./configure
    @}
    export_function configure

    # Lance make et construit éventuellement quelque chose.
    build()
    @{
        make -j 2
        if [ $# -gt 0 ]
        then
            ./pre-inst-env guix build "$@@"
        fi
    @}
    export_function build

    # Défini une commande Git pour pousser.
    push()
    @{
        git push --set-upstream origin
    @}
    export_function push

    clear                        # Nettoie l'écran.
    git-cal --author='Votre Nom' # Montre le calendrier des contributions.

    # Montre l'aide des commandes.
    echo "
build          construit un paquet ou juste un projet si aucun argument n'est fournit
configure      lance ./configure avec les paramètres pré-définis
push           pousse un dépôt Git
"
@}
@end example

Tous les projets contenant un @file{.envrc} avec une chaine @code{use guix}
aura des variables d'environnement et des procédures prédéfinies.

Lancez @command{direnv allow} pour mettre en place l'environnement pour la
première fois.


@c *********************************************************************
@node Recherche reproductible
@chapter Utiliser Guix pour la recherche reproductible

@cindex recherche reproductible
Comme il prend en charge les déploiements reproductibles, Guix est une base
solide pour @dfn{les méthodes de recherche reproductibles}. Cette section
s'adresse aux scientifiques ; elle montre comment ajouter Guix à sa trousse
à outils de recherche reproductible@footnote{Ce chapitre est adapté d'un
@uref{https://hpc.guix.info/blog/2023/06/a-guide-to-reproducible-research-papers/,
billet de blog publié sur le site web de Guix-HPC en 2023.}.}.

Avec Guix comme base de votre méthode de travail informatique, vous pouvez
obtenir ce qui se trouve être par essence des @emph{métadonnées de
provenance de l'exécutable} : c'est comme une liste de paires de nom de
paquet et de version que certaines personnes fournissent en fin de leur
publication, sauf que c'est plus précis et immédiatement déployable.

Cette section est un guide de seulement quatre étapes sur la manière de
rendre vos expériences informatiques reproductibles avec Guix, et comment
fournir cette information dans vos papiers de recherche.

@menu
* Mettre en place l'environnement::  Étape 1 : Utiliser `guix shell'.
* Enregistrer l'environnement::  Étape 2 : Utiliser `guix describe'.
* S'assurer d'un archivage du code source sur le long-terme::  Étape 3 : 
                                                                 Software 
                                                                 Heritage.
* Référencer l'environnement logiciel::  Étape 4 : les SWHID.
@end menu

@node Mettre en place l'environnement
@section Étape 1 : Mettre en place l'environnement

La première étape consiste à identifier précisément les paquets dont vous
avez besoin dans votre environnement logiciel pour exécuter votre expérience
informatique.

En supposant que vous avez un script Python qui utilise NumPy, vous pouvez
commencer par créer un environnement qui contient ces deux paquets et lancer
votre code dans cet environnement (@pxref{Invoquer guix shell,,, guix.fr, le
manuel de référence de GNU Guix}) :

@example
guix shell -C python python-numpy -- python3 ./myscript.py
@end example

Le drapeau @code{-C} ici (ou @code{--container}) indique à @command{guix
shell} de créer cet environnement dans un conteneur isolé avec rien d'autre
que les deux paquets demandés. De cette manière, si @command{./myscript.py}
a besoin de plus que ces deux paquets, il échouera à s'exécuter et vous le
remarquerez immédiatement. Sur certains système @code{--container} n'est pas
pris en charge ; dans ce cas, vous pouvez utiliser @code{--pure} à la place.

Peut-être que vous remarquerez que vous avez également besoin de Pandas et
vous l'ajoutez à l'environnement :

@example
guix shell -C python python-numpy python-pandas -- \
  python3 ./myscript.py
@end example

Si vous n'arrivez pas à deviner le nom du paquet (celui-là était facile !),
essayez @code{guix search}.

Les environnements pour Python, R, et les langages haut-niveau similaires
sont assez faciles à configurer. Pour du code C/C++, vous aurez peut-être
besoin de paquets supplémentaires :

@example
guix shell -C gcc-toolchain cmake coreutils grep sed make -- @dots{}
@end example

Ou bien vous pourriez tout aussi bien fournir une définition de paquet pour
votre code --- @pxref{Définition des paquets,,, guix.fr, le manuel de
référence de GNU Guix} pour en apprendre plus.

Enfin, vous obtiendrez une liste des paquets qui satisfont vos besoins.

@quotation Que faire s'il manque un paquet ?
Guix et les canaux scientifiques principaux fournissent environ
@uref{https://hpc.guix.info/browse, des dizaines de milliers de
paquets}. Cependant, il est toujours possible que le paquet dont vous avez
besoin ne soit pas disponible.

Dans ce cas, vous devrez fournir une définition pour celui-ci
(@pxref{Définition des paquets,,, guix.fr, le manuel de référence de GNU
Guix}) dans un canal dédié que vous possédez (@pxref{Écrire de nouveaux de
canaux,,, guix.fr, le manuel de référence de GNU Guix}). Pour des logiciels
en Python, R et d'autres langages de haut-niveau, le gros du travail peut
être automatisé en utilisant @command{guix import} (@pxref{Invoquer guix
import,,, guix.fr, le manuel de référence de GNU Guix}).

Rejoignez @uref{https://guix.gnu.org/contact/,la joyeuse communauté Guix}
pour recevoir de l'aide !
@end quotation

@node Enregistrer l'environnement
@section Étape 2 : Enregistrer l'environnement

Maintenant que vous avez cette ligne de commande @code{guix shell} avec une
liste de paquets, le mieux à faire est de la sauvegarder dans un fichier
@emph{manifeste} --- la nomenclature logicielle --- que Guix peut ensuite
utiliser (@pxref{Écrire un manifeste,,, guix.fr, le manuel de référence de
GNU Guix}). Le plus facile est de commencer par « traduire » votre ligne de
commande en un manifeste :

@example
guix shell python python-numpy python-pandas \
  --export-manifest > manifest.scm
@end example

Mettez ce manifeste sous contrôle de version ! À partir de maintenant
n'importe qui peut redéployer l'environnement logiciel décrit par le
manifeste et exécuter le code dans cet environnement :

@example
guix shell -C -m manifest.scm -- python3 ./myscript.py
@end example

Voilà ce à quoi ressemble un fichier @file{manifest.scm} :

@lisp
;; Ce qui suit est un "manifeste" équivalent à la ligne de commande que vous avez passée.
;; Vous pouvez le sauvegarder dans un fichier que vous pouvez passer à n'importe quelle commande « guix »
;; qui accepte l'option « --manifest » (ou « -m »).

(specifications->manifest
  (list "python" "python-numpy" "python-pandas"))
@end lisp

C'est un bout de code qui liste les paquets. Remarquez qu'il n'y a pas de
numéro de version ! En effet, ces numéros de version sont spécifiés dans les
définitions des paquets situés dans les canaux de Guix. Pour permettre aux
autres de reproduire exactement le même environnement que celui que vous
exécutez, vous devrez @emph{épingler Guix lui-même}, en capturant les
commits actuels des canaux de Guix avec @command{guix describe}
(@pxref{Répliquer Guix,,, guix.fr, le manuel de référence de GNU Guix}) :

@example
guix describe -f channels > channels.scm
@end example

@cindex fichiers verrous, pour la reproductibilité
Ce fichier @code{channels.scm} est semblable en principe aux « fichiers
verrous » que certains outils de déploiement utilisent pour fixer les
révisions de paquets. Vous devriez également le garder sous contrôle de
version dans votre code, et éventuellement le mettre à jour de temps en
temps lorsque vous voudrez exécuter votre code avec de nouvelles versions de
ses dépendances. Avec ce fichier, n'importe qui, @emph{à n'importe quel
moment et sur n'importe quelle machine}, peut maintenant reproduire
exactement le même environnement en exécutant :

@example
guix time-machine -C channels.scm -- \
  shell -C -m manifest.scm -- \
  python3 ./myscript.py
@end example

Dans cet exemple nous nous reposons sur le canal @code{guix}, qui fournit
les paquets Python dont nous avons besoin. Certains paquets dont vous avez
besoin peuvent se trouver @uref{https://hpc.guix.info/channels,dans d'autres
canaux} --- peut-être @code{guix-cran} si vous utilisez R, peut être
@code{guix-science}. Tout va bien : @code{guix describe} capture également
cela.

Bien sûr ajoutez un fichier @file{README} avec la commande exacte pour
exécuter votre code. Tout le monde n'utilise pas Guix, donc il peut être
utile de fournir également des instructions de mise en place minimales sans
Guix : les versions des paquets que vous utilisez, comment ces logiciels
sont construits, etc. Comme vu précédemment, ces instructions seront
probablement inexactes et peu pratiques à suivre au mieux. Cependant, il
peut s'agir d'un point d'entrée utile pour quelqu'un qui essaye de recréer
un environnement @emph{similaire} avec d'autres outils. Il devrait être
présenté de cette manière, en comprenant que la seule manière d'obtenir le
@emph{même} environnement est d'utiliser Guix.

@node S'assurer d'un archivage du code source sur le long-terme
@section Étape 3 : assurer l'archivage du code source sur le long-terme

Nous avons insisté sur le contrôle de version précédemment : pour les
fichiers @file{manifest.scm} et @file{channels.scm}, mais bien sûr également
pour votre propre code. Nous recommandons d'avoir ces deux fichiers
@file{.scm} dans le même répertoire que le code dont ils parlent.

Comme le but est de permettre la reproductibilité, la disponibilité du code
source est une préoccupation de premier plan. Les services d'hébergement de
code vont et viennent et nous n'aimerions pas voir notre code disparaître
d'un coup de tête et rendre notre recherche publiée impossible à
vérifier. @uref{https://www.softwareheritage.org/,Software Heritage} (SWH
pour les intimes) est @emph{la} solution à ce problème : SWH archive le code
source public et fournit des identifiants intrinsèques uniques pour s'y
référer --- @uref{https://swhid.org, @dfn{les SWHID}}. Guix lui-même est
@uref{https://doi.org/10.1145/3641525.3663622,connecté à SWH} pour 1°@
assurer que le code source de ses paquets est archivé, et 2°@ comme solution
de repli pour télécharger depuis l'archive SWH si le code disparaissait du
site originel.

Une fois que votre propre code est disponible sur un dépôt de contrôle de
version public, comme un dépôt Git sur le service d'hébergement de votre
laboratoire, vous pouvez demander à SWH de l'archiver en allant sur son
interface @uref{https://archive.softwareheritage.org/save/,Save Code
Now}. SWH traitera la requête de manière asynchrone et votre code finira par
arrivez dans @uref{https://archive.softwareheritage.org/,l'archive}.

@node Référencer l'environnement logiciel
@section Étape 4 : Référencer l'environnement logiciel

Cela nous amène à la dernière étape : référencer notre code @emph{et}
l'environnement logiciel dans notre papier. Nous avons déjà tout notre code
et les fichiers Guix dans le même dépôt, qui est archivé dans SWH. Grâce à
SWH, nous avons maintenant un SWHID, qui identifie de manière unique la
bonne révision de notre code.

En suivant le
@uref{https://www.softwareheritage.org/howto-archive-and-reference-your-code/,le
propre guide de SWH}, nous choisissons un identifiant de type
@code{swh:dir}, qui se réfère au répertoire de la révision ou du commit
concerné dans notre dépôt, et nous allons garder des @emph{informations
contextuelles} pour plus de clarté --- ce qui comprend l'URL originale. Pour
rassembler tout cela, nous conclurons notre papier avec une phrase de ce
style :

@quotation Exemple
Le code source utilisé pour produire cette étude, ainsi que les instructions
pour les exécuter dans le bon environnement logiciel avec GNU@ Guix, est
archivé sur Software Heritage à l'adresse
@uref{https://archive.softwareheritage.org/swh:1:dir:cc8919d7705fbaa31efa677ce00bef7eb374fb80;origin=https://gitlab.inria.fr/lcourtes-phd/edcc-2006-redone;visit=swh:1:snp:71a4d08ef4a2e8455b67ef0c6b82349e82870b46;anchor=swh:1:rev:36fde7e5ba289c4c3e30d9afccebbe0cfe83853a,@code{swh:1:dir:cc8919d7705fbaa31efa677ce00bef7eb374fb80;origin=https://gitlab.inria.fr/lcourtes-phd/edcc-2006-redone;visit=swh:1:snp:71a4d08ef4a2e8455b67ef0c6b82349e82870b46;anchor=swh:1:rev:36fde7e5ba289c4c3e30d9afccebbe0cfe83853a}}.
@end quotation

Avec ces informations, la personne qui lit votre papier peut :

@itemize
@item
récupérer le code source,
@item
reproduire son environnement logiciel avec @code{guix time-machine} et
exécuter le code,
@item
inspecter et éventuellement modifier le code aussi bien que son
environnement.
@end itemize

Mission accomplie !

@c *********************************************************************
@node Installer Guix sur une grappe de calcul
@chapter Installer Guix sur une grappe de calcul

@cindex installation d'une grappe de calcul
@cindex calcul haute-performance, HPC
@cindex HPC, calcul haute-performance
Guix est intéressant pour les scientifiques et les professionnels du
@acronym{HPC, calcul haute-performance}. Il facilite en effet le déploiement
de piles logicielles complexes et vous permet de le faire de manière
reproductible — vous pouvez redéployer exactement le même logiciel sur des
machines différentes et à des moments différents.

Dans ce chapitre nous allons voir comment l'administrateur·ice système d'une
grappe peut installer Guix sur le système, de sorte qu'il puisse être
utilisé sur tous les nœuds de la grappe, et nous discuterons de différents
compromis possibles@footnote{Ce chapitre est adapté d'un
@uref{https://hpc.guix.info/blog/2017/11/installing-guix-on-a-cluster/,
billet de blog publié sur le site web de Guix-HPC en 2017}.}.

@quotation Remarque
Nous supposons ici que la grappe utilise une distribution GNU/Linux autre
que le Système Guix et que nous allons installer Guix par-dessus.
@end quotation

@menu
* Mettre en place un nœud principal::  Le nœud qui fait tourner le démon.
* Mettre en place des nœuds de calcul::  Les nœuds clients.
* Accès réseau de la grappe::  Gérer les restrictions d'accès réseau.
* Utilisation de disque de la grappe::  Considérations d'utilisation de 
                                          l'espace disque.
* Considérations de sécurité de la grappe::  Garder votre grappe sécurisée.
@end menu

@node Mettre en place un nœud principal
@section Mettre en place un nœud principal

L'approche recommandée est d'installer un @emph{nœud principal} qui
exécuterait @command{guix-daemon} et exporterait @file{/gnu/store} sur NFS
vers les nœuds de calcul.

Rappelez-vous que @command{guix-daemon} est responsable du démarrage des
constructions et des téléchargements pour ses clients (@pxref{Invoquer
guix-daemon,,, guix.fr, le manuel de référence de GNU Guix}), et plus
généralement de l'accès à @file{/gnu/store} qui contient tous les binaires
des paquets construits par tous les utilisateur·ices (@pxref{Le dépôt,,,
guix.fr, le manuel de référence de GNU Guix}). Les « clients » signifient
toutes les commandes Guix que voient les utilisateurs et utilisatrices,
comme @command{guix install}. Sur une grappe, ces commandes peuvent être
lancées sur les nœuds de calcul et l'on souhaiterait qu'elles parlent au
@code{guix-daemon} du nœud principal.

Pour commencer, le nœud principal peut être installé suivant les
instructions d'installation binaires habituelles (@pxref{Installation
binaire,,, guix.fr, le manuel de référence de GNU Guix}). Grâce au script
d'installation, cela devrait être rapide. Une fois l'installation terminée,
nous devons faire quelques ajustements.

Comme nous voulons que @code{guix-daemon} soit joignable non seulement
depuis le nœud principal, mais aussi depuis les nœuds de calcul, nous devons
nous arranger pour qu'il se mette en écoute de connexions sur TCP/IP. Pour
ce faire, nous allons modifier le fichier de démarrage systemd de
@command{guix-daemon}, @file{/etc/systemd/system/guix-daemon.service}, et
ajouter un argument @code{--listen} à la ligne @code{ExecStart} ur qu'elle
ressemble à quelque chose comme :

@c Since Debian Buster, \ is documented to split lines.
@c https://manpages.debian.org/buster/systemd/systemd.exec.5.en.html
@c Use it in PDF and Info versions to avoid cut-off at the page border.
@ifnothtml
@example
ExecStart=/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon \
    --build-users-group=guixbuild \
    --listen=/var/guix/daemon-socket/socket --listen=0.0.0.0
@end example
@end ifnothtml
@ifhtml
@example
ExecStart=/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon --build-users-group=guixbuild --listen=/var/guix/daemon-socket/socket --listen=0.0.0.0
@end example
@end ifhtml

Pour que ces changements fassent effet, le service doit être redémarré :

@example
systemctl daemon-reload
systemctl restart guix-daemon
@end example

@quotation Remarque
@code{--listen=0.0.0.0} indique que @code{guix-daemon} traitera
@emph{toutes} les connexions TCP entrantes sur le port 44146
(@pxref{Invoquer guix-daemon,,, guix.fr, le manuel de référence de GNU
Guix}). C'est généralement acceptable sur une grappe dont le nœud principal
est exclusivement accessible à partir du réseau local de la grappe. Vous ne
voulez pas l'exposer sur Internet !
@end quotation

L'étape suivante consiste à définir nos exports NFS dans
@uref{https://linux.die.net/man/5/exports,@file{/etc/exports}} en ajoutant
quelque chose comme :

@example
/gnu/store    *(ro)
/var/guix     *(rw, async)
/var/log/guix *(ro)
@end example

Le répertoire @file{/gnu/store} peut être exporté en lecture-seule car seul
le @command{guix-daemon} du nœud principal le modifiera
jamais. @file{/var/guix} contient les @emph{profils utilisateurs} gérés par
@code{guix package}. Ainsi, pour permettre à tout le monde d'installer des
paquets avec @code{guix package}, il doit être en lecture-écriture.

Les utilisateurs et utilisatrices peuvent créer autant de profils qu'ils et
elles le souhaitent en plus du profil par défaut,
@file{~/.guix-profile}. Par exemple, @code{guix package -p ~/dev/python-dev
-i python} installe Python dans un profil accessible depuis le lien
symbolique @code{~/dev/python-dev}. Pour vous assurer que ce profil est
protégé contre le ramasse-miettes, c.-à-d.@: que Python ne sera pas supprimé
de @file{/gnu/store} alors que le profil existe, @emph{les répertoires
personnels devraient également être montés sur le nœud principal} pour que
@code{guix-daemon} connaisse ces profils non standards et évite de supprimer
les logiciels auxquels ils se réfèrent.

Vous devriez régulièrement supprimer les paquets inutilisés de
@file{/gnu/store} en exécutant @command{guix gc} (@pxref{Invoquer guix gc,,,
guix.fr, le manuel de référence de GNU Guix}). Vous pouvez le faire en
ajoutant une entrée à la crontab du nœud principal :

@example
root@@master# crontab -e
@end example

@noindent
… avec quelque chose comme cela :

@example
# Tous les jours à 5h, lancer le ramasse-miettes pour s'assurer
# d'avoir au moins 10 Gio libres sur /gnu/store.
0 5 * * 1  /usr/local/bin/guix gc -F10G
@end example

Nous en avons terminé avec le nœud principal ! Voyons maintenant les nœuds
de calcul.

@node Mettre en place des nœuds de calcul
@section Mettre en place des nœuds de calcul

Tout d'abord, les nœuds de calcul doivent monter les répertoires NFS
exportés par le nœud principal. Vous pouvez le faire en ajoutant les lignes
suivantes à @uref{https://linux.die.net/man/5/fstab,@file{/etc/fstab}} :

@example
@var{head-node}:/gnu/store    /gnu/store    nfs  defaults,_netdev,vers=3 0 0
@var{head-node}:/var/guix     /var/guix     nfs  defaults,_netdev,vers=3 0 0
@var{head-node}:/var/log/guix /var/log/guix nfs  defaults,_netdev,vers=3 0 0
@end example

@noindent
… où @var{head-node} est le nom ou l'IP de votre nœud principal. À partir de
là, en supposant que les points de montage existent, vous devriez pouvoir
monter tous ces répertoires sur chaque nœud de calcul.

Ensuite, nous devons fournir une commande @command{guix} par défaut que les
utilisateur·ices peuvent exécuter lors de leur première connexion à la
grappe (ils et elles finiront par invoquer @command{guix pull}, qui leur
fournira leur propre commande @command{guix}). Comme l'a fait le script
d'installation sur le nœud principal, nous le stockons dans
@file{/usr/local/bin} :

@example
mkdir -p /usr/local/bin
ln -s /var/guix/profiles/per-user/root/current-guix/bin/guix \
      /usr/local/bin/guix
@end example

Nous devons ensuite dire à @code{guix} de parler au démon qui s'exécute sur
notre nœud principal, en ajoutant ces lignes à @code{/etc/profile} :

@example
GUIX_DAEMON_SOCKET="guix://@var{head-node}"
export GUIX_DAEMON_SOCKET
@end example

Pour éviter des avertissements et vous assurer que @code{guix} utilise le
bon environnement linguistique, nous devons lui dire d'utiliser les données
linguistiques fournies par Guix (@pxref{Réglages applicatifs,,, guix.fr, le
manuel de référence de GNU Guix Reference Manual}) :

@example
GUIX_LOCPATH=/var/guix/profiles/per-user/root/guix-profile/lib/locale
export GUIX_LOCPATH

# Nous devons utiliser un nom de paramètre linguistique valide. Essayez « ls $GUIX_LOCPATH/* »
# pour voir les noms utilisables.
LC_ALL=fr_FR.utf8
export LC_ALL
@end example

Pour votre confort, @code{guix package} génère automatiquement
@file{~/.guix-profile/etc/profile}, qui définit toutes les variables
d'environnement nécessaires pour utiliser les paquets : @code{PATH},
@code{C_INCLUDE_PATH}, @code{PYTHONPATH}, etc. De même, @command{guix pull}
le fait pour @file{~/.config/guix/current}. Ainsi, il vaut mieux sourcer les
deux à partir de @code{/etc/profile} :

@example
for GUIX_PROFILE in "$HOME/.config/guix/current" "$HOME/.guix-profile"
do
  if [ -f "$GUIX_PROFILE/etc/profile" ]; then
    . "$GUIX_PROFILE/etc/profile"
  fi
done
@end example

Enfin, Guix propose la complétion des commandes notamment pour Bash et
zsh. Dans @code{/etc/bashrc}, pensez à ajouter cette ligne :

@verbatim
. /var/guix/profiles/per-user/root/current-guix/etc/bash_completion.d/guix
@end verbatim

Et voilà !

Vous pouvez vérifier que tout est en place en vous connectant à un nœud de
calcul et en exécutant :

@example
guix install hello
@end example

Le démon sur le nœud principal devrait télécharger les binaires
préconstruits pour vous et les déballer dans @file{/gnu/store} et
@command{guix install} devrait créer un @file{~/.guix-profile} contenant la
commande @file{~/.guix-profile/bin/hello}.

@node Accès réseau de la grappe
@section Accès réseau

Guix a besoin d'un accès réseau pour télécharger le code source et les
binaires préconstruits. La bonne nouvelle, c'est que seul le nœud principal
en a besoin car les nœuds de calcul lui délèguent cette tâche.

Les nœuds d'une grappe ont traditionnellement accès au mieux à une liste
blanche d'hôtes. Notre nœud principal a besoin au minimum que
@code{ci.guix.gnu.org} soit dans cette liste car c'est là qu'il récupère les
binaires préconstruits par défaut, pour tous les paquets dans Guix lui-même.

Au passage, @code{ci.guix.gnu.org} sert également de @emph{miroir addressé
par le contenu} du code source de ces paquets. En conséquence, il suffit
d'avoir @emph{uniquement} @code{ci.guix.gnu.org} dans cette liste blanche.

Les paquets logiciels maintenus dans un dépôt séparé comme ceux des divers
@uref{https://hpc.guix.info/channels, canaux HPC} ne sont bien sur pas
disponibles sur @code{ci.guix.gnu.org}. Pour ces paquets, vous devriez
étendre la liste blanche pour que les sources et les binaires préconstruits
(en supposant que des serveurs tiers fournissent des binaires pour ces
paquets) puissent être téléchargés. En dernier recours, les utilisateur·ices
peuvent toujours télécharger les sources sur leur machine de travail et les
ajouter au @file{/gnu/store} de la grappe, de cette manière :

@verbatim
GUIX_DAEMON_SOCKET=ssh://compute-node.example.org \
  guix download http://starpu.gforge.inria.fr/files/starpu-1.2.3/starpu-1.2.3.tar.gz
@end verbatim

La commande ci-dessus télécharge @code{starpu-1.2.3.tar.gz} @emph{et}
l'envoie à l'instance @code{guix-daemon} du nœud principal par SSH.

Les grappes sans connexion nécessitent plus de travail. Pour le moment,
notre suggestion est de télécharger tout le code source nécessaire sur une
station de travail qui exécute Guix. Par exemple, avec l'option
@option{--sources} de @command{guix build} (@pxref{Invoquer guix build,,,
guix.fr, le manuel de référence de GNU Guix}), l'exemple ci-dessous
télécharge tout le code source dont dépend le paquet @code{openmpi} :

@example
$ guix build --sources=transitive openmpi

@dots{}

/gnu/store/xc17sm60fb8nxadc4qy0c7rqph499z8s-openmpi-1.10.7.tar.bz2
/gnu/store/s67jx92lpipy2nfj5cz818xv430n4b7w-gcc-5.4.0.tar.xz
/gnu/store/npw9qh8a46lrxiwh9xwk0wpi3jlzmjnh-gmp-6.0.0a.tar.xz
/gnu/store/hcz0f4wkdbsvsdky3c0vdvcawhdkyldb-mpfr-3.1.5.tar.xz
/gnu/store/y9akh452n3p4w2v631nj0injx7y0d68x-mpc-1.0.3.tar.gz
/gnu/store/6g5c35q8avfnzs3v14dzl54cmrvddjm2-glibc-2.25.tar.xz
/gnu/store/p9k48dk3dvvk7gads7fk30xc2pxsd66z-hwloc-1.11.8.tar.bz2
/gnu/store/cry9lqidwfrfmgl0x389cs3syr15p13q-gcc-5.4.0.tar.xz
/gnu/store/7ak0v3rzpqm2c5q1mp3v7cj0rxz0qakf-libfabric-1.4.1.tar.bz2
/gnu/store/vh8syjrsilnbfcf582qhmvpg1v3rampf-rdma-core-14.tar.gz
…
@end example

(Si vous vous posez la question, c'est plus de 320 Mio de code source
compressé)

Nous pouvons ensuite créer une grosse archive contenant tout cela
(@pxref{Invoquer guix archive,,, guix.fr, le manuel de référence de GNU
Guix}) :

@verbatim
$ guix archive --export \
    `guix build --sources=transitive openmpi` \
    > openmpi-source-code.nar
@end verbatim

@dots{} et nous pouvons enfin transférer cette archive vers la grappe avec
une clé usb et la déballer :

@verbatim
$ guix archive --import < openmpi-source-code.nar
@end verbatim

Ce processus doit être répété chaque fois qu'un nouveau code source doit
être apporté à la grappe.

Au moment d'écrire ces lignes, les instituts de recherche qui participent à
Guix-HPC n'ont pas de grappe déconnectées. Si vous avez de l'expérience avec
ce genre de configuration, nous aimerions entendre vos retours et vos
suggestions.

@node Utilisation de disque de la grappe
@section Utilisation du disque

@cindex utilisation du disque, sur une grappe de calcul
Une inquiétude courante des administrateur·ices systèmes est de savoir si
cela va prendre beaucoup de place. Si cela doit avoir lieu, les jeux de
données scientifiques prendront plus probablement toute la place, plutôt que
les logiciels compilés. C'est notre expérience après presque dix ans
d'utilisation de Guix sur des grappes HPC. Néanmoins, il vaut mieux jeter un
œil à la manière dont Guix contribue à l'utilisation du disque.

Tout d'abord, avoir plusieurs versions ou variantes d'un paquet donné dans
@file{/gnu/store} ne coûte pas forcément très cher, car
@command{guix-daemon} implémente la déduplication des fichiers identiques et
les variantes de paquets ont tendance à avoir de nombreux fichiers en
commun.

Comme nous l'avons mentionné plus haut, nous recommandons d'utiliser une
tâche cron pour exécuter @code{guix gc} régulièrement, pour supprimer les
logiciels @emph{inutilisés} de @file{/gnu/store}. Cependant, il est toujours
possible que les utilisateur·ices gardent de nombreux logiciels dans leurs
profils, qui sont « vivants » et ne peuvent pas être supprimés du point de
vu de @command{guix gc}.

La solution à ce problème est de demander aux utilisateur·ices de
régulièrement supprimer les anciennes générations de leurs profils. Par
exemple, la commande suivante supprime les générations de plus de deux mois
:

@example
guix package --delete-generations=2m
@end example

De même, il vaut mieux inviter les utilisateur·ices à régulièrement mettre à
jour leur profil, ce qui peut réduire le nombre de variantes d'un logiciel
donnés stockés dans @file{/gnu/store} :

@example
guix pull
guix upgrade
@end example

En dernier recours, il est toujours possibles pour les administrateur·ices
systèmes de le faire pour leurs utilisateur·ices. Néanmoins, l'une des
forces de Guix est la liberté et le contrôle que ses utilisateur·ices ont
sur leur environnement logiciel, donc nous vous recommandons fortement de
les laisser aux manettes.

@node Considérations de sécurité de la grappe
@section Considérations de sécurité

@cindex sécurité, sur une grappe
Sur un grappe PC, guix est généralement utilisé pour gérer des logiciels
scientifiques. Les logiciels critiques pour la sécurité comme le noyau du
système d'exploitation et les services systèmes comme @code{sshd} et
l'ordonnanceur par lot restent sous votre contrôle.

Le projet Guix a un bon historique de délivrance de mises à jour de sécurité
à temps (@pxref{Mises à jour de sécurité,,, guix.fr, le manuel de référence
de GNU Guix}). Pour récupérer les mises à jour de sécurité, les
utilisateur·ices doivent exécuter @code{guix pull && guix upgrade}.

Comme Guix identifie de manière unique les variantes des logiciels, il est
facile de voir si un logiciel vulnérable est utilisé. Par exemple, pour
vérifier si la variante glibc@@2.25 sans correctif d'atténuation de «
@uref{https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt,Stack
Clash} », on peut vérifier si les profils des utilisateur·ices s'y réfèrent
ou non :

@example
guix gc --referrers /gnu/store/…-glibc-2.25
@end example

Cela rapportera si des profils qui se réfèrent à cette variante spécifique
de glibc existent.

@c *********************************************************************
@node Gestion du système Guix
@chapter Gestion du système Guix
@cindex gestion du système
@cindex adminsys

Comme Guix ne gère pas les paquets, la configuration système et les services
de la même manière que les autres distributions (plus « classiques »),
certaines tâches se déroule un peu différemment de ce à quoi on est habitué
et doivent être légèrement ajustées. Ce chapitre a pour but de vous aider en
ce sens.

@menu
* Mise à jour de Postgres pour Cuirass::  Mise à jour des paramètres par 
                                            défaut de postgres.
@end menu

@node Mise à jour de Postgres pour Cuirass
@section Mise à jour de Postgres pour Cuirass

Avec l'obsolescence de la valeur par défaut du paquet postgres dans
postgresql-configuration (voir b93434e656eba4260df82158a96c295000d3ff44),
les mises à jour du système ont besoin d'une action manuelle avant de
pouvoir être exécutées. Voici un guide pratique pour cela.

Remarquez que c'est une manière simple pour les petits jeux de données. Pour
des bases plus grandes
@url{https://www.postgresql.org/docs/current/pgupgrade.html,
@code{pg_upgrade}} peut être plus adapté. La gestion du service et de la
mise à jour du système décrites dans ce guide s'appliquent cependant
toujours.

@enumerate
@item
Arrêtez et désactivez cuirass.

Évitez que le service ne démarre et échoue après une reconfiguration :

    @code{sudo herd stop cuirass && sudo herd disable cuirass}

@item
Récupérez le contenu de la base de données.

    @code{sudo su - postgres -s /bin/sh -c pg_dumpall > /tmp/pg.dump}

@item
Ajoutez ou modifiez le service postgres.

En fonction de si votre service postgres est défini implicitement (à travers
la dépendance au service cuirass) ou s'il a sa propre entrée dans la
propriété @code{(services)} de votre système d'exploitation, vous devrez
soit ajouter, soit modifier, la configuration existante pour refléter la
mise à jour vers la version souhaitée.

Faites attention à ne pas mettre à jour directement vers postgres-16. Le
service cuirass n'aime pas ça. J'ai du trouver et purger les fichiers puis
réinitialiser le tout après une mise à jour échouée vers postgres 16.

@lisp
(service postgresql-service-type
         (postgresql-configuration
          (postgresql (@@ (gnu packages databases) postgresql-15))))
@end lisp

Remarque : si pour quelque raison que ce soit, vous n'avez pas lu le texte
ici mais avez suivi aveuglément les exemple et @emph{avez mis à jour vers la
version 16}, voici comment réinitialiser l'état :

@enumerate
@item
Supprimez les fichiers de l'instance de base de données.

Par défaut ils se trouvent dans @file{/var/lib/postgres/data}.

@item
Réinitialisez postgres.

@code{sudo su - postgres -s /bin/sh -c 'pg_ctl init -D /var/lib/postgres/data'}
@end enumerate

@item
Reconfigurez votre système.

@code{sudo guix system reconfigure chemin/vers/votre/nouveau/config.scm}

@item
Restaurez le contenu de la base de données.

@code{sudo su - postgres -s /bin/sh -c 'psql -d postgres -f /tmp/pg.dump'}

@item
Activez et démarrez le service.

@example
sudo herd enable cuirass
sudo herd start cuirass
@end example

@end enumerate


@c *********************************************************************
@node Remerciements
@chapter Remerciements

Guix se base sur le @uref{https://nixos.org/nix/ gestionnaire de paquets
Nix} conçu et implémenté par Eelco Dolstra, avec des contributions d'autres
personnes (voir le fichier @file{nix/AUTHORS} dans Guix).  Nix a inventé la
gestion de paquet fonctionnelle et promu des fonctionnalités sans précédents
comme les mises à jour de paquets transactionnelles et les retours en
arrière, les profils par utilisateurs et les processus de constructions
transparents pour les références.  Sans ce travail, Guix n'existerait pas.

Les distributions logicielles basées sur Nix, Nixpkgs et NixOS, ont aussi
été une inspiration pour Guix.

GNU@tie{}Guix lui-même est un travail collectif avec des contributions d'un
grand nombre de personnes.  Voyez le fichier @file{AUTHORS} dans Guix pour
plus d'information sur ces personnes de qualité.  Le fichier @file{THANKS}
liste les personnes qui ont aidé en rapportant des bogues, en prenant soin
de l'infrastructure, en fournissant des images et des thèmes, en faisant des
suggestions et bien plus. Merci !

Ce document contient des sections adaptées d'articles précédemment publiés
sur le blog de Guix sur @uref{https://guix.gnu.org/blog} et le blog de
Guix-HPC sur @uref{https://hpc.guix.info/blog}.


@c *********************************************************************
@node La licence GNU Free Documentation
@appendix La licence GNU Free Documentation
@cindex licence, GNU Free Documentation License
@include fdl-1.3.texi

@c *********************************************************************
@node Index des concepts
@unnumbered Index des concepts
@printindex cp

@bye

@c Local Variables:
@c ispell-local-dictionary: "american";
@c End:
