Guix time travelling tricks

Screenshot showing a web page from the Guix Data Service that has a time-line of different versions of Weechat

Versions of Weechat in Guix

Guix's time-travel is a unique and clever capability which we can use to go to any revision (point in time) and use all the Guix packages that were available at that moment. Last time we explored how to use it to pin the versions in our build environments. In this post we'll show how to use guix time-machine to move back and forwards in time to:

  1. Install earlier version of a package
  2. Install newer versions of packages
  3. Test Guix branches
  4. Use packages from other channels

Earlier versions of applications

The most common use of Guix's time-travel is installing a previous version of an application. We covered the basics of this in the previous Guix time-machine post - this time we'll investigate how to run different versions of the same program at the same time.

To explore the previous versions of a package in Guix we use the Guix Data Service. Click the link labelled master on this page to see all the revisions that have been processed: a revision means that the Guix build system has processed a change from a commit and compiled all the packages that are impacted. On this page we click Latest processed revision and from there View packages. The View packages link lets us search for any package and see all the revisions that have been in Guix.

Select to search Synopsis and the Search query is for Weechat: when the query comes back select it as a package. This page shows us the current version of the package and whether there's any build process associated with it. We're interested in the Version history link. The version history page (also see the screenshot), shows the entire history of all the different versions of this package in Guix. The current version, as I write this, is 4.0.4. We'll go back to version 3.0 which was released in 2020.

We start by creating a channels file from our current channels:

$ guix describe --format=channels > weechat-3.0-guix-channel.scm

Edit the file to remove any channels other than Guix. For the Guix channel we put in the commit we want - the versions page tells us that commit 6eea92e3 was the last commit where Weechat 3.0 was in the repository so we'll use this commit. The updated channels file looks like this:

