unofficial mirror of help-guix@gnu.org 
 help / color / mirror / Atom feed
From: Tomas Volf <~@wolfsden.cz>
To: Ian Eure <ian@retrospec.tv>
Cc: help-guix@gnu.org
Subject: Re: Modular home configuration
Date: Sat, 09 Nov 2024 20:09:12 +0100	[thread overview]
Message-ID: <87o72o6x7r.fsf@wolfsden.cz> (raw)
In-Reply-To: <87y11s1ekj.fsf@meson> (Ian Eure's message of "Sat, 09 Nov 2024 09:35:10 -0800")

[-- Attachment #1: Type: text/plain, Size: 8365 bytes --]


Hello,

Ian Eure <ian@retrospec.tv> writes:

> Hi folks,
>
> I’m trying to make my home configuration more modular, so I can better support
> system variances.  For example, I have a laptop I use interactively, and a
> headless machine that runs Cuirass.  It’s advantageous to share certain aspects
> of the home configuration between the two machines (shell prompt/environment,
> GPG agent, etc), but not others (anything X11/graphical stuff shouldn’t be on
> the build machine).  One approach to this is to define packages and services and
> reference them in the home configuration.  What I dislike about this is that
> many things require both packages and services, and I’d prefer to have a way to
> completely encapsulate that -- for example, the mpd-mpc package to control my
> music server, plus a home-environment-variables-service-type to set MPD_HOST.
>
> I attempted to solve this by writing a procedure:
>
>    (define (+mpd-client home-config)
>      (home-environment
>        (inherit home-config)
>        (packages (cons mpd-mpc (home-environment-packages         home-config)))
>        (services
>          (cons
>            (simple-service
>              'mpd-environment-service
>              home-environment-variables-service-type
>              '(("MPD_HOST" . "audio.box")))
>            (home-environment-services home-config)))))
>
> Which I can then wrap around a home-environment to add the mpd-mpc package and
> environment variable it needs to work:
>
>    (+mpc-client (home-environment ...))
>
> Surprisingly, this doesn’t work -- it complains that there’s more than one
> "home" service type.  I’m not sure why that is, and I haven’t been able to see
> anything obviously wrong in the REPL -- though I haven’t been able to get my
> actual home configuration up in the Emacs-Guix REPL, due to #67290.

It is the same as with operating-system.  While you are setting the
(services) field, the accessor is (home-environment-user-services).

(home-environment-services home) returns (at least as far I understand
it) something like (append (home-environment-user-services home)
(home-environment-essential-services home)).

Hence the "more that one home services type", you get it twice, once via
the (services) you set in +mpc-client (because it includes
essential-services), and once by the essential-services directly.

>
> Does anyone have a suggestion for a workaround for this issue, explanation of
> how two home services are ending up in the config,

See above.

> or a better approach for building modular home configs?

Not sure if better, but different.  I am using two variations of the
same approach.  One is to define a new service type that takes care of
adding all necessary bits into the home environment.  The following for
example defines home-keychain-service-type for starting keychain[0].

--8<---------------cut here---------------start------------->8---
(define-configuration/no-serialization home-keychain-configuration
  (keychain-package
   (package keychain)
   "Package to use keychain from.")
  (ssh-package
   (package openssh)
   "SSH package to add into the profile.")
  (gpg-package
   (package gnupg)
   "GPG package to add into the profile.")
  (ssh-keys
   (list-of-strings '())
   "Ensure the ssh-agent is started and register the listed keys into it.")
  (gpg-keys
   (list-of-strings '())
   "Ensure the gpg-agent is started and register the listed keys into it."))

(define (q s)
  "Quote string into a form suitable for shell."
  (string-append "'"
                 (string-replace-substring s "'" "'\\'")
                 "'"))

(define (home-keychain-configuration->file config)
  (let* ((ssh-keys (home-keychain-configuration-ssh-keys config))
         (ssh? (pair? ssh-keys))
         (gpg-keys (home-keychain-configuration-gpg-keys config))
         (gpg? (pair? gpg-keys))
         (agents (string-join (append (if ssh? '("ssh") '())
                                      (if gpg? '("gpg") '()))
                              ","))
         (keys (string-append (string-join (map q ssh-keys) " " 'suffix)
                              (string-join (map q gpg-keys) " " 'suffix))))
    (mixed-text-file "keychain-init"
                     "eval $("
                     keychain "/bin/keychain"
                     " --agents " agents
                     " --eval "
                     " --quiet "
                     " -Q "
                     keys
                     ")")))

(define (home-keychain-bash config)
  (home-bash-extension
   (bash-profile (list (home-keychain-configuration->file config)))))

(define (home-keychain-profile config)
  (list (home-keychain-configuration-ssh-package config)
        (home-keychain-configuration-gpg-package config)))

(define home-keychain-service-type
  (service-type
   (name 'home-keychain)
   (extensions (list (service-extension home-bash-service-type
                                        home-keychain-bash)
                     (service-extension home-profile-service-type
                                        home-keychain-profile)))
   (description "Start a keychain on login.")))
--8<---------------cut here---------------end--------------->8---

The interesting piece here is the `home-profile-service-type', which
allows a service to add additional packages into the home environment
(same as you would do with (package) field).

However the limitation is that some services do not support extensions,
so for those case I just have procedure or list (depending on whether
configuration is required) with the required services, and use (append).
Following is an example from my configuration:

--8<---------------cut here---------------start------------->8---
(define* (home-desktop-services #:key (mpv-config %default-mpv-config))
  (list (simple-service 'pkgs-desktop home-profile-service-type
                        %desktop-packages)
        (simple-service 'home-files home-files-service-type
                        `((".xinitrc" ,file/xinitrc)
                          (".Xresources" ,file/Xresources)))
        (simple-service
         'im-env-vars home-environment-variables-service-type
         '(("GTK_IM_MODULE" . "ibus")
           ("QT_IM_MODULE" . "ibus")
           ("XMODIFIERS" . "@im=ibus")
           ("GTK2_RC_FILES" . "$HOME/.config/gtk-2.0/gtkrc")
           ;; TODO: Are these still required?  If yes, try to get rid of them.
           ("GUIX_GTK2_IM_MODULE_FILE"
            . "$HOME/.guix-home/profile/lib/gtk-2.0/2.10.0/immodules-gtk2.cache")
           ("GUIX_GTK3_IM_MODULE_FILE"
            . "$HOME/.guix-home/profile/lib/gtk-3.0/3.0.0/immodules-gtk3.cache")))

        (simple-service 'desktop-xdg-config-files
                        home-xdg-configuration-files-service-type
                        `(("i3/config" ,file/i3/config)
                          ("gtk-2.0/gtkrc" ,file/gtk-2/gtkrc)
                          ("gtk-3.0/settings.ini" ,file/gtk-3/settings.ini)
                          ("gtk-3.0/gtk.css" ,file/gtk-3/gtk.css)
                          ("mpv/mpv.conf" ,(mpv-config->file mpv-config))))

        (service home-startx-command-service-type)

        (service home-dbus-service-type)))
--8<---------------cut here---------------end--------------->8---

And then my in my home environment:

--8<---------------cut here---------------start------------->8---
  (let ((home %basic-home))
    (home-environment
     (inherit home)

     (packages ...)
     (services
      (append
       (list ...)
       (home-desktop-services)
       (home-environment-user-services home)))))
--8<---------------cut here---------------end--------------->8---



I am not convinced this approach is the best, originally I had it the
same way you had in a functional style with a helper procedure, but
rewrote it to this model since Guix already has a concept of "services",
so I wanted to try to mirror it.

Maybe I will switch back to the functional style.  Not sure yet.


Hope this helps,
Tomas

0: https://www.funtoo.org/Funtoo:Keychain

-- 
There are only two hard things in Computer Science:
cache invalidation, naming things and off-by-one errors.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 853 bytes --]

  reply	other threads:[~2024-11-09 19:10 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-09 17:35 Modular home configuration Ian Eure
2024-11-09 19:09 ` Tomas Volf [this message]
2024-11-09 19:30 ` Edouard Klein
2024-11-09 19:30 ` Edouard Klein

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://guix.gnu.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87o72o6x7r.fsf@wolfsden.cz \
    --to=~@wolfsden.cz \
    --cc=help-guix@gnu.org \
    --cc=ian@retrospec.tv \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).