Subject: [PATCH v2] sevices: guix: Add channels field.

From: Brice Waegeneire <[email-masked]>
Date: Tue, 21 Dec 2021 22:00:42 +0100

* doc/guix.texi (Channels): Specify that '/etc/guix/channels.scm'
  contains channels configuration.
  (Base Services): Document 'guix-configuration-channels' field.
* gnu/services/base.scm (install-channels-file): New procedure.
  (guix-configuration): Add channels field.
  (guix-activation): Use 'install-channels-file' procedure.
---
 doc/guix.texi         | 15 ++++++++++++++-
 gnu/services/base.scm | 42 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 54 insertions(+), 3 deletions(-)

I've changed the type of the new field from a list to a s-expression, I'm not
sure if it should be a G-exp instead.  The documentation of the
'channels' field as been updated as suggested.

Ludovic Courtès <[email-masked]> writes:

>> +;; FIXME Does this gexp should be build before boot, such as
>> +;; substitute-key-authorization does?
>
> There’s a grammatical issue :-), but also I’m not sure: what are you
> worried about?

This is related to your commit 8b3ad455be7e8ace35a2eaebf7fffbb611280852, where
you added pre-computation of the ACL to make « [...] the first boot slightly
faster ».  Should this be done in this case too?

>> +  (channels         guix-configuration-channels ;list of channels
>> +                    (default '()))
>
> I wonder if it should default to ‘%default-channels’, for consistency
> and least-surprise.  In practice, it means we’d always end up creating
> /etc/guix/channels.scm, but that’s probably OK.  (The downside is if we,
> Guix devs, choose to change ‘%default-channels’ at some point: users
> would be stuck with the value that got written to /etc.  That’s a very
> hypothetical situation though.)

Users would not have been stuck with a stale ‘%default-channels’, even with
the first version of this patch.  The issue with using a non null default
value, is the absence of backward compatibility.  A user with an already defined
/etc/guix/chanels.scm, would see its custom channels being replaced by the
default one after having reconfigure a system with this patch for the first
time.  So I guess I should make further adjustment to the patch

¹ <https://git.savannah.gnu.org/cgit/guix.git/commit/?id=8b3ad455be7e8ace35a2eaebf7fffbb611280852>

diff --git a/doc/guix.texi b/doc/guix.texi
index a826171f34..5284a69156 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -5001,7 +5001,7 @@ $ wget -O - \
 Guix and its package collection are updated by running @command{guix pull}
 (@pxref{Invoking guix pull}).  By default @command{guix pull} downloads and
 deploys Guix itself from the official GNU@tie{}Guix repository.  This can be
-customized by defining @dfn{channels} in the
+customized by defining @dfn{channels} in @file{/etc/guix/channels.scm} and
 @file{~/.config/guix/channels.scm} file.  A channel specifies a URL and branch
 of a Git repository to be deployed, and @command{guix pull} can be instructed
 to pull from one or more channels.  In other words, channels can be used
@@ -15557,6 +15557,19 @@ This example assumes that the file @file{./guix.example.org-key.pub}
 contains the public key that @code{guix.example.org} uses to sign
 substitutes.
 
+@item @code{channels} (default: @code{'(cons* %default-channels)})
+S-expression producing a list of channels to be used by @command{guix
+pull}, by default.  The S-exp is written to
+@file{/etc/guix/channels.scm}.
+
+@quotation Note
+When booting or reconfiguring to a system where @code{channels}
+is not null, the existing @file{/etc/guix/channels.scm} file is backed up as
+@file{/etc/guix/channels.scm.bak} if it was determined to be a manually modified
+file.  This is to facilitate migration from earlier versions, which
+allowed for in-place modifications to @file{/etc/guix/channels.scm}.
+@end quotation
+
 @item @code{max-silent-time} (default: @code{0})
 @itemx @code{timeout} (default: @code{0})
 The number of seconds of silence and the number of seconds of activity,
diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index e206bea5f0..c9823e6d55 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -58,6 +58,7 @@
   #:use-module (gnu packages terminals)
   #:use-module ((gnu build file-systems)
                 #:select (mount-flags->bit-mask))
+  #:use-module (guix channels)
   #:use-module (guix gexp)
   #:use-module (guix records)
   #:use-module (guix modules)
@@ -66,6 +67,7 @@
   #:use-module (srfi srfi-26)
   #:use-module (ice-9 match)
   #:use-module (ice-9 format)
+  #:use-module (ice-9 pretty-print)
   #:re-export (user-processes-service-type        ;backwards compatibility
                %default-substitute-urls)
   #:export (fstab-service-type
@@ -1502,6 +1504,39 @@ archive' public keys, with GUIX."
         ;; Installed the declared ACL.
         (symlink #+default-acl "/etc/guix/acl"))))
 
+;; FIXME Does this gexp should be built before boot, such as
+;; substitute-key-authorization does?
+(define (install-channels-file channels)
+  "Return a gexp with code to install a file with CHANNELS, a S-exp returning
+a list of channels."
+  (define channels-file
+    (plain-file "channels.scm"
+                (with-output-to-string
+                  (lambda _
+                    (pretty-print (map (lambda (channel)
+                                         (if (channel? channel)
+                                             (channel->code channel)
+                                             channel))
+                                       channels))))))
+
+  (with-imported-modules '((guix build utils))
+    #~(begin
+        (use-modules (guix build utils))
+
+        ;; If channels.scm already exists, move it out of the way. Create a
+        ;; backup if it's a regular file: it's likely that the user
+        ;; manually defined it.
+        (if (file-exists? "/etc/guix/channels.scm")
+            (if (and (symbolic-link? "/etc/guix/channels.scm")
+                     (store-file-name? (readlink "/etc/guix/channels.scm")))
+                (delete-file "/etc/guix/channels.scm")
+                (rename-file "/etc/guix/channels.scm"
+                             "/etc/guix/channels.scm.bak"))
+            (mkdir-p "/etc/guix"))
+
+        ;; Installed the declared channels.
+        (symlink #+channels-file "/etc/guix/channels.scm"))))
+
 (define %default-authorized-guix-keys
   ;; List of authorized substitute keys.
   (list (file-append guix "/share/guix/berlin.guix.gnu.org.pub")
@@ -1524,6 +1559,8 @@ archive' public keys, with GUIX."
                     (default #t))
   (substitute-urls  guix-configuration-substitute-urls ;list of strings
                     (default %default-substitute-urls))
+  (channels         guix-configuration-channels ;sexp
+                    (default '(cons* %default-channels)))
   (chroot-directories guix-configuration-chroot-directories ;list of file-like/strings
                       (default '()))
   (max-silent-time  guix-configuration-max-silent-time ;integer
@@ -1701,7 +1738,7 @@ proxy of 'guix-daemon'...~%")
 (define (guix-activation config)
   "Return the activation gexp for CONFIG."
   (match-record config <guix-configuration>
-    (guix authorize-key? authorized-keys)
+    (guix authorize-key? authorized-keys channels)
     #~(begin
         ;; Assume that the store has BUILD-GROUP as its group.  We could
         ;; otherwise call 'chown' here, but the problem is that on a COW overlayfs,
@@ -1714,7 +1751,8 @@ proxy of 'guix-daemon'...~%")
 
         #$(if authorize-key?
               (substitute-key-authorization authorized-keys guix)
-              #~#f))))
+              #~#f)
+        #$(install-channels-file channels))))
 
 (define* (references-file item #:optional (name "references"))
   "Return a file that contains the list of references of ITEM."
-- 
2.32.0