(list
      (channel
        (name 'guix)
        (url "https://git.savannah.gnu.org/git/guix.git")
        (branch "master")
        (commit "6eea92e38bfe5e74eecbff4bb8885e5a64163eb5")
        (introduction
          (make-channel-introduction
            "9edb3f66fd807b096b48283debdcddccfea34bad"
            (openpgp-fingerprint
              "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))

To run it we do:

$ guix shell --container --nesting --network openssl nss-certs coreutils

First we create a clean shell environment so that there's no chance any mistake will pollute our standard profile. Now we're ready to run guix time-machine:

[env]$ guix time-machine --channels=weechat-3.0-guix-channel.scm -- build weechat@3.0
[env]$ guix time-machine --channels=weechat-3.0-guix-channel.scm -- package --install weechat@3.0
[env]$ /home/steve/.guix-profile/bin/weechat --version
3.0

To run multiple versions we can put them into different profiles: the easiest way to do this is to handle it through guix shell which will keep each version fully separated.

Lets also run version 3.8, by creating a new channels file and using an appropriate commit (e.g. 94ac93042f09b4ba68b7b64ed1feeebd3dab1ea4). I've named it weechat-3.8-guix-channel.scm and it looks like this:

(list
      (channel
        (name 'guix)
        (url "https://git.savannah.gnu.org/git/guix.git")
        (branch "master")
        (commit "94ac93042f09b4ba68b7b64ed1feeebd3dab1ea4")
        (introduction
          (make-channel-introduction
            "9edb3f66fd807b096b48283debdcddccfea34bad"
            (openpgp-fingerprint
              "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))

Then we do:

# create the clean shell environment
$ guix shell --container --nesting --network openssl nss-certs coreutils

# build and install the package
[env]$ guix time-machine --channels=weechat-3.8-guix-channel.scm -- build weechat@3.8
[env]$ guix time-machine --channels=weechat-3.5-guix-channel.scm -- package --install weechat@3.8
[env]$ /home/steve/.guix-profile/bin/weechat --version
3.8

Nice, we can go back in time to run different versions of the same application in parallel!

Abridge to time-machine

Aside from the pun, we can abridge our use of guix time-machine to just one command:

$ guix time-machine --channels=weechat-3.8-guix-channel.scm -- \
  shell --container weechat@3.8 --  \
  weechat --version
3.8

We're telling guix time-machine to run guix shell and to install Weechat version 3.8 as part of starting the shell; the last part of the shell command is to execute the command in the environment, in this case telling it to start the Weechat we've installed. This is nice short way of running everything in one command, or putting into a simple script file.

🏆 TIP: If you want to actually run Weechat within a shell like this add --preserve=^TERM$ as it needs a working terminal type.

However, now we come to the first caveat when using guix time-machine which is that it can only run the Guix commands that were available at that point in time. For example, guix shell was added on the 1st October 2021. If we try and run guix shell when the time-machine is before that time then it will fail since the command didn't exist then.

Lets try and run our single command version to install and run Weechat 3.0:

$ guix time-machine --channels=weechat-3.0-guix-channel.scm -- shell --container --nesting weechat@3.0 -- \
  weechat --version

guix: shell: command not found

There you go, it turns out that Star Trek was wrong, we cannot change the time-line!

There's a couple of ways around this. The easiest is to run the slightly longer version that we showed earlier where we create a shell environment using current-day Guix's capabilities and then just use time-machine to install into that shell environment. Alternatively, we can use guix environment which is the precursor to guix shell: guix environment --ad-hoc weechat@3.0

Time travel bombs

As we've seen we have to pay attention to the fact that before October 2021 guix shell didn't exist, and the --nesting command isn't available before the 6th April 2023.

The other caveat is that there are often time bombs in the sources that prevent rebuilding versions completely bit-for-bit the same. An example of this is building a package that has an embedded SSL certificate which has expired. There are workarounds, but this is an area of active development - if you're curious to learn more the Rescience 10 year challenge is a great start point [1].

Screenshot with the log of commits to the rust-team branch

Spotifyd on the Rust-team branch

Newer version of applications

As this is time-travel, we can also travel to the future! Running applications that aren't yet generally available in Guix.

Guix's development model is that the master branch receives updates of packages as Guix operates a rolling release. To keep master stable teams work on branches and package newer versions there first (these are regularly merged into the master branch). We can use Guix's time-machine capabilities to install packages from these branches - this is useful for installing newer versions that are not generally available yet. It's also a great way of testing out new packages and contributing to Guix by confirming that packages build and run correctly.

Running this example depends on the most recent merges from different teams in Guix - so the situation will be different when you explore this yourself!

For me, the rust-team branch has newer packages than those on master, so we're going to install Spotifyd - coincidently (hah!) a package I created! Browse the Guix repository and find the rust-team branch, the most recent commit on this branch is bbec79fd.

We create a channels file and add the branch and the commit, so it looks like this:

(list
      (channel
        (name 'guix)
        (url "https://git.savannah.gnu.org/git/guix.git")
        (branch "rust-team")
        (commit "bbec79fd55ba8efe4cb015bd07e4f40fb7d252d1")
        (introduction
          (make-channel-introduction
            "9edb3f66fd807b096b48283debdcddccfea34bad"
            (openpgp-fingerprint
              "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))

We start a shell environment and can install any package from this branch:

$ guix time-machine --channels=rust-team-guix-channel.scm -- \
  shell --container --network --expose=/dev/snd --expose=/run/user/"$(id -u)"/pulse \
  --share=$HOME/.config/pulse  --preserve='XDG_RUNTIME_DIR' spotifyd@0.3.5 -- \
  spotifyd --no-daemon --backend "pulseaudio" --verbose

The first part of the command runs guix time-machine using the new channels file which I've named rust-team-guix-channel.scm. The second part of the guix time-machine command is to set-up a shell, as we know it's important that this is a --container so that it only uses the channels file that we've provided. We give the container access to the network so that we can play our music and we tell it to install Spotifyd as part of creating the environment. The rest of the container command (e.g. --expose=/dev/snd) are parts required so that PulseAudio will work correctly. The guix shell command itself has the ability to run a command, so the third part of this command is to launch Spotifyd with PulsAaudio enabled.

Remember that the first time it runs this command will take a while as it has to build Guix, and there are unlikely to be binary substitutes for new packages that are on the team branches so installing packages will mean they'll have to be built locally.

Trying out a channel

We can extend this idea to using time-machine to access other channels.

We'll try out the Guix 'R us channel which is part of the Whereiseveryone community. Information about the channel is on the main page and we can view the repositories logs to get the branch (master) and the latest commit (for me it is 36b9f066).

We use those details to create a channels file. Notice that we have to have the guix channel in the configuration, but we're also adding the second channel:

(list
  (channel
    (name 'guixrus)
    (url "https://git.sr.ht/~whereiseveryone/guixrus")
    (branch "master")
    (commit
      "36b9f066d9a1df9b02d574ee5647c46b1a9b1194")
    (introduction
      (make-channel-introduction
        "7c67c3a9f299517bfc4ce8235628657898dd26b2"
        (openpgp-fingerprint
          "CD2D 5EAA A98C CB37 DA91  D6B0 5F58 1664 7F8B E551"))))
  (channel
    (name 'guix)
    (url "https://git.savannah.gnu.org/git/guix.git")
    (branch "master")
    (commit
      "3f83dc5587573f173b1f61864c9b510f05de84b1")
    (introduction
      (make-channel-introduction
        "9edb3f66fd807b096b48283debdcddccfea34bad"
        (openpgp-fingerprint
            "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))

There's a bunch of packages that aren't available through Guix - an interesting one is a writing application called jrnl. We can install it into our default profile with

$ guix time-machine --channels=guix-r-us.scm -- package install jrnl

$ jrnl today: Worked on finishing up learning Guix's time-machine command!

It will take a few moments to build the version of the environment, and to build the application. Then the jrnl command is available in our standard shell environment. Alternatively, we can install it into a temporary container as we've done above. I couldn't get guix time-machine -- shell --container to work properly (it would only show the guix channel), so that approach doesn't seem to be an option.

Technically, we're not using guix time-machine to time-travel, as we're using the most recent commit that's available - instead the value that time-machine provides is the ability to simply manage the channels file.

Time to move on

There we go, Guix really does give us a time-machine that can travel forwards or backwards in time. We've used it to install old versions of packages, newer versions that aren't yet generally available, work on team branches and even explore other channels. And, through the course of the last few posts we've used it to pin the version of Guix that we're using, to create reproducible and repeatable development environments.

It's time to bring our exploration of guix time-machine to an end. Even though it's barely been two shakes of a lambs tail, but as time is the most valuable thing we can spend - lets bring this post to an end! So, wish I'd worked in a Time Cops reference though!


[1]Ten Years Reproducibility Challenge, Reproducible research articles, from source code to PDF and Redoing one paper from Rescience C back on 2020

Posted in Tech Tuesday 07 November 2023
Tagged with tech ubuntu guix