ci (sourcehut)

img
Remote #ci as the way to go in #modernhw digital design testing. Let’s see it in detail using sourcehut.
Sourcehut is a lightweight #gitforge where I host my #git repositories. Not only it is based on a paradigm perfectly adapted to #modernhw, but also its builds service includes support for guix (x86_64) images. This means that we will be able to execute all of our testing online inside guix profiles, shells or natively on top of the bare-bones image.

Open logic

Let’s see how in detail using the cookbook as a starting point, and taking as a complete example the fw-open-logic firmware package which comes with the electronics guix channel.

guix install fw-open-logic:out

Open logic is a useful #vhdl library of commonly used components, implemented in a reusable and vendor/tool-independent way. As any other #modernhw library, it includes tests sets for any of its components, using the vunit utility in this case.
To run the full tests suite use (user wide using the default $GUIX_PROFILE)

guix install ghdl-clang python-vunit
cd open-logic/sim
python3 run.py --ghdl -v

or (local to the project)

mkdir _deps
export GUIX_PROFILE=open-logic/_deps
guix install -P $GUIX_PROFILE ghdl-clang python-vunit
. $GUIX_PROFILE/etc/profile
cd open-logic/sim
python3 run.py --ghdl -v

go remote

img
Now, how do we proceed online using #sourcehut #ci builds facility ? Builds will pop up a new environment based on an up to date guix-system image when we push a commit to git.sr.ht, provided we include a .build.yml manifest file, or by a .build folder with up to 4 manifest files, at the root of the git project [1]. Be careful: consider that this image is built daily using a crontab job, which is a good and a bad thing at the same time. From one side, you won’t be using the same environment for your tests, which breaks #reproducibility (see comments section below). On the other side, #guix is a rolling release, and new fancy features and new fixes are added every day. Keep this in mind.
Let’s create a .builds folder in a topic test branch, with the following contents:

manifest.scm, list of dependencies in our project
guix.scm, default guix repository, redundant, included here for convenience
channels.scm, list of guix channels remote repositories, in addition to the default guix repository, from where we pull packages
We will be using here my own electronics channel (no substitutes), as well as the guix science channel (which provides substitutes).
(note how here we load the local guix.scm file, instead of making use of the %default-channels global variable)

scheme (load "guix.scm") ;;; %default-channels key.pub, auth key to access substitutes of packages in guix channels

manifests

From now on, every new push to the test #git branch will trigger the execution of the tasks defined in the three manifest files

profile1
profile2
shell1

Profile manifest files use a slightly different approach, and are given here for comparison purposes only. The shell manifest uses an isolated shell container within the image itself to illustrate this feature.
Inside the manifests, I declare the image to use, guix, and the global environment variables sourced before each task is run: prj (project name), srv (list of servers with substitutes), manifest and channels (pointing to the corresponding files) and key (same). It is important to declare a trigger action, to receive an email with all relevant information in case of failure (log, id, commit, etc.).

tasks

What’s interesting here is the list of tasks. Some of them are common to all three manifests

env, useful only for debugging
guix__update__channels, replace the default project local guix.scm file by the output of

sh guix describe --format=channels

The goal here is avoid pulling latest guix upstream, useless and cpu and time consuming, and using the local version instead. Remember that the guix system image we are using here is updated daily.

guix__auth, runs the authorize command to add the key.pub file to guix, so that we will be able to download package substitutes when necessary

sh sudo guix archive --authorize < "$key"

Here, one may opt by doing a

sh guix pull --channels="$channels"

as in profile2, to set the revision of the guix channels we are using (remember channels are nothing but git repositories).
Note how in profile1 and shell1 we opt for a different approach.
guix__update__profile, where we create a _deps folder to be used as a local $GUIX_PROFILE (defined in .envrc).
Then, one of

sh # profile1 guix time-machine --channels="$channels" -- \ package -p "$GUIX_PROFILE" \ --substitute-urls="$srv" \ -m "$manifest"

or

sh # profile2 guix \ package -p "$GUIX_PROFILE" \ --substitute-urls="$srv" \ -m "$manifest"

will install packages in $manifest into the $GUIX_PROFILE. I’m using here the time-machine mechanism to set the revision of the guix channels, depending if guix pull was run in the previous stage or not.
vunit, sets env variables in .envrc and runs python3 run.py --ghdl -v inside sim directory
Note that here, we are using ghdl-clang and python-vunit packages, provided respectively by guix-science and the electronics channel.
guix__shell__test, used by shell1, make use of time-machine (no former guix pull, then), to create a shell container, where to install project dependencies. Then, if calls inmediately run.sh to run the unitary tests

sh guix time-machine --channels="$channels" -- shell -C --substitute-urls="$srv" -m "$manifest" -- ./.builds/run.sh

Comments

You may check the logs of profile1, profile2 and shell1 manifests, including a section with logs per task, to better understand what’s going on here. Remember that #sourcehut gives ssh access to the builds by connecting to the runners in case of failures, which provides a practical way of debugging the manifest files.
You may see how, using the remove guix image, it is possible to deploy a series of tasks to test our #modernhw design as we develop it: we will get an email in case of failure to pass the tests. Here, I present three approaches: guix pulling to set the repositories revisions on use; time-machine, to achieve the same, and guix shell to create an isolated container. These three alternatives are not necessary here, of course, but are given as a simple and practical demo of what can be achieved with #guix, #sourcehut and #ci.
To conclude this long post, it is important to stress once again that the point on using #guix resides in its reproducibility capabilities. By keeping a couple of #plaintext files, namely the manifest.scm and channels.scm, one can obtain #determinism in the execution of the tests. Even if the guix image is upgraded and rebuilt daily (and so it changes), by fixing the revision of our channels (remember, guix pull or guix time-machine) we obtain always the same products out of our tests, as we run the same (project and tests) code, within exactly the same environment.


[1] It is also possible to automatically submit builds when a patch to a repo with build manifests is sent to a mailing list. This is achieved by appending the project name as a prefix to the subject of the message, for example [PATCH project-name].
#ciseries