mzan

my learning journal

At least until 2024-11-10, #Guix support for #bcachefs is not optimal.

I patched Guix for supporting multi-device specifications.

I mount the file-system in this way:

  (file-systems (cons*
          (file-system
             (mount-point "/")
             (device (uuid "c1b801d4-ab86-4e62-b0d2-f8ef7d424879"))
             (type "btrfs")
             (options "compress=zstd"))

          (file-system
             (mount-point "/mnt/bcachefs")
             (type "bcachefs")
             (device "/dev/sdb:/dev/sdc:/dev/sdd")
             (mount-may-fail? #t) ; TODO temporary hack, otherwise the Guix boot process can be blocked in case of errors on some device
             (options "degraded") ; accept also recoverable errors,
                                  ; otherwise the system is blocked
          )

          %base-file-systems)))
  • bcachefs on root file-system is not yet supported, so I use btrfs;
  • the boot process in Guix must read the gnu store, that it is on btrfs;
  • I created symbolic links from the root btrfs file-system to the bcachefs file-system mounted on /mnt/bcachefs, e.g. /home, /srv, /var/opt;
  • I didn't touched system directory needed at boot, like /var/lib;
  • I disabled the calling of bcachefs fsck from bcachefs-tools, because it is automatically done from the kernel bcachefs, and there can be mismatch between the versions of the two;

I'm working on a patch for improving the management of bcachefs on Guix.

StGit (i.e. Stacked Git) is a command-line application for mixing user-level patches with upstream changes of a repository. I use it for managing my private Guix repository.

Configuration

