[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Modular home configuration
From: |
Tomas Volf |
Subject: |
Re: Modular home configuration |
Date: |
Sat, 09 Nov 2024 20:09:12 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) |
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.
signature.asc
Description: PGP signature