23 April 2024 |
I was ricing my guix, as one does naturally, and I stumbled upon 733T hacks leading to a flash of inspiration. What follows is a blow-by-blow account of this adventure.
Hold on tight!
First, I found out that there is a home-bash-service-type in the guix manual. It occurred to me that I could rid of zsh, coz bash purism. So, I configured it like so:
(service home-bash-service-type (home-bash-configuration (guix-defaults? #t) (aliases '(("feed" . "nohup mpv --playlist=~/playlist.m3u &") ("gparted" . "sudo -EH gparted") ("rm" . "rm -i") ("cp" . "cp -i") ("mv" . "mv -i") (".." . "cd ..") ("du" . "du -kh") ("df" . "df -kTh") )) (bashrc (list (local-file "./.bashrc" "bashrc") (mixed-text-file "blesh-init" "[[ $- == *i* ]] && source " blesh "/share/blesh/ble.sh --noattach ") ;;; blesh with init config file ;; (mixed-text-file "blesh-init" ;; "[[ $- == *i* ]] && source " ;; blesh ;; "/share/blesh/ble.sh --noattach --rcfile /home/kaizer/.config/blesh/init.sh ;; ") (mixed-text-file "liquidprompt" "[[ $- = *i* ]] && source " liquidprompt "/share/liquidprompt/liquidprompt ") (mixed-text-file "powerline-theme" "source " liquidprompt "/share/liquidprompt/themes/powerline/powerline.theme ") (mixed-text-file "blesh-exit" "[[ ${BLE_VERSION-} ]] && ble-attach")))))
There are a few things to note:
~/.bashrc as well as the defaults.
This argument here: provides the rest of the ~/.bashrc:
(local-file "./.bashrc" "bashrc")
/gnu/store/<hash>/<pkg>.~/.bashrc then we will run into 2 problems:
(mixed-text-file "blesh-init" "[[ $- == *i* ]] && source " blesh "/share/blesh/ble.sh --noattach ") ;; blesh with init config file ;; (mixed-text-file "blesh-init" ;; "[[ $- == *i* ]] && source " ;; blesh ;; "/share/blesh/ble.sh --noattach --rcfile /home/kaizer/.config/blesh/init.sh ;; ") (mixed-text-file "liquidprompt" "[[ $- = *i* ]] && source " liquidprompt "/share/liquidprompt/liquidprompt ") (mixed-text-file "powerline-theme" "source " liquidprompt "/share/liquidprompt/themes/powerline/powerline.theme ") (mixed-text-file "blesh-exit" "[[ ${BLE_VERSION-} ]] && ble-attach")
Guix home also provides a home-xdg-configuration-files-service-type such that you can provide files to be stored in $XDG_CONFIG_HOME.
Why would you want to do this? It seems pretty basic.
There are 2 main reasons:
So, simply, the configuration is as follows:
(service home-xdg-configuration-files-service-type `(("alacritty/alacritty.toml" ,(local-file "./alacritty.toml")) ("tmux/tmux.conf" ,(local-file "./tmux.conf")) ("beets/config.yaml" ,(local-file "./beets-config.yaml")) ("mako/config" ,(local-file "./mako-config")) ("waybar/config" ,(local-file "./waybar-config")) ("waybar/style.css" ,(local-file "./waybar-style.css")) ("sway/config" ,(local-file "./sway-greetd.conf")) ;; ("blesh/init.sh" ,(local-file "./blesh-init.sh")) ;; ("liquidpromptrc" ,(local-file "./liquidpromptrc") ("pantalaimon/pantalaimon.conf" ,(local-file "./pantalaimon.conf"))))
There is a guix system service that allows you to reload your home configuration such that you do not have to every time you make a change to the system.
We can use services to further define specific system behavior either by running certain commands on startup, daemonizing (wrapping them up in shepherd) them or through mcron.
For example, if we want a certain command to run on the system-level on a schedule, we can use the mcron-service-type to create a simple-service that we will provide to our os-level config like so:
(simple-service 'my-cron-jobs mcron-service-type (list ;; garbage-collector-job ; FIXME: messes with guix home so constant reinstalls ;; updatedb-job idutils-job))
We can then define the services like so:
;; superceded by file-database-service-type ;; (define updatedb-job ;; ;; Run 'updatedb' at 3AM every day. Here we write the ;; ;; job's action as a Scheme procedure. ;; #~(job '(next-hour '(3)) ;; (lambda () ;; (system* (string-append #$findutils "/bin/updatedb") ;; "--prunepaths=/tmp /var/tmp /gnu/store")) ;; "updatedb")) (define garbage-collector-job ;; Collect garbage 90 minutes after midnight every day. ;; The job's action is a shell command. #~(job "30 0 * * 1" ;Vixie cron syntax "guix gc -F 1G")) (define idutils-job ;; Update the index database as user "kaizer" at 12:15PM ;; and 19:15PM. This runs from the user's home directory. #~(job '(next-minute-from (next-hour '(12 19)) '(15)) (string-append #$idutils "/bin/mkid src") #:user "kaizer"))
This uses the job command to create this service which you can read about here. Suffice to say what is most important is grokking the Vixie cron syntax.
On a user level, we have two options; either we use shepherd or mcron.
If our command does not need to be scheduled then shepherd is the best option because it will allow us to start, stop, enable and disable the service on demand.
A simple shepherd configuration, located at $XDG_CONFIG_HOME/shepherd/init.scm, can look like this:
;; Shepherd User Services (load "/home/kaizer/.config/shepherd/services.scm") (register-services emacs ;; wallpaper ; NOTE: launched with mcron atm mcron ;; mpd ;; gpg-agent ;; jackd ;; ibus-daemon ;; workrave ) ;; Send shepherd into the background. (action 'shepherd 'daemonize) ;; Services to start when shepherd starts: (for-each start '(emacs ;; wallpaper ; NOTE: launched with mcron atm mcron ;; mpd ;; gpg-agent ;; ibus-daemon ;; workrave ))
The accompanying services file:
(define emacs (make <service> #:provides '(emacs) ;; #:requires '() #:start (make-system-constructor "emacs --daemon") #:stop (make-system-destructor "emacsclient --eval \"(kill-emacs)\""))) (define mpd (make <service> #:provides '(mpd) #:respawn? #t #:start (make-forkexec-constructor '("mpd" "--no-daemon")) #:stop (make-kill-destructor))) (define wallpaper (make <service> #:provides '(wallpaper) #:respawn? #t #:start (make-forkexec-constructor '("random-sway-wallpaper.sh >/dev/null 2>&1")) #:stop (make-kill-destructor))) (define mcron (service '(mcron) ;; Run /usr/bin/mcron without any command-line arguments. #:start (make-forkexec-constructor '("mcron")) #:stop (make-kill-destructor) #:respawn? #t))
Finally, we can specify the jobs to be run by mcron like so:
(job '(next-minute-from (current-time) (range 0 60 15)) "random-sway-wallpaper.sh &> /dev/null")
The file is located in $XDG_CONFIG_HOME/cron/<name>.guile, where <name> is the name of the job. One job per file.
We have seen how to use Guix to manage .config files as well as how to specify services to run certain programs either on startup or on timed loops with mcron.
The beauty is that we used scheme for everything and in a deterministic manner.
All we need to do is check the config into version control and boom, we are done. No more ricing from scratch.
Respect the Guix.
I figured I might as well share the random-sway-wallpaper.sh script. Here you go:
#!/bin/sh # I am running sway so I set the bg with swaybg there for the startup bg # We get that PID here so we can kill it and launch swaybg again PID=$(pidof swaybg) kill "$PID" # we launch swaybg with a random .jpg wallpaper from our collection swaybg -i "$(find "$HOME"/Pictures/Wallpapers/*.jpg -type f | shuf -n1)" -m fill &
Another bonus, say we wanted to get dad jokes every hour or so, we can create a job like so in $XDG_CONFIG_HOME/cron/jokes.guile:
(job '(next-hour-from (current-time) (range 0 24 1)) "jokes.sh")
And the script could be:
#!/bin/sh # we use the api from icanhazdadjoke.com to get a random dad joke on demand! joke=$(curl -H "Accept: text/plain" https://icanhazdadjoke.com/) # send it as a notification notify-send "$joke"
Did you hear about the bread factory burning down? They say the business is toast.
Did you hear about the submarine industry? It really took a dive…