git send-email utility must be installed. It is part of git package, but not the default output but the git:send-email output. In the system configuration it is something like this:

  (packages
       (list git
             `(,git "send-email")
             stgit-2 ; Stackeg Git 
   ))

git send-email must be configured using the git guidelines.

Sending the patch

$ stg edit my-patch-name
$ stg send email --annotate my-patch-name

Informal notes can be specified after the header of the commit message, after the ---. Something like

From 2142f04036761f24a045a176098b1d0f958ce3bf Mon Sep 17 00:00:00 2001
Message-ID: <2142f04036761f24a045a176098b1d0f958ce3bf.1731110493.git.mzan@dokmelody.org>
From: Massimo Zaniboni <mzan@dokmelody.org>
Date: Fri, 8 Nov 2024 23:31:48 +0100
Subject: [PATCH] Support for bcachefs-like multi-device file-systems.

Support multi-device like "/dev/sda:/dev/sdb".

Change-Id: Iddd9c31f8c083a55e7a1fb193e7bbfb396e2def6
---
These are informal notes to send to the mailing list.

 gnu/build/file-systems.scm  | 49 ++++++++++++++++++++++++++++---------
 gnu/machine/ssh.scm         | 23 ++++++++++++++++-
 gnu/system/file-systems.scm | 15 ++++++++++++
 guix/scripts/system.scm     | 25 ++++++++++++++++++-
 4 files changed, 98 insertions(+), 14 deletions(-)

diff --git a/gnu/build/file-systems.scm b/gnu/build/file-systems.scm
index 41e1c9e..7dba7e0 100644
--- a/gnu/build/file-systems.scm
+++ b/gnu/build/file-systems.scm
@@ -9,6 +9,7 @@

Updating the patch

Assuming that the patch number assigned from Guix issue manager is 74273 and the revision is 2 (i.e. the first change, after the initial commit), the command to use for sending a new revision of my-patch-name is:

stg email send --annotate -v2 --to=74273@debbugs.gnu.org my-patch-name

HashTags

Requirements:

The VM starts in Virtual-Machine-Manager, and it is administered using guix deploy.

Overview

  • create a qcow2 system image
  • resize the partition
  • expand the file-system
  • install on virtual-machine-manager

Container definition

I created a template like this

(use-modules
  (gnu )
  (gnu packages aspell)
  (gnu packages attr)
  (gnu packages ci)
  (gnu packages cups)
  (gnu packages vim)
  (gnu packages version-control)
  (gnu packages file-systems)
  (gnu packages fontutils)
  (gnu packages image)
  (gnu packages package-management)
  (gnu packages graphviz)
  (gnu packages rust-apps)
  (gnu packages fonts)
  (gnu packages shellutils)
  (gnu packages code)
  (gnu packages patchutils)
  (gnu packages documentation)
  (gnu packages screen)
  (gnu packages admin)
  (gnu packages plan9)
  (gnu packages password-utils)
  (gnu packages disk)
  (gnu packages networking)
  (gnu packages linux)
  (gnu packages rsync)
  (gnu packages sync)
  (gnu packages compression)
  (gnu packages backup)
  (gnu packages shells)
  (gnu packages tcl)
  (gnu packages flashing-tools)
  (gnu packages toys)
  (gnu packages messaging)
  (gnu packages syndication)
  (gnu packages sqlite)
  (gnu packages virtualization)
  (gnu packages cryptsetup)
  (gnu packages samba)
  (gnu system)
  (ice-9 textual-ports)
  (gnu home)
  (gnu home services)
  (gnu home services shells)
  (gnu packages haskell-apps)
  (gnu packages haskell-xyz)
  (gnu packages commencement)
  (gnu services shepherd)
  (gnu system locale)
  (gnu packages unicode)
  (gnu packages terminals)
  (gnu packages version-control)
  (guix channels)
  (srfi srfi-1))

(use-service-modules networking ssh)

(operating-system
  (locale "en_US.utf8")
  (timezone "Europe/Rome")
  (keyboard-layout (keyboard-layout "it" "winkeys"))
  (host-name "template")

  (file-systems (cons (file-system
                        (device (file-system-label "does-not-matter"))
                        (mount-point "/")
                        (type "ext4"))
                      %base-file-systems))

  (bootloader (bootloader-configuration
               (bootloader grub-bootloader)
               (targets '("/dev/sdX"))))

  (users (cons* (user-account
                  (name "mzan")
                  (comment "Massimo Zaniboni")
                  (group "users")
                  (home-directory "/home/mzan")
                  (supplementary-groups '("wheel" "netdev" "audio" "video")))

                (user-account
                  (name "deploy")
                  (comment "Guix deploy user")
                  (group "users")
                  (home-directory "/home/deploy")
                  (supplementary-groups '("wheel" "netdev")))
                %base-user-accounts))

   (sudoers-file
      (plain-file "sudoers"
        (string-append (plain-file-content %sudoers-specification)
                           "deploy ALL = NOPASSWD: ALL
")))

  (packages
   (cons*
      vim htop git rsync
      the-silver-searcher
      ripgrep fd direnv shellcheck
      screen

      bcachefs-tools
      cryptsetup
      f2fs-tools
      cifs-utils
      wipe
      parted gpart
      attr ; extended file attributes
      rsync rclone

      nushell
      just

      util-linux

      %base-packages))

  (services
   (cons*
     (service dhcp-client-service-type)
     (service openssh-service-type)

     (modify-services %base-services
       (guix-service-type config => (guix-configuration
         (inherit config)
         (authorized-keys
           (cons* (local-file "/home/mzan/lavoro/admin/configs/files/keys/master.signing-key.pub")
                  %default-authorized-guix-keys))))))))

Creation of the qcow2 image

I run inside my custom Guix repository:

/pre-inst-env guix system image --image-type=qcow2 /home/mzan/lavoro/admin/configs/container-template.scm

At the end, it is returned the file name of the image. I imported it, into my directory containing other virtual-machines:

$ sudo cp /gnu/store/mpjmjn16l2z3vkcpalwx980p4jna4qp0-image.qcow2 /var/opt/virtual-machines-to-test/virt-manager/guix01.qcow2

$ sudo chmod +w /var/opt/virtual-machines-to-test/virt-manager/guix01.qcow2

$ sudo chown mzan:users /var/opt/virtual-machines-to-test/virt-manager/guix01.qcow2

Resize of the image

Guix creates a qcow2 file without space for additional packages and data. I will increase its logical size. The physical size will increase only on real demand.

$ qemu-img info guix01.qcow2

image: guix01.qcow2
file format: qcow2
virtual size: 2.36 GiB (2535514112 bytes)
disk size: 721 MiB
cluster_size: 65536
Format specific information:
    compat: 1.1
    compression type: zlib
    lazy refcounts: false
    refcount bits: 16
    corrupt: false
    extended l2: false
Child node '/file':
    filename: guix01.qcow2
    protocol type: file
    file length: 726 MiB (760793088 bytes)
    disk size: 721 MiB 

$ qemu-img resize guix01.qcow2 +200G
Image resized.

Initialize the VM

I import the image into Virtual-Machine-Manager.

I launch it. I enter as root and I set a password using passwd, passwd mzan, and passwd deploy, becuase initially there are no passwords.

I check the internal ip address with ip a, and I set a ssh with a private, i.e. something like

$ cd ~/.ssh
$ ssh-copy-id -i guix-deploy.pub mzan@192.168.100.155
$ ssh-copy-id -i guix-deploy.pub deploy@192.168.100.155

Expand the file-system

The qcow2 image was resized, but the partition and file-system no. I execute something like this:

$ ssh mzan@192.168.100.155

$ sudo lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
sda      8:0    0 202.4G  0 disk
├─sda1   8:1    0    40M  0 part
└─sda2   8:2    0   2.3G  0 part /gnu/store
                                 /

$ sudo cfdisk /dev/sda
# I resize the partition sda2

$ sudo resize2fs /dev/sda2

$ df -hT
Filesystem     Type      Size  Used Avail Use% Mounted on
none           devtmpfs  1.5G     0  1.5G   0% /dev
/dev/sda2      ext4      200G  2.1G  190G   2% /
tmpfs          tmpfs     1.5G     0  1.5G   0% /dev/shm

Administer using “guix deploy”

I must refine the template definition.

First I will obtain the uuid of the partitions. Inside the template VM:

$ sudo blkid 
/dev/sda2: LABEL="Guix_image" UUID="38af4c98-04f8-c549-ad36-132e38af4c98" BLOCK_SIZE="4096" TYPE="ext4"
/dev/sda1: SEC_TYPE="msdos" LABEL_FATBOOT="GNU-ESP" LABEL="GNU-ESP" UUID="CE91-D34B" BLOCK_SIZE="512" TYPE="vfat"

In my local admin directory I prepare a configuration file: according the guidelines of guix deploy format.

; deploy-local-vms.scm
 
(use-modules
  (gnu)
  (gnu packages admin)
  (gnu packages aspell)
  (gnu packages attr)
  (gnu packages ci)
  (gnu packages cups)
  (gnu packages vim)
  (gnu packages version-control)
  (gnu packages file-systems)
  (gnu packages fontutils)
  (gnu packages image)
  (gnu packages package-management)
  (gnu packages graphviz)
  (gnu packages rust-apps)
  (gnu packages fonts)
  (gnu packages shellutils)
  (gnu packages code)
  (gnu packages patchutils)
  (gnu packages documentation)
  (gnu packages screen)
  (gnu packages admin)
  (gnu packages plan9)
  (gnu packages password-utils)
  (gnu packages disk)
  (gnu packages networking)
  (gnu packages linux)
  (gnu packages rsync)
  (gnu packages sync)
  (gnu packages compression)
  (gnu packages backup)
  (gnu packages shells)
  (gnu packages tcl)
  (gnu packages flashing-tools)
  (gnu packages toys)
  (gnu packages messaging)
  (gnu packages syndication)
  (gnu packages sqlite)
  (gnu packages virtualization)
  (gnu packages cryptsetup)
  (gnu packages samba)
  (gnu system)
  (ice-9 textual-ports)
  (gnu home)
  (gnu home services)
  (gnu home services shells)
  (gnu packages haskell-apps)
  (gnu packages haskell-xyz)
  (gnu packages commencement)
  (gnu services shepherd)
  (gnu system locale)
  (gnu packages unicode)
  (gnu packages terminals)
  (gnu packages version-control)
  (guix channels)
  (srfi srfi-1)
  (gnu system)
  (ice-9 textual-ports))

(use-service-modules cups desktop networking ssh xorg)

(define %guix-server
  (operating-system
    (locale "en_US.utf8")
    (timezone "Europe/Rome")
    (keyboard-layout (keyboard-layout "it" "winkeys"))
    (host-name "guix01")

    (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets (list "/dev/sda"))))

    (file-systems (cons* (file-system
                           (mount-point "/")
                           (device (uuid "38af4c98-04f8-c549-ad36-132e38af4c98"))
                           (type "ext4"))
                         %base-file-systems))

    (users (cons*
            (user-account
               (name "mzan")
               (comment "Massimo")
               (group "users")
               (home-directory "/home/mzan")
               (supplementary-groups '("wheel" "netdev" "audio" "video")))
            (user-account
              (name "deploy")
              (comment "Guix deploy user")
              (group "users")
              (home-directory "/home/deploy")
              (supplementary-groups '("wheel" "netdev")))
            %base-user-accounts))

    (sudoers-file
      (plain-file "sudoers"
        (string-append (plain-file-content %sudoers-specification)
                           "deploy ALL = NOPASSWD: ALL
")))

    (packages
     (cons*
      vim htop git rsync
      the-silver-searcher
      ripgrep fd direnv shellcheck
      screen

      bcachefs-tools
      cryptsetup
      f2fs-tools
      cifs-utils
      wipe
      parted gpart
      attr ; extended file attributes
      rsync rclone

      nushell
      just

      util-linux
      %base-packages))

    (services
      (cons*
        (service openssh-service-type)
        (service dhcp-client-service-type)

        (modify-services %base-services
          (guix-service-type config => (guix-configuration
            (inherit config)
            (authorized-keys
              (cons* (local-file "/home/mzan/lavoro/admin/configs/files/keys/master.signing-key.pub")
                      %default-authorized-guix-keys)))))))))

(list (machine
        (operating-system %guix-server)
        (environment managed-host-environment-type)
        (configuration
          (machine-ssh-configuration
            (host-name "192.168.100.155")
            (system "x86_64-linux")
            (user "deploy")
            (identity "/home/mzan/.ssh/guix-deploy")))))

In this file I set: – the correct uuid to mount; – the device where installing grub; – the ip, user and key to use for accessing it during deploy; – the masker-key of the host, so the host can send closures to the guest store;

I install the VM, from my private repository:

$ ./pre-inst-env guix deploy /home/mzan/lavoro/admin/configs/deploy-local-vms.scm

Finally I reboot the VM.

HashTags

I'm very bad in chats: – I write too much; sometime I misread the others; – I like to speak about high-level/philosophical things where there can be contrasting point of view; – sometime I speak about things I studied, but I never used in practice, only because I like the paradigm/design decisions;

But at least, often I'm quick to recognize when I'm wrong, and being sorry.

Some lessons I learned: – if a reply seems stupid/illogical, read it 2-4 times, for being sure you really understood it, and in case ask for clarifications, i.e. read more – write less; – if the answer is complex, first give the short and direct answer, adding details and motivation later, otherwise very few will follow the discussion in the chat; – there is no problem in admitting of being wrong, if there is intellectual honesty; – a discussion is a step by step process versus an agreement and/or some middle ground where everyone knows the position of the other; – if it is not the case above, then the discussion has become a “people problem”; – if there is no intellectual honesty, it is futile to continue the debate;

HashTags

Sometime #Guile and #Guix error messages are confusing. I will collect here some of them, as case study. Don't get me wrong: Guile is wonderful and it is OSS.

Guile limitations

  • Racket uses contracts, so the error is signaled at the exact point where something of unexpected is happening, while in Guile one should travel the stack-trace for identifying the real source of the problem;
  • Typed Racket recognizes many errors at compile-time, while in Guile mainly at run-time;
  • CL has more type annotations in libraries, and (at least in SBCL) they are signaled as warnings during compilation;
  • CL run-time uses a stack, while Scheme has continuations, so sometime a proper stack-trace is missing;
  • in CL you can inspect also the parameters passed to functions, so it is easier figuring out the problem;

Guix system reconfigure

sudo guix system reconfigure ...

mnt/bcachefs/home/mzan/lavoro/admin/guix/think/config-nonguix.scm:439:10: error: device '/dev/sdb:/dev/sdc:/dev/sdd' not found: No such file or directory
error: Recipe `system-reconfigure` failed with exit code 1

The signaled problem is in this part of the configuration

          (file-system
             (mount-point "/mnt/bcachefs")
             (device "/dev/sdb:/dev/sdc:/dev/sdd")
             (type "bcachefs"))

Guix does not yet support a bcachefs-like device format. I need to extend Guix. The error message is informative enough for end-users, but it is not optimal for developers wanting to improve Guix, because there is no stack-trace informing in which point Guix is stuck.

Solution

I added a unique fixed error-code to generated errors, so the exact point of the error can be found quickly. For example

/mnt/bcachefs/home/mzan/lavoro/admin/guix/think/config-nonguix.scm:439:10: error:  #8658 device '/dev/sdb:/dev/sdc:/dev/sdd' not found: No such file or directory
error: Recipe `system-reconfigure` failed with exit code 1

In this way the error messages will be still nice for end-users (they don't see an ugly stack-trace), but useful also for developers.

Guix system reconfigure

After extending the source code, I tried to reconfigure

 sudo -E ./pre-inst-env guix system reconfigure /home/mzan/lavoro/admin/guix/think/config-nonguix.scm
--keep-going

I obtain this

Backtrace:
In guix/ui.scm:
  2293:10 19 (run-guix-command _ . _)
In ice-9/boot-9.scm:
  1752:10 18 (with-exception-handler _ _ #:unwind? _ # _)
    152:2 17 (with-fluid* _ _ _)
In guix/status.scm:
    839:4 16 (call-with-status-report _ _)
In ice-9/eval.scm:
    619:8 15 (_ #(#(#(#(#(#(#(#(#(#(#(…) …) …) …) …) …) …) …) …) …) …))
In ice-9/boot-9.scm:
  1752:10 14 (with-exception-handler _ _ #:unwind? _ # _)
In guix/store.scm:
   689:37 13 (thunk)
   1330:8 12 (call-with-build-handler #<procedure 7f7ea628ed80 at g…> …)
  2210:25 11 (run-with-store #<store-connection 256.100 7f7ea61700f0> …)
In ice-9/eval.scm:
   191:27 10 (_ #(#(#<directory (guix scripts system) 7f7ebac0…> …) …))
    619:8  9 (_ #(#(#(#<directory (guix scripts system) 7f7eb…>) …) …))
    619:8  8 (_ #(#(#(#<directory (guix scripts system) 7f7eb…>) …) …))
    619:8  7 (_ #(#(#(#<directory (guix scripts system) 7f7eb…>) …) …))
In srfi/srfi-1.scm:
    634:9  6 (for-each #<procedure 7f7ea4380ac0 at ice-9/eval.scm:3…> …)
In ice-9/eval.scm:
   298:42  5 (_ #(#(#<directory (guix scripts system) 7f7ebac0…> …) …))
    159:9  4 (_ #(#(#<directory (guix scripts system) 7f7ebac0…> …) …))
   223:20  3 (proc #(#(#<directory (guix scripts system) 7f7eb…> …) …))
In unknown file:
           2 (%resolve-variable (7 . devices) #<directory (guix scri…>)
In ice-9/boot-9.scm:
  1685:16  1 (raise-exception _ #:continuable? _)
  1685:16  0 (raise-exception _ #:continuable? _)

ice-9/boot-9.scm:1685:16: In procedure raise-exception:
error: devices: unbound variable
error: Recipe `system-reconfigure` failed with exit code 1

The file and position of the error is missing. Also the stack trace is rather incomplete and there are no useful names for functions. I had to check the devices variable name in last modified source-code. I found it, but this way of proceeding is far from optimal, and rather stressful.

Solution

Probably this needs many improvements to the Guile source code.

Guix reboot

I rebooted the computer with the new settings. There were an error in the code, and the machine was blocked:

image

In this case I have only praises for Guix: – the error message reports the exact source-code file and position; – I easily rebooted using old profiles, and fixed the bug;

gexp errors

I patched Guix, for recognizing bcachefs multi-device (e.g. “/dev/sda:/dev/sdb:/dev/sdc”).

 ./pre-inst-env guix deploy /home/mzan/lavoro/admin/guix/buildbart/deploy.scm

I obtained this confusing error message

The following 1 machine will be deployed:
  buildbart

guix deploy: deploying to buildbart...
guix deploy: warning: <machine-ssh-configuration> without a 'host-key' is deprecated
guix deploy: sending 0 store items (0 MiB) to 'buildbart'...
Backtrace:
In ice-9/boot-9.scm:
  1752:10 19 (with-exception-handler _ _ #:unwind? _ # _)
In guix/store.scm:
   689:37 18 (thunk)
   1330:8 17 (call-with-build-handler _ _)
   1330:8 16 (call-with-build-handler #<procedure 7f0503a86630 at g…> …)
In guix/scripts/deploy.scm:
   284:23 15 (_)
In guix/store.scm:
  1437:13 14 (map/accumulate-builds #<store-connection 256.100 7f05…> …)
  1412:11 13 (map/accumulate-builds #<store-connection 256.100 7f05…> …)
   1330:8 12 (call-with-build-handler #<procedure 7f0505de66c0 at g…> …)
In ice-9/boot-9.scm:
  1752:10 11 (with-exception-handler _ _ #:unwind? _ # _)
In guix/scripts/deploy.scm:
    166:6 10 (_)
In guix/store.scm:
  2210:25  9 (run-with-store #<store-connection 256.100 7f050369dd70> …)
In unknown file:
           8 (_ #<procedure 7f05038af060 at ice-9/eval.scm:330:13 ()> …)
In ice-9/eval.scm:
   191:27  7 (_ #(#(#<directory (gnu machine ssh) 7f0505887e60> #) …))
    619:8  6 (_ #(#(#(#<directory (gnu machine ssh) 7f05058…> …) …) …))
In srfi/srfi-1.scm:
   650:11  5 (for-each #<procedure 7f0511893cc0 at ice-9/eval.scm:3…> …)
In ice-9/eval.scm:
   245:16  4 (_ #(#(#<directory (gnu machine ssh) 7f0505887e60> #) #))
In srfi/srfi-1.scm:
   876:18  3 (every1 #<procedure number? (_)> _)
In ice-9/boot-9.scm:
  1685:16  2 (raise-exception _ #:continuable? _)
  1685:16  1 (raise-exception _ #:continuable? _)
  1685:16  0 (raise-exception _ #:continuable? _)

ice-9/boot-9.scm:1685:16: In procedure raise-exception:
In procedure cdr: Wrong type argument in position 1 (expecting pair): #f
mzan@think ~/lavoro/admin/guix/custom-guix-repo$

Probably it is a stupid error, and in this case a static type-checking could help, because it seems an error in the params sent to a function. But, for discovering the error in Guile, I need to test the code, figuring out where is the problem.

The error message lacks some info about the correct position on sorce-code, because it is a thunk of code (i.e. a gexp) executed on the remote machine. To be fair, sometime Guix is able to indicate the correct position in the source-code of gexp, because it can add this meta-info to gexp thunks. So I'm doing some FUD.

Analysis of Guix code

The Guix command line entry point is guix/ui/guix-main. It reads the settings passed by my command-line ./pre-inst-env guix .... If I want to launch a debugging Guile session, I need to pass these params in some way.

  • from a shell, I asked the position of guix with ./pre-inst-env which guix. In my case it is /mnt/bcachefs/srv/git/custom-guix-repo/scripts/guix
  • I loaded guix/ui.scm in Geiser, the Emacs Guile module
  • I entered in the module with ,module guix ui
  • I called the guix deploy function with (guix-main "/mnt/bcachefs/srv/git/custom-guix-repo/scripts/guix" "deploy" "/home/mzan/lavoro/admin/guix/buildbart/deploy.scm")
  • I obtain the error message ice-9/boot-9.scm:1685:16: In procedure raise-exception: In procedure port-column: Wrong type argument in position 1: #<closed: string 7efc1922e1c0>
  • I can play with the Guile debug shell, for inspecting better the source of the error
  • I'm not an expert but the CL SBCL debug session seems a lot more interactive, and I can explore better. In Guile it is a lot less fun.

Sadly the obtained error is different from the error executing on the command line, using ./pre-inst-env, and it starts to becoming frustrating

Debugging using test code

I created simplified version of the code, for isolating the error, and I called from Geiser, using

(guix-main "/mnt/bcachefs/srv/git/custom-guix-repo/scripts/guix" "deploy" "/home/mzan/lavoro/admin/guix/buildbart/deploy.scm")

So instead of an interactive process, like in CL, an iterative one: – save the code; – simplify the code; – create test code; – print some results; – isolate the error; – backport the fix to the real saved code;

This process is far from ideal.

Debugging of code to execute as root

The Guile code used in the initrd phase can call protected files, e.g. for reading the bcachefs super-block. So I cannot execute it inside a Geiser process.

I created this file, containing code to test. It must be safe code, because it will run as root:

; t.scm

(use-modules (gnu build file-systems))

(canonicalize-device-spec "/dev/sda:/dev/sda1:/dev/sdb:/dev/sdc")

and I call it with

$ ./pre-inst-env sudo -E guix repl t.scm

obtaining these debug mesages

specs: ("/dev/sda" "/dev/sda1" "/dev/sdb" "/dev/sdc")

 "/dev/sda" #f #f
 "/dev/sda1" #f #vu8(218 217 209 178 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 193 184 1 212 171 134 78 98 176 210 248 239 125 66 72 121 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 95 66 72 82 102 83 95 77 165 226 1 0 0 0 0 0 0 192 28 33 35 0 0 0 0 64 135 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 160 56 58 0 0 0 0 208 76 18 35 0 0 0 6 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 16 0 0 0 64 0 0 0 64 0 0 0 16 0 0 129 0 0 0 177 220 1 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0
[...]
 0 0 0 0 0 0 0 0 0 0 0 0 0 0)

 "/dev/sdb" #f #f
 "/dev/sdc" #f #f
 valid-specs: ()
Backtrace:
           3 (primitive-load "/mnt/bcachefs/home/mzan/lavoro/admin/c…")
In ice-9/eval.scm:
    619:8  2 (_ #(#(#(#(#<directory (gnu build file-syst…> …) …) …) …))
In ice-9/boot-9.scm:
   2007:7  1 (error _ . _)
  1685:16  0 (raise-exception _ #:continuable? _)

ice-9/boot-9.scm:1685:16: In procedure raise-exception:
failed to resolve multi-device  "/dev/sda:/dev/sda1:/dev/sdb:/dev/sdc"

This message contains also debug info I manually generated using (format #t ...) code.

HashTags

Right now I'm using hyprscroller that is a scrollable tiling of windows. Similar windows managers are PaperWM, Karousel, Niri, papersway.

In a scrollable window manager, windows are arranged in columns on an infinite strip going to the right. Every new window does not resize previous windows, but it is added in a new column of the stripe. All windows remain readable, in case their column is in the current visible area.

There is a direct relationship between spatial (i.e. the windows on the screen) and temporal locality (i.e. the task to complete), because the recently opened windows are near together. Windows can be moved, like paper on a desk.

## HashTags

The ideal #keyboard must have a programmable and open-source firmware like QMK. These firmware support advanced features like: macros; distinction between short and long press of a key; additional modifier keys for changing the behavior of other keys; and so on...

Contrary to my initial wisdom, if you have a programmable firmware, then a fancy keyboard with a lot of keys is not important, because every key can have two or more functions. In fact, I didn't programmed the far-away keys at all.

I have an Ergodox keyboard. I'm accustomed to Columnar Layout (i.e. the keys are on a regular matrix), and I cannot go back.

I like a split keyboard, because it is adaptable to the width of shoulders and arms.

I switched to a #dvorak based layout. Now I type with all fingers, in an efficient way, and I don't feel any stress. But, the main difference is not between dvorak and qwerty layout. The root of the problem is that with qwerty I accumulated bad-habits, that I were not able to remove. But switching to a new layout, I were forced to learn from scratch, but this time I started with sane habits.

The drawbacks of a potentially better layout like #dvorak (I like it very much) is that I had to customize many DoomEmacs keybindings, because some key chords and sequences does not make sense anymore.

HashTags

I create a #guix project in a directory like /home/mzan/fun-projects/snow.

This is the guix.scm file

(use-modules
  ((guix licenses) #:prefix license:)
  (guix packages)
  (guix download)
  (guix gexp)
  (guix git-download)
  (guix build-system asdf)
  (guix build-system gnu)
  (guix utils)
  (gnu packages)
  (gnu packages bash)
  (gnu packages admin)
  (gnu packages autotools)
  (gnu packages base)
  (gnu packages lisp)
  (gnu packages lisp-xyz)
  (gnu packages commencement))

(define %source-dir (dirname (current-filename)))

(package
    (name "snow-assembler")
    (version "0.1")
    (source (local-file %source-dir #:recursive? #t))
    (build-system asdf-build-system/sbcl)
    (native-inputs
     (list
        sbcl
        sbcl-slynk
        sbcl-agnostic-lizard

        sbcl-defstar
        sbcl-trivia
        sbcl-alexandria
        sbcl-trivial-types
        sbcl-cl-str
        sbcl-parse-float
        sbcl-iterate
        sbcl-let-plus
        sbcl-array-operations
        sbcl-sdl2
        sbcl-trivial-benchmark
        sbcl-random-state))
    (outputs '("out" "lib"))
    (synopsis "Generate a fractal image")
    (description
     "Generate a fractal image.")
    (home-page "")
    (license license:lgpl3+))

Note that all used #commonlisp packages are defined in the project, and that the sbcl-sdl2 package will take care to install also the external (i.e. C) library. sbcl-... packages are needed only for development inside Emacs.

This is the .envrc file to use for direnv.

eval $(guix shell --search-paths)
export GUILE_LOAD_PATH="$PWD:$GUILE_LOAD_PATH"

It will be enable with direnv allow in the shell, or with envrc-allow in Emacs.

In case of changes in the guix.scm file, it can be reloaded with direnv reload in the shell, or envrc-reload in Emacs.

This is the #commonlisp #asdf project file snow-assembler.asd

(asdf:defsystem "snow-assembler"
  :description "Draw a fractal"

  :author "mzan@dokmelody.org"
  :license  "LGPL-3.0-or-later"
  :depends-on (
     "alexandria"
     "trivial-types"
     "defstar"
     "iterate"
     "str"
     "let-plus"
     "array-operations"
     "sdl2"
     "cl-opengl"
     "cffi"
     "trivial-benchmark"
     "random-state")

  :components ((:file "snow-assembler")))

In this file, I'm reusing the packages I defined in guix.scm.

In ~/.sbclrc I instruct #asdf that there is a system (i.e. a #commonlisp project) in the directory of the project. I'm using something like this

(require :asdf)

; NOTE: all subdirectories of specified directories are searched for asdf project files
(asdf:initialize-source-registry
  `(:source-registry
     (:tree "/home/mzan/fun-projects/snow")
     (:tree "/home/mzan/communities")
     :inherit-configuration))

I launch Emacs. I open the snow-assembler.asd file. I make sure that the #guix environment is loaded executing the Emacs function envrc-reload.

I start a connection to sbcl using sly.

I open the #sbcl #commonlisp REPL, and I load the system with (asdf:load-system "snow-assembler").

Now, I'm ready to code in #commonlisp, using #emacs and #sly.

HashTags

Scope: – customize default #guix channel; – use it in the working environment/system (i.e. “eating your own dog food”); – send patches to #guix upstream; – wait patches are merged;

Preparation

I installed #guix from source code, as described here.

I'm using Stacked-Git for managing patches.

When I need to extend #guix, I search first in the contributor page if there is some open pull request to use as starting point. There can be also Guix channels in external repositories.

I create a patch, using Stacked-Git.

I test the build of packages, using the instructions in Guix manual.

Installing a system

Some packages like the window-manager must be tested on a live system. Better if it is the working environment. I use

sudo -E ./pre-inst-env guix system reconfigure /home/mzan/lavoro/admin/guix/think/config-nonguix.scm

In particular without sudo -E, it will not work correctly.

The build/reconfigure workflow

I'm using just for defining simple scripts, and nushell as scripting language.

These are the scripts in justfile for system build and upgrade:

# Import new changes.
pull:
    #!/usr/bin/env nu
    guix pull
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    stg pull
    cd ../nonguix-repo
    git pull

# Fix permissions, that can be corrupted during sudo system reconfigure
fix-permissions:
    sudo chown -R mzan:users custom-guix-repo
    sudo chown -R mzan:users /home/mzan/.cache/guix
    sudo chown -R mzan:users /home/mzan/.cache/guile

# Recompile from scratch repo files. Slow, but sometime it is required.
repo-bootstrap: fix-permissions
    sudo rm -r -f /home/mzan/.cache/guix
    sudo rm -r -f /home/mzan/.cache/guile
    cd custom-guix-repo && guix shell -D guix help2man git strace pkg-config bash --pure -- bash -c "./bootstrap && ./configure --localstatedir=/var && make clean && make"

# Update repo files. Faster than `repo-bootstrap`.
repo-make: fix-permissions
    cd custom-guix-repo && guix shell -D guix help2man git strace pkg-config bash --pure -- make

# Try to build a new version of the system, relaunching the task for 10 times in case of errors.
system-build10: fix-permissions
    #!/usr/bin/env nu
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    for $it in [1 2 3 4 5 6 7 8 9 10] {
      try {
        print "###"
        print $"### Attempt ($it)"
        print "###"
        ./pre-inst-env guix system build /home/mzan/lavoro/admin/configs/config-think.scm --keep-going

        print "###"
        print $"### Success at pass ($it)"
        print "###"

        break
      }
      print "!!!"
      print $"!!! Failure at pass ($it). Wait 60sec and retry."
      print "!!!"

      sleep 60sec
    }

# Like 'system-build10' but using 2 jobs.
system-build10x2: fix-permissions
    #!/usr/bin/env nu
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    for $it in [1 2 3 4 5 6 7 8 9 10] {
      try {
        print "###"
        print $"### Attempt ($it)"
        print "###"
        ./pre-inst-env guix system build --keep-going --max-jobs=2 /home/mzan/lavoro/admin/configs/config-think.scm
        print "###"
        print $"### Success at pass ($it)"
        print "###"

        break
      }
      print "!!!"
      print $"!!! Failure at pass ($it). Wait 60sec and retry."
      print "!!!"

      sleep 60sec
    }

# Like 'system-build10', but it works also if ci.gnu.guix.org is down.
system-build10-fallback: fix-permissions
    #!/usr/bin/env nu
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    for $it in [1 2 3 4 5 6 7 8 9 10] {
      try {
        print "###"
        print $"### Attempt ($it)"
        print "###"
        ./pre-inst-env guix system build /home/mzan/lavoro/admin/configs/config-think.scm --keep-going --no-substitutes --substitute-urls="https://git.savannah.gnu.org/git/guix.git https://bordeaux.guix.gnu.org https://gitlab.com/nonguix/nonguix"

        print "###"
        print $"### Success at pass ($it)"
        print "###"

        break
      }
      print "!!!"
      print $"!!! Failure at pass ($it). Wait 60sec and retry."
      print "!!!"

      sleep 60sec
    }

# Build the system as normal user and then install it
system-reconfigure: system-build10
    #!/usr/bin/env nu
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    sudo -E ./pre-inst-env guix system reconfigure /home/mzan/lavoro/admin/configs/config-think.scm --keep-going

# Like system-reconfigure but with fallback if CI server is down
system-reconfigure-no-ci: system-build10-fallback
    #!/usr/bin/env nu
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    sudo -E ./pre-inst-env guix system reconfigure /home/mzan/lavoro/admin/configs/config-think.scm --keep-going --no-substitutes --substitute-urls="https://git.savannah.gnu.org/git/guix.git https://bordeaux.guix.gnu.org https://gitlab.com/nonguix/nonguix"

# Create a new patch in the 'custom-guix-repo'.
new-patch name:
    #!/usr/bin/env nu
    cd custom-guix-repo
    stg branch | into string | str trim | $in == "master"
    stg pull
    stg new {{name}}
    stg series

# Update the (many) packages of the hyprland project
refresh-hyprland:
    #!/usr/bin/env sh
    cd custom-guix-repo
    ./pre-inst-env guix refresh -u \
        hyprland aquamarine hyprcursor \
        hyprlang hyprutils hyprlock hypridle hyprpaper hyprpicker \
        hyprwayland-scanner wayland-protocols-next wayland \
        xdg-desktop-portal-hyprland

Adding other channels

The additional channels defined in .config/guix/channels.scm and returned by guix describe are not loaded by ./pre-inst-env. For adding them: – clone the channel content – make sure to pull updates when you pull the main guix channel – modify pre-inst-env from something like GUILE_LOAD_PATH="$abs_top_builddir:$abs_top_srcdir${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH" e to something like GUILE_LOAD_PATH="/mnt/bcachefs/home/mzan/lavoro/admin/nonguix-repo:$abs_top_builddir:$abs_top_srcdir${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH" e – make sure to apply again this modification, if you call just repo-bootstrap

HashTags