dotfiles/Guix.org

324 lines
11 KiB
Org Mode

#+TITLE: Guix
#+PROPERTY: header-args :mkdirp yes
#+PROPERTY: header-args:bash :tangle-mode (identity #o755) :comments link :shebang "#!/usr/bin/env bash"
#+PROPERTY: header-args:scheme :comments link
[[https://guix.gnu.org/][GNU Guix]] is (1) a transactional package manager and (2) a GNU/Linux distribution.
My personal selling points are declarative package configuration and transactional upgrades.
References:
- [[https://guix.gnu.org/en/help/][Official help]]
- [[https://wiki.systemcrafters.cc/guix][System Crafters wiki]]
- [[https://gitlab.com/pjotrp/guix-notes][Pjotr Prins' Guix notes]]
- [[https://www.youtube.com/watch?v=iBaqOK75cho&list=PLEoMzSkcN8oNxnj7jm5V2ZcGc52002pQU][Davil Wilson's YouTube series]]
* Profiles
A profile is way to group Guix packages. Amongst many advantages, profiles can be defined by manifests, which in turn can be stored in VCS.
References:
- [[https://guix.gnu.org/en/cookbook/en/html_node/Guix-Profiles-in-Practice.html][Guix Profiles in Practice]]
** Activate profiles
A script to activate guix profiles. Usage:
#+begin_example
activate-profiles [profile1] [profile2] ...
#+end_example
Source: [[https://github.com/daviwil/dotfiles/blob/master/Systems.org#activating-profiles][David Wilson's config]]
#+begin_src bash :tangle ./bin/scripts/activate-profles
GREEN='\033[1;32m'
RED='\033[1;30m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
profiles=$*
if [[ $# -eq 0 ]]; then
profiles="$HOME/.config/guix/manifests/*.scm";
fi
for profile in $profiles; do
# Remove the path and file extension, if any
profileName=$(basename $profile)
profileName="${profileName%.*}"
profilePath="$GUIX_EXTRA_PROFILES/$profileName"
manifestPath=$HOME/.config/guix/manifests/$profileName.scm
if [ -f $manifestPath ]; then
echo
echo -e "${GREEN}Activating profile:" $manifestPath "${NC}"
echo
mkdir -p $profilePath
guix package --manifest=$manifestPath --profile="$profilePath/$profileName"
# Source the new profile
GUIX_PROFILE="$profilePath/$profileName"
if [ -f $GUIX_PROFILE/etc/profile ]; then
. "$GUIX_PROFILE"/etc/profile
else
echo -e "${RED}Couldn't find profile:" $GUIX_PROFILE/etc/profile "${NC}"
fi
else
echo "No profile found at path" $profilePath
fi
done
#+end_src
** Update profiles
A script to update Guix profiles. Usage:
#+begin_example
update-profiles [profile1] [profile2] ...
#+end_example
Source: once again, [[https://github.com/daviwil/dotfiles/blob/master/Systems.org#updating-profiles][David Wilson's config]].
#+begin_src bash :tangle ./bin/scripts/update-profiles
GREEN='\033[1;32m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
profiles=$*
if [[ $# -eq 0 ]]; then
profiles="$GUIX_EXTRA_PROFILES/*";
fi
for profile in $profiles; do
profileName=$(basename $profile)
profilePath=$GUIX_EXTRA_PROFILES/$profileName
echo
echo -e "${GREEN}Updating profile:" $profilePath "${NC}"
echo
guix package --profile="$profilePath/$profileName" --manifest="$HOME/.config/guix/manifests/$profileName.scm"
done
#+end_src
* Channels
Specifying additional channels.
References:
- [[https://gitlab.com/nonguix/nonguix][nonguix channel repo]]
- [[https://guix.gnu.org/manual/en/html_node/Channels.html][Guix channels reference]]
#+begin_src scheme :tangle .config/guix/channels.scm
(cons*
(channel
(name 'channel-q)
(url "file:///home/pavel/Code/channel-q"))
(channel
(name 'nonguix)
(url "https://gitlab.com/nonguix/nonguix"))
%default-channels)
#+end_src
* Systems
Configuring systems with Guix.
** Base configuration
The base configuration is shared between all the machines.
While it's possible to make a single =.scm= file with base confguration and load it, I noticed that it produces more cryptic error messages whenever there is an error in the base file, so I opt in for noweb.
=guix system= invocation is as follows:
#+begin_example
sudo -E guix system reconfigure ~/.config/guix/systems/[system].scm
#+end_example
Common modules:
#+begin_src scheme :tangle no :noweb-ref system-common
(use-modules (gnu))
(use-modules (gnu system nss))
(use-modules (gnu packages certs))
(use-modules (gnu packages version-control))
(use-modules (gnu packages vim))
(use-modules (gnu packages wm))
(use-modules (gnu packages openbox))
(use-modules (nongnu packages linux))
(use-modules (nongnu system linux-initrd))
(use-service-modules desktop networking ssh xorg)
(use-package-modules ssh)
#+end_src
In principle, we could define a variable called =base-operating-system= and extend it in ancestors. However, then we would have to define mandatory fields like =host-name=, =bootloader= with dummy values. Since I'm already using noweb, there is little point.
The following code will be inserted in the top of the =operating-system= definition.
#+begin_src scheme :tangle no :noweb-ref system-base
;; Use the full Linux kernel
(kernel linux)
(initrd microcode-initrd)
(firmware (list linux-firmware))
(locale "en_US.utf8")
(timezone "Europe/Moscow")
;; US/RU keyboard layout
(keyboard-layout (keyboard-layout "us,ru" #:options '("grp:alt_shift_toggle")))
;; User accounts
(users (cons* (user-account
(name "pavel")
(comment "Pavel")
(group "users")
(home-directory "/home/pavel")
(supplementary-groups
'("wheel" ;; sudo
"netdev" ;; network devices
"audio"
"video"
"input"
"tty"
;; "docker"
"lp")))
%base-user-accounts))
;; Base packages
(packages
(append
(list nss-certs
git
i3-gaps
openbox
vim)
%base-packages))
;; Services
(services
(append
(list (service openssh-service-type)
(set-xorg-configuration
(xorg-configuration
(keyboard-layout keyboard-layout))))
%desktop-services))
#+end_src
** blue
A VM on which I test Guix. Will probably be deleted sooner or later.
#+begin_src scheme :noweb yes :tangle ~/.config/guix/systems/blue.scm
<<system-common>>
(operating-system
<<system-base>>
(host-name "blue")
(bootloader
(bootloader-configuration
(bootloader grub-bootloader)
(target "/dev/sda")
(keyboard-layout keyboard-layout)))
(swap-devices
(list (uuid "d9ca4f8b-4bb1-420e-9371-3558731bada1")))
(file-systems
(cons* (file-system
(mount-point "/")
(device
(uuid "179fbd75-3c7f-4de2-8c4f-4c30939b8a3f"
'ext4))
(type "ext4"))
%base-file-systems)))
#+end_src
* System installation
** Preparation
In my cases the provided ISO doesn't work because of Libre kernel.
Fortunately, David Wilson has made [[https://github.com/SystemCrafters/guix-installer][a repository]] with a toolchain to make an ISO with the full kernel. In case it won't be an option, the [[https://gitlab.com/nonguix/nonguix][nonguix repo]] also has instructions on how to do that.
When an ISO is there, we have to write it on a USB stick. Run =sudo fdisk -l= to get a list of disks.
The approach in the official instruction is to create a bootable USB with =dd=:
#+begin_example
sudo dd of=/dev/sdxX if=<path-to-iso> status=progress && sync
#+end_example
However, I couldn't make it work for some strange reason. Fortunately, =gnome-disk-utility= was able to produce a bootable USB.
** Installation
Going further, the official instructions for installation & SystemCrafters wiki entry are pretty good, so it's not necessary to repeat them here.
** After installation
After the installation, the strategy is as follows.
Set a password for the main user (pavel). Login with openbox to get a tolerable interface, because i3 default config is horrible.
[[https://guix.gnu.org/en/manual/en/html_node/Keyboard-Layout-and-Networking-and-Partitioning.html#Keyboard-Layout-and-Networking-and-Partitioning][Connect to the internet]].
Clone the dotfiles repo:
#+begin_example
mkdir Code
cd Code
git clone https://github.com/SqrtMinusOne/dotfiles.git
#+end_example
Copy the channels file and run guix pull:
#+begin_example
cp ~/Code/dotfiles/.config/guix/channels.scm ~/.config/guix
guix pull
#+end_example
The first pull usually takes a while. After that install yadm and pull dotfiles:
#+begin_example
guix install yadm
guix clone https://github.com/SqrtMinusOne/dotfiles.git
#+end_example
And activate the required profiles. Again, downloading & building Emacs, Starship and stuff will take a while.
Don't forget to install =JetBrainsMono Nerd Font=.
* Notes on installing software
| Category | Guix dependency | Description |
|----------+-----------------+----------------------------------------------------|
| dev | patchelf | A program to modify existsing ELF executables |
| dev | glibc | A lot of stuff, including ELF interpeter and ~ldd~ |
** wakatime-cli
| Note | Description |
|------+-----------------------|
| TODO | Package this for Guix |
Before I figure out how to package this for Guix:
- Clone [[https://github.com/wakatime/wakatime-cli][the repo]]
- Run ~go build~
- Copy the binary to the =~/bin= folder
** ActivityWatch
| Note | Description |
|------+-----------------------|
| TODO | Package this for Guix |
The official binaries work just fine after some patching, except for the =aw-qt= binary.
Properly building from source is more awkward (PyInstaller? Are you serious? xD), as there are multiple packages with lots of dependencies.
The patching is as follows:
- Get ELF interpeter patch from ~guix build glibc~, after which patch ELF interpeter path for the required binaries, e.g.:
#+begin_src bash eval :no
patchelf --set-interpreter /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 aw-qt
patchelf --set-interpreter /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 aw-server/aw-server
patchelf --set-interpreter /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 aw-server-rust/aw-server-rust
patchelf --set-interpreter /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 aw-watcher-afk/aw-watcher-afk
patchelf --set-interpreter /gnu/store/fa6wj5bxkj5ll1d7292a70knmyl7a0cr-glibc-2.31/lib/ld-linux-x86-64.so.2 aw-watcher-window/aw-watcher-window
#+end_src
Add libz to RPATH
| Category | Guix dependency |
|----------+-----------------|
| misc | zlib |
#+begin_src bash eval :no
patchelf --set-rpath /gnu/store/rykm237xkmq7rl1p0nwass01p090p88x-zlib-1.2.11/lib/ aw-qt
patchelf --set-rpath /gnu/store/rykm237xkmq7rl1p0nwass01p090p88x-zlib-1.2.11/lib/ aw-server/aw-server
patchelf --set-rpath /gnu/store/rykm237xkmq7rl1p0nwass01p090p88x-zlib-1.2.11/lib/ aw-server-rust/aw-server-rust
patchelf --set-rpath /gnu/store/rykm237xkmq7rl1p0nwass01p090p88x-zlib-1.2.11/lib/ aw-watcher-afk/aw-watcher-afk
patchelf --set-rpath /gnu/store/rykm237xkmq7rl1p0nwass01p090p88x-zlib-1.2.11/lib/ aw-watcher-window/aw-watcher-window
#+end_src
As aw-qt doesn't work properly, and the only thing it does is makes a tray icon anyhow, here is a script to launch the required components:
#+begin_src bash :tangle ./bin/aw-start
~/Programs/activitywatch/aw-server/aw-server &
~/Programs/activitywatch/aw-watcher-afk/aw-watcher-afk &
~/Programs/activitywatch/aw-watcher-window/aw-watcher-window &
#+end_src