Othacehehttps://othacehe.org/feed.xmlRecent Posts2024-03-15T09:10:41ZWSL images for Guix Systemhttps://othacehe.org/wsl-images-for-guix-system.htmlMathieu Othaceheothacehe@gnu.org2022-09-26T10:00:00Z<p>During my holidays some rare conditions were met: I had some free time and
access to a Windows machine. Time to review this
<a href="https://issues.guix.gnu.org/53912">patch</a> from Alex Griffin adding WSL
support to the <code>guix system image</code> command.</p><p>I'm not exactly sure how WSL works, but it looks like some kind of emulation
layer allowing to run GNU/Linux distributions on Windows. To be frank, I hope
that I won't have to use this WSL thing in the future. However, some less
fortunate people are forced to work in a Windows environment so being able to
run GNU Guix could be a lifeline.</p><h2>Merge the patch</h2><p>Let's have a short look to the patches proposed by Alex to better understand
what adding WSL support means.</p><p>First stop is to add <code>tarball</code> support to <code>guix system image</code>.</p><p><img src="/static/tarball.png" alt="tarball" /></p><p>It means that if you were to run:</p><p><code>guix system image --image-type=tarball my-image.scm</code></p><p>you would obtain a tarball archive containing the Guix System image declared
in <code>my-image.scm</code>. Handy and pretty straightforward.</p><p>Then, Alex added some plumbing to declare that a WSL image is basically a
tarball image. My only concern here is that generating <code>tar.gz</code> archives
compressed at level 9 takes quite some times. I wonder if Windows would accept
<code>zstd</code> archives or so. This question is left open for early adopters.</p><p><img src="/static/wsl.png" alt="wsl" /></p><p>The last required patch, that I tweaked a little bit adds a <code>(gnu system images wsl2)</code> module. In this module, the <code>wsl-os</code> variable is declared.</p><pre><code><span class="syntax-open">(</span><span class="syntax-special">define-public</span> <span class="syntax-symbol">wsl-os</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host-name</span> <span class="syntax-string">"gnu"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">timezone</span> <span class="syntax-string">"Etc/UTC"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader</span> <span class="syntax-symbol">dummy-bootloader</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">kernel</span> <span class="syntax-symbol">dummy-kernel</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">initrd</span> <span class="syntax-symbol">dummy-initrd</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">initrd-modules</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">firmware</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">file-systems</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">...</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>This operating-system is meant to be the foundation for any future WSL
image. It is a bit special because several fields are set to dummy
packages. That's because the WSL environment already provides the Linux kernel
and possibly the initrd, mounts the file-systems and so on.</p><p>The strategy of defining a <code>dummy-kernel</code>, <code>dummy-initrd</code> and
<code>dummy-bootloader</code> for this specific use-case works but is not perfectly
satisfactory to me. Maybe we should not define those dummy packages directly
in <code>(gnu system images wsl2)</code> but rather in <code>(gnu system)</code> or so.</p><p>Another point worth noticing is how Alex managed to start the Shepherd, the
init system used by Guix System. WSL is taking care of starting the Linux
kernel but it cannot know how to start the Shepherd because that's the role of
the Guix System initrd.</p><p>To work around that, a <code>wsl-boot-program</code> script is defined. This script takes
care of starting the Shepherd and spawning a shell. It is registered as the
login program of the root account to trick WSL into running it.</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">users</span> <span class="syntax-open">(</span><span class="syntax-symbol">cons*</span> <span class="syntax-open">(</span><span class="syntax-symbol">user-account</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-string">"guest"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">group</span> <span class="syntax-string">"users"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">supplementary-groups</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"wheel"</span><span class="syntax-close">)</span><span class="syntax-close">)</span> <span class="syntax-comment">; allow use of sudo
</span> <span class="syntax-open">(</span><span class="syntax-symbol">password</span> <span class="syntax-string">""</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">comment</span> <span class="syntax-string">"Guest of GNU"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">user-account</span>
<span class="syntax-open">(</span><span class="syntax-symbol">inherit</span> <span class="syntax-symbol">%root-account</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">shell</span> <span class="syntax-open">(</span><span class="syntax-symbol">wsl-boot-program</span> <span class="syntax-string">"guest"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%base-user-accounts</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>OK, so this is a good start and all those patches are now merged.</p><h2>Generate images</h2><p>Now let's generate some images. We can first try to generate a basic image
that does nothing more than instantiating the <code>wsl-os</code> operating-system and
produce a WSL bootable image out of it. From a Guix checkout, we can run:</p><pre><code>guix system image gnu/system/images/wsl2.scm
/gnu/store/cgbd1yl5ab12pn7a103pixld868lcslf-image.tar.gz</code></pre><p>that's it! The resulting image can be imported in Windows using the following commands:</p><pre><code>wsl --import Guix ./guix cgbd1yl5ab12pn7a103pixld868lcslf-image.tar.gz
wsl -d Guix</code></pre><p>From within the WSL environment we can stop holding our breath and enjoy a
classic Guix System distribution.</p><p><img src="/static/pull.png" alt="pull" /></p><p>That's nice but we can go a step further. Let's say we want to deploy a WSL
image which role is to run a particular service, say <code>tailon</code> a web server for
browsing some system logs. We can define our operating-system this way</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">use-modules</span> <span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">system</span> <span class="syntax-symbol">images</span> <span class="syntax-symbol">wsl2</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">services</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">inherit</span> <span class="syntax-symbol">wsl-os</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">services</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cons</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">tailon-service-type</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system-user-services</span> <span class="syntax-symbol">wsl-os</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>run,</p><pre><code>guix system image --image-type=wsl2 wsl-tailon.scm</code></pre><p>import the image, like the first one, and from a Windows browser we can
directly use the <code>tailon</code> service.</p><p><img src="/static/tailon.png" alt="tailon" /></p><p>We can also choose a more complex service: the Nginx web server.</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">use-modules</span> <span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">system</span> <span class="syntax-symbol">images</span> <span class="syntax-symbol">wsl2</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">packages</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">services</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">srfi</span> <span class="syntax-symbol">srfi-1</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">inherit</span> <span class="syntax-symbol">wsl-os</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">services</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cons*</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">dummy-service-type</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">simple-service</span>
<span class="syntax-symbol">'index</span>
<span class="syntax-symbol">activation-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">with-imported-modules</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-open">(</span><span class="syntax-symbol">guix</span> <span class="syntax-symbol">build</span> <span class="syntax-symbol">utils</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">#~</span><span class="syntax-open">(</span><span class="syntax-special">begin</span>
<span class="syntax-open">(</span><span class="syntax-symbol">use-modules</span> <span class="syntax-open">(</span><span class="syntax-symbol">guix</span> <span class="syntax-symbol">build</span> <span class="syntax-symbol">utils</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">mkdir-p</span> <span class="syntax-string">"/srv/http/www.example.com"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">copy-file</span>
<span class="syntax-symbol">#$</span><span class="syntax-open">(</span><span class="syntax-symbol">file-append</span> <span class="syntax-symbol">nginx</span> <span class="syntax-string">"/share/nginx/html/index.html"</span><span class="syntax-close">)</span>
<span class="syntax-string">"/srv/http/www.example.com/index.html"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">nginx-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">nginx-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">server-blocks</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-open">(</span><span class="syntax-symbol">nginx-server-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">server-name</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"www.example.com"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">listen</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"8080"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">root</span> <span class="syntax-string">"/srv/http/www.example.com"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system-user-services</span> <span class="syntax-symbol">wsl-os</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>This way, <code>nginx</code> will serve the <code>index.html</code> file at the
<code>http://localhost:8080</code> location. Sadly this won't work because the <code>nginx</code>
Shepherd service requires the <code>loopback</code> service. If we add the <code>loopback</code>
service however, we will get a run time issue because WSL already took care of
creating this interface and Shepherd will fail to start it and refuse to start
<code>nginx</code> which depends of the <code>loopback</code> service.</p><p>As a quick hack, I defined a <code>dummy-service</code> which is conveniently called
<code>loopback</code> and does nothing. The operating-system declaration now looks like:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">use-modules</span> <span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">system</span> <span class="syntax-symbol">images</span> <span class="syntax-symbol">wsl2</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">guix</span> <span class="syntax-symbol">gexp</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">packages</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">services</span> <span class="syntax-symbol">shepherd</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">gnu</span> <span class="syntax-symbol">services</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">srfi</span> <span class="syntax-symbol">srfi-1</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-special">define</span> <span class="syntax-open">(</span><span class="syntax-symbol">dummy-service</span> <span class="syntax-symbol">_</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span>
<span class="syntax-open">(</span><span class="syntax-symbol">shepherd-service</span>
<span class="syntax-open">(</span><span class="syntax-symbol">documentation</span> <span class="syntax-string">""</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">provision</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-symbol">loopback</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">start</span> <span class="syntax-symbol">#~</span><span class="syntax-open">(</span><span class="syntax-symbol">const</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">stop</span> <span class="syntax-symbol">#~</span><span class="syntax-open">(</span><span class="syntax-symbol">const</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">respawn?</span> <span class="syntax-symbol">#f</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-special">define</span> <span class="syntax-symbol">dummy-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-symbol">'loopback</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">extensions</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-open">(</span><span class="syntax-symbol">service-extension</span> <span class="syntax-symbol">shepherd-root-service-type</span>
<span class="syntax-symbol">dummy-service</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">default-value</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">description</span> <span class="syntax-string">""</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">inherit</span> <span class="syntax-symbol">wsl-os</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">services</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cons*</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">dummy-service-type</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">simple-service</span>
<span class="syntax-symbol">'index</span>
<span class="syntax-symbol">activation-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">with-imported-modules</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-open">(</span><span class="syntax-symbol">guix</span> <span class="syntax-symbol">build</span> <span class="syntax-symbol">utils</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">#~</span><span class="syntax-open">(</span><span class="syntax-special">begin</span>
<span class="syntax-open">(</span><span class="syntax-symbol">use-modules</span> <span class="syntax-open">(</span><span class="syntax-symbol">guix</span> <span class="syntax-symbol">build</span> <span class="syntax-symbol">utils</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">mkdir-p</span> <span class="syntax-string">"/srv/http/www.example.com"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">copy-file</span>
<span class="syntax-symbol">#$</span><span class="syntax-open">(</span><span class="syntax-symbol">file-append</span> <span class="syntax-symbol">nginx</span> <span class="syntax-string">"/share/nginx/html/index.html"</span><span class="syntax-close">)</span>
<span class="syntax-string">"/srv/http/www.example.com/index.html"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">nginx-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">nginx-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">server-blocks</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-open">(</span><span class="syntax-symbol">nginx-server-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">server-name</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"www.example.com"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">listen</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"8080"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">root</span> <span class="syntax-string">"/srv/http/www.example.com"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system-user-services</span> <span class="syntax-symbol">wsl-os</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>And just like before, we can generate an image, import it, and open a Windows
web browser to check that is works correctly.</p><p><img src="/static/nginx.png" alt="nginx" /></p><p>All in all that's a pretty good start that still needs to be polished a bit by
future users. It is also a good demonstration that generating new kind of
system images is quite easy with the Guix System image API. Thank you Alex
for working on that topic!</p>Distributing Guix System Pinebook Pro imageshttps://othacehe.org/distributing-guix-system-pinebook-pro-images.htmlMathieu Othaceheothacehe@gnu.org2021-08-29T10:00:00Z<p>A few months ago, Enrico Schwass very generously offered to ship me a Pinebook
Pro so that I can port Guix System on it. I finally took the time to take it
out of the drawer, where it was quietly expecting a travel to the land of
parenthesis.</p><p>There were previous efforts in the past to add support for that
machine. Janneke managed to install Guix System from Debian and documented it
in a nice blog
<a href="https://joyofsource.com/guix-system-on-the-pinebook-pro.html">post</a>. Vagrant
Cascadian built up on that work so that the default Guix System ARM64 Linux
kernel can support the Pinebook Pro, as summarized
<a href="https://lists.gnu.org/archive/html/guix-devel/2021-05/msg00032.html">here</a>.</p><p>On my side, I worked on improving the Guix System image generation mechanism,
notably under the impulsion of the
<a href="https://guix.gnu.org/en/blog/2020/childhurds-and-substitutes/">Hurd</a> work
driven by Janneke. You can have a look to the blog
<a href="https://othacehe.org/the-guix-system-image-api.html">post</a> I wrote at the
time.</p><p>That new image API allowed to define a Pinebook Pro image type and a barebones
Pinebook Pro image in the Guix
<a href="https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/system/images/pinebook-pro.scm">sources</a>.</p><p>This means that one can run:</p><pre><code>guix system image gnu/system/images/pinebook-pro.scm
...
successfully built /gnu/store/m82b13hq40fv45895hfsd7hak4cch51z-pinebook-pro-barebones-raw-image.drv
/gnu/store/0gq2qqx5npmx94hdj1l059b7kf1cz68g-pinebook-pro-barebones-raw-image</code></pre><p>to produce a raw barebones Guix System image targeting the Pinebook Pro. The
only work left is to copy this image on a micro SD-card, this way:</p><pre><code>dd if=/gnu/store/0gq2qqx5npmx94hdj1l059b7kf1cz68g-pinebook-pro-barebones-raw-image of=/dev/mmcblk0 bs=4M</code></pre><p>When the micro SD-card is inserted in the Pinebook Pro, it will automatically
boot on it and display the long-awaited login shell.</p><p><img src="/static/pinebook-pro.jpg" alt="Guix System on the Pinebook Pro" /></p><p>If you do not wish to build the Pinebook Pro image yourself, you can also
download it from the Guix <a href="https://guix.gnu.org/en/download/latest/">website</a>
in the <strong>Download</strong> section. This image is built daily by the
<a href="https://guix.gnu.org/cuirass/">Cuirass</a> continuous integration system.</p><p><img src="/static/pinebook-download.png" alt="Pinebook Pro image download" /></p><h2>Future steps</h2><p>The next logical step is to provide a Guix System Pinebook Pro image with some
lightweight desktop environment such as Xfce.</p><p>While technically this should be as simple as adding the following line in the
pinebook-pro.scm operating-system declaration:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">services</span> <span class="syntax-open">(</span><span class="syntax-symbol">cons</span> <span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">xfce-desktop-service-type</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%base-services</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>it is a little bit more challenging in practice.</p><p>By default, the Pinebook Pro image is cross-compiled to the ARM64
architecture. Guix does not support cross-compilation for all the build
systems yet. On the bright side, Maxime Devos recently added cross-compilation
support for the
<a href="https://lists.gnu.org/archive/html/guix-patches/2021-07/msg00923.html">meson</a>
build system, and is currently working on the
<a href="https://lists.gnu.org/archive/html/guix-patches/2021-08/msg01074.html">glib-or-gtk</a>
build system.</p><p>Another possibility would be to use the QEMU transparent emulation
<a href="https://guix.gnu.org/manual/en/html_node/Virtualization-Services.html">mechanism</a>
to build the image by emulating an ARM64 architecture on an Intel x86_64
machine. The problem with that approach is that compilation is terribly slow
and sometimes fails due to emulation limits. This is nonetheless the approach
that I will probably take in the short term.</p><p>Thanks again to Enrico for his support, keep tuned!</p>Building your own channelshttps://othacehe.org/building-your-own-channels.htmlMathieu Othaceheothacehe@gnu.org2021-04-02T10:00:00Z<p>Like many GNU Guix users, I'm using multiple Guix
<a href="https://guix.gnu.org/manual/en/html_node/Channels.html">channels</a>.</p><p>Let say I'm using this <code>~/.config/guix/channels.scm</code> file:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">cons*</span> <span class="syntax-open">(</span><span class="syntax-symbol">channel</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-symbol">'flat</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">url</span> <span class="syntax-string">"https://github.com/flatwhatson/guix-channel.git"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">introduction</span>
<span class="syntax-open">(</span><span class="syntax-symbol">make-channel-introduction</span>
<span class="syntax-string">"33f86a4b48205c0dc19d7c036c85393f0766f806"</span>
<span class="syntax-open">(</span><span class="syntax-symbol">openpgp-fingerprint</span>
<span class="syntax-string">"736A C00E 1254 378B A982 7AF6 9DBE 8265 81B6 4490"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%default-channels</span><span class="syntax-close">)</span></code></pre><p>The official GNU Guix <a href="https://ci.guix.gnu.org">build farm</a> is only building
the default Guix channel. It means that the packages provided by the <code>flat</code>
channel won't have any available substitutes.</p><p>On my local network, I have a few laptops without much processing power, and a
powerful desktop. The ideal setup would be to have the powerful desktop build
all the packages of the <code>flat</code> channel, each time there is a new commit either
in the default <code>guix</code> channel or in the <code>flat</code> channel.</p><p>Then, the substitutes built by the desktop machine would be made available on
the local network.</p><p>The good news is that using the new release of
<a href="https://guix.gnu.org/en/blog/2021/cuirass-10-released/">Cuirass</a>, the GNU
Guix continuous integration software, as well as the recent substitute server
discovery <a href="https://othacehe.org/substitute-server-discovery.html">mechanism</a>,
this should be relatively easy.</p><p>Let's see how it can be done, step by step.</p><h2>The desktop machine configuration</h2><p>We need to setup two specific services on the desktop machine:</p><ul><li><p>A Cuirass service to build the <code>flat</code> channel.</p></li><li><p>A Guix publish service to share the package substitutes.</p></li></ul><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">cuirass-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cuirass-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">specifications</span> <span class="syntax-symbol">#~'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host</span> <span class="syntax-string">"0.0.0.0"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">guix-publish-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">guix-publish-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host</span> <span class="syntax-string">"0.0.0.0"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">advertise?</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>Here we pass an empty specifications list to the Cuirass service, as we will
use the Web interface to configure it later on.</p><p>We also configure the Guix publish service to advertise itself on the local
network using Avahi.</p><h2>The flat specification creation</h2><p>It's time to configure Cuirass to build the <code>flat</code> channel packages. Let's do
it through the Web interface by visiting: <code>http://my-desktop:8081</code>.</p><p>Then by clicking on the "+" button, we can add a specification.</p><p><img src="/static/flat-spec.png" alt="flat spec" /></p><p>We configure Cuirass to operate on two channels: the default <code>guix</code> channel on
the <code>master</code> branch, and the <code>flat</code> channel also on the <code>master</code> branch.</p><p>Here we use the <code>channels</code> build type with the <code>flat</code> parameter. It means
that we ask Cuirass to build only the packages that are part of the <code>flat</code>
channel. You can read the Cuirass documentation for more information on the
<a href="https://guix.gnu.org/cuirass/manual/html_node/Specifications.html#Specifications">specification</a>
format.</p><p>We choose to build those packages for the <code>x86_64-linux</code> architecture with the
default priority.</p><p>Cuirass will now start building the latest version of the <code>flat</code> channel
packages.</p><p><img src="/static/flat-eval.png" alt="flat evaluation" /></p><p>If your are allergic to web interfaces, here's how to configure Cuirass to
achieve the same result:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">cuirass-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cuirass-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">specifications</span>
<span class="syntax-symbol">#~</span><span class="syntax-open">(</span><span class="syntax-symbol">list</span>
<span class="syntax-open">(</span><span class="syntax-symbol">specification</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-string">"flat"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">build</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-symbol">channels</span> <span class="syntax-symbol">flat</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">channels</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cons</span> <span class="syntax-open">(</span><span class="syntax-symbol">channel</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-symbol">'flat</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">url</span> <span class="syntax-string">"https://github.com/flatwhatson/guix-channel"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%default-channels</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host</span> <span class="syntax-string">"0.0.0.0"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><h3>The laptops configuration</h3><p>There's one missing piece. We need to configure the laptops to use the
substitutes built on the desktop machine.</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">guix-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">discover?</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">authorized-keys</span>
<span class="syntax-open">(</span><span class="syntax-symbol">append</span> <span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-open">(</span><span class="syntax-symbol">local-file</span> <span class="syntax-string">"./keys/elbruz.scm"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%default-authorized-guix-keys</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>It can be done by setting the <code>discover?</code> option of the Guix daemon service to
<code>true</code>. We also need to authorize the key of the desktop machine.</p><p>Now, if we run <code>guix build emacs-native-comp</code> we do not need to build stuff
anymore.</p><pre><code>mathieu@meije ~$ guix build emacs-native-comp
The following file will be downloaded:
/gnu/store/263zbli2cq3zbwcmsc4n27kb7zlzxrfj-emacs-native-comp-28.0.50-149.8d55070
substituting /gnu/store/263zbli2cq3zbwcmsc4n27kb7zlzxrfj-emacs-native-comp-28.0.50-149.8d55070...
downloading from http://192.168.1.51/nar/gzip/263zbli2cq3zbwcmsc4n27kb7zlzxrfj-emacs-native-comp-28.0.50-149.8d55070 ...
emacs-native-comp-28.0.50-149.8d55070 14.3MiB/s 00:06 | 88.3MiB transferred
/gnu/store/7wiv7jk52ix14zx1dk2bdnvnm07zrh41-emacs-native-comp-28.0.50-149.8d55070</code></pre><p>You can replicate this setup at home, and even simplify it by running Cuirass
directly on your main machine. You can also configure Cuirass to build
specific packages or manifests.</p><p>Don't hesitate to ask for help on <code>#guix</code> channel or on <code>help-guix@gnu.org</code> to
configure your own Cuirass service.</p>GNU Guix continuous integration - NLNet granthttps://othacehe.org/gnu-guix-continuous-integration---nlnet-grant.htmlMathieu Othaceheothacehe@gnu.org2021-01-19T10:00:00Z<p>I am glad to let you know that NLNet will support my project of improving GNU
Guix continuous integration system. The project will be funded through the
NGI0 PET Fund, a fund established by NLnet with financial support from the
European Commission's Next Generation, as explained
<a href="https://nlnet.nl/project/Cuirass/">here</a>.</p><p><img src="/static/cuirass.png" alt="Cuirass" /></p><p>GNU Guix is using <a href="https://guix.gnu.org/en/cuirass/">Cuirass</a> as continuous
integration software. It's a general purpose build automation server written
in GNU Guile that checks out sources from VCS repositories, execute build jobs
and store build results in a database. Cuirass also provides a <a href="https://ci.guix.gnu.org/">web
interface</a> to monitor the build results.</p><p>Cuirass is supposed to build and provide binary substitutes for every package
or system image definition. Without those substitutes available in time, the
user experience is really degraded, as the user has to allocate important time
and computation power resources into package building.</p><p>Cuirass has been supported by the Google summer of code project (2016, 2017
and 2018). It is right now developed voluntarily mostly by the GNU Guix
community. This grant will allow me to dedicate 6 months of my time to work
on improving Cuirass.</p><p>I plan to achieve the following tasks.</p><h2>Add dynamic offloading support</h2><p>Implement a dynamic offloading mode. This mode will allow an offloading server
to distribute work among different build machines.</p><p>The network discovery between the offloading server and the build machines
will be done using the Guile-Avahi library.</p><p>The build machines must be able to come an go dynamically. They will pick
build tasks from the offloading server using a work stealing scheduling
strategy.</p><p>The communication between the server and the build machines will be
implemented using Guile-ZMQ library.</p><h2>Improve the test suite</h2><p>Implement different dynamic offloading scenarios in the Cuirass test suite to
secure the development and prevent the risks of regressions.</p><h2>Improve Cuirass build monitoring interface</h2><p>Add build monitoring tools to Cuirass so that system administrators and users
can closely monitor the health of the whole continuous integration system and
promptly report any issue or slowdown.</p><ul><li><p>Add a web page allowing to monitor the builds that are currently performed
on the build farm, machine per machine. The build machine status (CPU load,
RAM, disk usage) will also appear on that page.</p></li><li><p>Add a live build logging feature, so that the build logs can be read in
realtime through the web interface.</p></li><li><p>Add new related metrics such as "build speed per machine" and "idle time per
machine" to the UI metrics reports.</p></li></ul><h2>Improve Cuirass build reporting interface</h2><p>Create a user account section to setup customized monitoring dashboards,
subscribe to build failures notifications and get notified of the availability
of new substitutes.</p><ul><li><p>Add support to report build failures by email and RSS.</p></li><li><p>Add a user account section to setup customized monitoring dashboards and
subscribe to build failures notifications.</p></li><li><p>Add a substitutes availability service, so that users can be notified when
the substitutes coverage of their manifest is passing a threshold.</p></li><li><p>Enforce accessibility guidelines after the WCAG audit.</p></li></ul><h2>Starting to work!</h2><p>As a first step, I have already migrated Cuirass from SQLite to PostgreSQL. I
might write something about that later on. I also started working on the
dynamic offloading mechanism with some encouraging results.</p><p>I will of course report the results periodically on this website and on the
GNU Guix <a href="https://guix.gnu.org/blog/">blog</a>.</p><p>Don't hesitate to contact me by email or on
<a href="https://guix.gnu.org/en/contact/irc/">#guix</a> to discuss your impressions as
well as the new features you would like to see in Cuirass.</p>Substitute server discoveryhttps://othacehe.org/substitute-server-discovery.htmlMathieu Othaceheothacehe@gnu.org2020-12-08T10:00:00Z<p>Last month I stumbled upon <a href="https://www.nongnu.org/guile-avahi/">guile-avahi</a>,
writen by Ludovic Courtès, the creator of GNU Guix. Strangely, this package
never made its way to Guix repository. With Ludovic help, I released the 0.4
version and added it to Guix.</p><p>Having a fully declarative system mechanism in Guix is great. However, every
now and then, having some auto-magical configuration can also be appreciable.</p><p>In this blog
<a href="https://guix.gnu.org/blog/2017/reproducible-builds-a-status-update/">article</a>,
Ludovic suggested to advertise <code>guix publish</code> servers on the local network
using Avahi. As you may know, Guix supports fetching substitutes from several
substitute servers.</p><p>When fetching substitutes from an unauthorized substitute server, the
substitute signature is checked against an authorized substitute server,
typically <code>ci.guix.gnu.org</code>, if it matches then the substitute can be
downloaded and installed.</p><p>The interesting part is that the unauthorized substitute server can be a
mirror or a local machine allowing faster download speed than the official
substitute server.</p><p>The only missing part to this puzzle is then, to automatically discover <code>guix publish</code> servers on the local network using Avahi and add them the list of
substitute servers.</p><p>As a first step, I created <code>(guix avahi)</code> module, providing two procedures.</p><pre><code><span class="syntax-symbol">avahi-publish-service-thread</span> <span class="syntax-comment">;advertise a service.
</span><span class="syntax-symbol">avahi-browse-service-thread</span> <span class="syntax-comment">;discover services.</span></code></pre><p>Then, I added an <code>--advertise</code> option to <code>guix publish</code> using the first
procedure defined above,</p><pre><code>Usage: guix publish [OPTION]...
Publish /gnu/store over HTTP.
[...]
-a, --advertise advertise on the local network</code></pre><p>and its counterpart in <code>guix-publish-service-type</code> service:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">guix-publish-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host</span> <span class="syntax-string">"0.0.0.0"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">port</span> <span class="syntax-symbol">3000</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">advertise?</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span> <span class="syntax-comment">;advertise using Avahi.
</span> <span class="syntax-open">(</span><span class="syntax-symbol">cache</span> <span class="syntax-symbol">#f</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">ttl</span> <span class="syntax-symbol">#f</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">compression-level</span> <span class="syntax-symbol">9</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>Running <code>avahi-browse</code>, we can now discover kind users providing substitutes
on the local network.</p><pre><code>mathieu@cervin:~$ avahi-browse -alr
= wlp3s0 IPv4 guix-publish-elbruz _guix_publish._tcp local
hostname = [elbruz.local]
address = [192.168.1.51]
port = [3000]
txt = []</code></pre><p>Finally, I added a <code>--discover</code> option to the <code>guix-daemon</code>, so that it can
spawn a <code>guix discover</code> process that uses <code>avahi-browse-service-thread</code> to
discover and report the available substitute servers.</p><pre><code>Usage: guix-daemon [OPTION...]
guix-daemon -- perform derivation builds and store accesses
[...]
--discover[=yes/no] use substitute servers discovered on the local
network</code></pre><p>The discovered substitute servers are then automatically added to the list of
substitute servers. There's of course, a related field in the <code>guix-daemon</code>
service:</p><pre><code>(guix-configuration
(authorize-key? #f)
(discover? #t) ;enable substitute server discovery.
(substitute-urls '("https://ci.guix.gnu.org"))
(log-compression 'gzip)
(build-accounts (* 4 16))
(extra-options (list "--max-jobs" "16" "--cores" "4")))</code></pre><p>Then, running any Guix command, substitutes will be downloaded from local
substitute server if possible:</p><pre><code>mathieu@cervin:~$ guix pull
Updating channel 'guix' from Git repository at 'https://git.savannah.gnu.org/git/guix.git'...
Authenticating channel 'guix', commits 9edb3f6 to 770fb65 (2 new commits)...
Building from this channel:
guix https://git.savannah.gnu.org/git/guix.git 770fb65
Computing Guix derivation for 'x86_64-linux'... |
substitute: updating substitutes from 'http://192.168.1.51:3000'... 100.0%
substitute: updating substitutes from 'https://ci.guix.gnu.org'... 100.0%
The following derivations will be built:
[...]
substitute: updating substitutes from 'http://192.168.1.51:3000'... 100.0%
downloading from http://192.168.1.51:3000/nar/gzip/kq69y9h1k3mv1dn289aayfikqizzgmgx-guix-manual
guix-manual 23.2MiB/s 00:00 | 4.7MiB transferred
downloading from http://192.168.1.51:3000/nar/gzip/8f4qppkg0qqmmi1rhzis9hxz6dmwrj3l-guix-cli
guix-cli 968KiB/s 00:02 | 1.4MiB transferred</code></pre><p>I have also added a <code>herd</code> command so that discovery can be enabled and
disabled at run-time when using Guix System, this way:</p><pre><code>herd discover guix-daemon on
herd discover guix-daemon off</code></pre><p>Finally, a patch adding this mechanism to the installer is on its way.</p><p><img src="./static/substitute.png" alt="Substitute server discovery in the installer" /></p><p>This development made me realize that improving substitute download speed was
a big deal. I hope to find some time to work on restoring a CDN and maybe
work on the IPFS branch.</p><p>Until then, many thanks to my sponsors and nice end-of-year festivities!</p>The Guix System image APIhttps://othacehe.org/the-guix-system-image-api.htmlMathieu Othaceheothacehe@gnu.org2020-10-22T10:00:00Z<p>First, I would first like to thank people that are supporting me via Librepay,
your help is much appreciated.</p><h2>Some history</h2><p>When I started hacking on GNU Guix a few years ago, the image generation
mechanism directly caught my attention. Turning a Scheme configuration file
into a disk-image by running one command seemed futuristic to me.</p><p>At that time, I was working as an embedded software developer using <strong>Yocto</strong>
and <strong>Buildroot</strong>, and constantly disappointed.</p><p>Turning Guix into an alternative quickly became a priority to me. The task
wasn't easy. Guix System was only used as an x86_64 distribution, the only
supported bootloader was <strong>Grub</strong> and the image generation used a complex
mechanism spawning a virtual machine to produce a disk-image.</p><p>Thanks to pending patches from David Craven, I added support for <strong>extlinux</strong>
and <strong>u-boot</strong> bootloaders. Then, some developments were necessary to support
image generation, system reconfiguration and installation on ARMv7.</p><p>Soon, I was able to create a disk-image for my BeagleBone Black and wrote an
<a href="https://guix.gnu.org/blog/2017/porting-guixsd-to-armv7/">article</a> about it.</p><p><img src="/static/guixsd-bbb.jpg" alt="Guix System on BeagleBone Black" /></p><p>You can also have a look to the talk I gave at <a href="https://archive.fosdem.org/2020/schedule/event/ggaaattyp/">Fosdem
2020</a>.</p><p>Further developments were required to make this first hack viable:</p><ul><li>Allow Guix System cross-compilation.</li><li>Simplify the image generation mechanism by getting rid of the virtual
machine step.</li><li>Add an image API to ease image declaration.</li></ul><p>Those developments kept me busy for the last two years, but were recently sped
up thanks to Jan (janneke) Nieuwenhuizen and his tremendous work on the Hurd.
Porting Guix System to the Hurd is achieved by cross-compiling a Guix System
image for the <strong>i586-pc-gnu</strong> architecture. His work is detailed in this
<a href="https://guix.gnu.org/en/blog/2020/childhurds-and-substitutes/">article</a>.</p><p>Now, let's dive into this Guix System image API.</p><h2>Guix System image API</h2><p>Historically, Guix System is centered around an <strong>operating-system</strong>
structure. This structure contains various fields ranging from the bootloader
and kernel declaration to the services to install.</p><p>Turning this structure into a disk-image requires additional information such
as the image label, size and partitioning. That's the purpose of the new
<strong>image</strong> record.</p><pre><code><span class="syntax-open">(</span><span class="syntax-special">define-record-type*</span> <span class="syntax-symbol"><image></span>
<span class="syntax-symbol">image</span> <span class="syntax-symbol">make-image</span>
<span class="syntax-symbol">image?</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-symbol">image-name</span> <span class="syntax-comment">;symbol
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">#f</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">format</span> <span class="syntax-symbol">image-format</span><span class="syntax-close">)</span> <span class="syntax-comment">;symbol
</span> <span class="syntax-open">(</span><span class="syntax-symbol">target</span> <span class="syntax-symbol">image-target</span>
<span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">#f</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">size</span> <span class="syntax-symbol">image-size</span> <span class="syntax-comment">;size in bytes as integer
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">'guess</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span> <span class="syntax-symbol">image-operating-system</span> <span class="syntax-comment">;<operating-system>
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">#f</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">partitions</span> <span class="syntax-symbol">image-partitions</span> <span class="syntax-comment">;list of <partition>
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">compression?</span> <span class="syntax-symbol">image-compression?</span> <span class="syntax-comment">;boolean
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">volatile-root?</span> <span class="syntax-symbol">image-volatile-root?</span> <span class="syntax-comment">;boolean
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">substitutable?</span> <span class="syntax-symbol">image-substitutable?</span> <span class="syntax-comment">;boolean
</span> <span class="syntax-open">(</span><span class="syntax-symbol">default</span> <span class="syntax-symbol">#t</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>This record also contains the operating-system to instantiate. The <code>format</code>
field defines the image type and can be <code>disk-image</code>, <code>compressed-qcow2</code> or
<code>iso9660</code>. In the future, it could be extended to <code>docker</code> or other image
types.</p><p>A new directory in the Guix sources is dedicated to images definition. For now
there are two files:</p><ul><li>gnu/system/images/hurd.scm</li><li>gnu/system/images/pine64.scm</li></ul><p>Let's have a look to <code>pine64.scm</code>. It contains the <code>pine64-barebones-os</code>
variable which is a minimal definition of an operating-system dedicated to the
<strong>Pine A64 LTS</strong> board.</p><pre><code><span class="syntax-open">(</span><span class="syntax-special">define</span> <span class="syntax-symbol">pine64-barebones-os</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host-name</span> <span class="syntax-string">"vignemale"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">timezone</span> <span class="syntax-string">"Europe/Paris"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">locale</span> <span class="syntax-string">"en_US.utf8"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader</span> <span class="syntax-open">(</span><span class="syntax-symbol">bootloader-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader</span> <span class="syntax-symbol">u-boot-pine64-lts-bootloader</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">target</span> <span class="syntax-string">"/dev/vda"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">initrd-modules</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">kernel</span> <span class="syntax-symbol">linux-libre-arm64-generic</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">file-systems</span> <span class="syntax-open">(</span><span class="syntax-symbol">cons</span> <span class="syntax-open">(</span><span class="syntax-symbol">file-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">device</span> <span class="syntax-open">(</span><span class="syntax-symbol">file-system-label</span> <span class="syntax-string">"my-root"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">mount-point</span> <span class="syntax-string">"/"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">type</span> <span class="syntax-string">"ext4"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%base-file-systems</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">services</span> <span class="syntax-open">(</span><span class="syntax-symbol">cons</span> <span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">agetty-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">agetty-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">extra-options</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"-L"</span><span class="syntax-close">)</span><span class="syntax-close">)</span> <span class="syntax-comment">; no carrier detect
</span> <span class="syntax-open">(</span><span class="syntax-symbol">baud-rate</span> <span class="syntax-string">"115200"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">term</span> <span class="syntax-string">"vt100"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">tty</span> <span class="syntax-string">"ttyS0"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%base-services</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>The <code>kernel</code> and <code>bootloader</code> fields are pointing to packages dedicated to
this board.</p><p>Right below, the <code>pine64-image-type</code> variable is also defined.</p><pre><code><span class="syntax-open">(</span><span class="syntax-special">define</span> <span class="syntax-symbol">pine64-image-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">image-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-symbol">'pine64-raw</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">constructor</span> <span class="syntax-open">(</span><span class="syntax-symbol">cut</span> <span class="syntax-symbol">image-with-os</span> <span class="syntax-symbol">arm64-disk-image</span> <span class="syntax-symbol"><></span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>It's using a record we haven't talked about yet, the <code>image-type</code> record,
defined this way:</p><pre><code><span class="syntax-open">(</span><span class="syntax-special">define-record-type*</span> <span class="syntax-symbol"><image-type></span>
<span class="syntax-symbol">image-type</span> <span class="syntax-symbol">make-image-type</span>
<span class="syntax-symbol">image-type?</span>
<span class="syntax-open">(</span><span class="syntax-symbol">name</span> <span class="syntax-symbol">image-type-name</span><span class="syntax-close">)</span> <span class="syntax-comment">;symbol
</span> <span class="syntax-open">(</span><span class="syntax-symbol">constructor</span> <span class="syntax-symbol">image-type-constructor</span><span class="syntax-close">)</span><span class="syntax-close">)</span> <span class="syntax-comment">;<operating-system> -> <image></span></code></pre><p>The main purpose of this record is to associate a name to a procedure
transforming an operating-system to an image. To understand why it is
necessary, let's have a look to the command producing a disk-image from an
operating-system configuration file:</p><pre><code>guix system disk-image my-os.scm</code></pre><p>This command expects an operating-system configuration but how should we
indicate that we want an image targeting a Pine64 board? We need to provide an
extra information, the <code>image-type</code>, by passing the <code>--image-type</code> or <code>-t</code>
flag, this way:</p><pre><code>guix system disk-image --image-type=pine64-raw my-os.scm</code></pre><p>This image-type parameter points to the <code>pine64-image-type</code> defined
above. Hence, the operating-system declared in <code>my-os.scm</code> will be applied the
<code>(cut image-with-os arm64-disk-image <>)</code> procedure to turn it into an image.</p><p>The resulting image looks like:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">image</span>
<span class="syntax-open">(</span><span class="syntax-symbol">format</span> <span class="syntax-symbol">'disk-image</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">target</span> <span class="syntax-string">"aarch64-linux-gnu"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span> <span class="syntax-symbol">my-os</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">partitions</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-open">(</span><span class="syntax-symbol">partition</span>
<span class="syntax-open">(</span><span class="syntax-symbol">inherit</span> <span class="syntax-symbol">root-partition</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">offset</span> <span class="syntax-symbol">root-offset</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>which is the aggregation of the operating-system defined in <code>my-os.scm</code> to the
<code>arm64-disk-image</code> record.</p><p>But enough Scheme madness. What does this image API bring to the Guix user?</p><h2>Image API usage</h2><p>One can run:</p><pre><code>mathieu@cervin:~$ guix system --list-image-types
The available image types are:
- pine64-raw
- hurd-raw
- hurd-qcow2
- iso9660
- uncompressed-iso9660
- raw
- qcow2</code></pre><p>and by writing an operating-system file based on <code>pine64-barebones-os</code> or
<code>hurd-barebones-os</code> run:</p><pre><code>guix system --image-type=pine64-raw my-pine-os.scm</code></pre><p>or,</p><pre><code>guix system --image-type=hurd-raw my-hurd-os.scm</code></pre><p>to get a disk-image that can directly be written to a support and booted from.</p><p>Without changing anything to <code>my-hurd-os.scm</code>, calling:</p><pre><code>guix system --image-type=hurd-qcow2 my-hurd-os.scm</code></pre><p>will instead produce a Hurd QEMU image.</p><p>This image API brings some flexibility to the image generation but there's still room for some improvements.</p><h2>Future improvements</h2><p>The first improvement would be to add support for new image-types so that Guix
System can be used on other machines such as <strong>Novena</strong>, <strong>Pinebook Pro</strong> or
<strong>MNT Reform</strong>. This task should now be relatively straightforward.</p><p>Some images built periodically by the CI are made available
<a href="https://guix.gnu.org/en/download/latest/">here</a>. Adding images targeting new
supported platforms, such as Pine64 LTS to this page would be nice.</p><p>Finally, images targeting foreign architectures are cross-compiled. As Guix
cross-compilation support is not optimal yet, improving <a href="https://guix.gnu.org/manual/en/html_node/Virtualization-Services.html#index-emulation">transparent
emulation</a> support could also help.</p><p>Now you are more familiar with the image API, feel free to join the party and
help porting Guix System to your favourite machine!</p>Hosting a blog using only Schemehttps://othacehe.org/hosting-a-blog-using-only-scheme.htmlMathieu Othaceheothacehe@gnu.org2020-08-31T10:00:00Z<p>I've discovered static blog generators using <a href="https://gohugo.io/">Hugo</a> to
write a <a href="https://levelocestrigolo.org/">travel blog</a> a few years ago.</p><p>While it gets the job done, and allowed me to write articles using <a href="https://www.orgmode.org/">Org
mode</a>, I would not recommend it. I found it too
complex for my modest needs.</p><p>Being quite involved in <a href="https://guix.gnu.org/">GNU Guix</a>, I decided to have a
look to <a href="https://dthompson.us/projects/haunt.html">Haunt</a> that is currently
used for the GNU Guix website.</p><p>Haunt defines itself as <em>a simple, functional, hackable static site generator
that gives authors the ability to treat websites as Scheme programs.</em></p><h2>Writing the blog using Haunt</h2><p>I chose to get started using the blog of David Thomson, the creator of Haunt
itself. Haunt is well
<a href="https://dthompson.us/manuals/haunt/index.html">documented</a> and writing a
website mostly consists in creating a site object, this way:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">site</span> <span class="syntax-keyword">#:title</span> <span class="syntax-string">"Othacehe"</span>
<span class="syntax-keyword">#:domain</span> <span class="syntax-string">"othacehe.org"</span>
<span class="syntax-keyword">#:build-directory</span> <span class="syntax-string">"/tmp/website"</span>
<span class="syntax-keyword">#:default-metadata</span>
<span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-open">(</span><span class="syntax-symbol">author</span> <span class="syntax-symbol">.</span> <span class="syntax-string">"Mathieu Othacehe"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">email</span> <span class="syntax-symbol">.</span> <span class="syntax-string">"othacehe@gnu.org"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-keyword">#:readers</span> <span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-symbol">commonmark-reader*</span><span class="syntax-close">)</span>
<span class="syntax-keyword">#:builders</span> <span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-open">(</span><span class="syntax-symbol">blog</span> <span class="syntax-keyword">#:theme</span> <span class="syntax-symbol">my-theme</span>
<span class="syntax-keyword">#:collections</span> <span class="syntax-symbol">%collections</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">atom-feed</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">atom-feeds-by-tag</span><span class="syntax-close">)</span>
<span class="syntax-symbol">index-page</span>
<span class="syntax-symbol">projects-page</span>
<span class="syntax-open">(</span><span class="syntax-symbol">static-directory</span> <span class="syntax-string">"css"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">static-directory</span> <span class="syntax-string">"fonts"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">static-directory</span> <span class="syntax-string">"files"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>Pretty straightforward, compared to Hugo complex directory organization. The
most problematical part for me here are the three letters CSS. My strong
aversion for web design forbids me to roll-out my own theme.</p><p>One advantage of Hugo is the impressive collection of available website
themes. Haunt being for now less popular, I opted for the exact same theme as
David.</p><p>Then, a few lines of Scheme later, the website was ready. You can have a look
to my
<a href="https://gitlab.com/mothacehe/website/-/blob/master/haunt.scm">haunt.scm</a>. This
very file contains the whole configuration of the website, all the rest is
static content.</p><h2>Building the website</h2><p>The operation of <strong>building the website</strong> roughly consists in calling Haunt to
convert the Scheme files and the post articles into a bunch of HTML files.</p><p>This can be done by invoking this command:</p><pre><code>$ haunt build</code></pre><p>To run this command you obviously need to have <strong>haunt</strong> installed, but also
<strong>guile-syntax-highlight</strong> that is used for highlighting code snippets. As I'd
like the building process to be self-contained, I use GNU Guix for this
operation.</p><p>I won't go into details here but the idea is to write a
<a href="https://gitlab.com/mothacehe/website/-/blob/master/.guix.scm">.guix.scm</a> file
that specifies the build dependencies and the required command to build the
website.</p><p>Then, all you have to do is:</p><pre><code>$ guix build -f .guix.scm
...
build completed successfully
successfully built /gnu/store/19fx3njl6sr53nraayrvfzgb209g6mml-my-web-site.drv
/gnu/store/cgs02fcvmb3p7rni0nlb7wp85yd3mnkm-my-web-site
</code></pre><p>This returns a <em>store</em> directory that contains the built website. The only
requirement here is to have GNU Guix available.</p><p>Now, we need to find a way to deploy this directory on a web server,
and believe it or not, that's the fun part!</p><h2>Deploying the website</h2><p>I've been previously hosting stuff on a friend's server. I'd like to take my
independence, but I don't want to use a machine at home, as I'm frequently
moving.</p><p>This means that I need to find a hosting company. The choice is tricky because
I don't want to run Dockers, Kubernetes or type a bunch of <code>apt install</code>
commands and edit brutally some <code>/etc/</code> configuration files.</p><p>I would prefer the hosted machine to run <em>Guix System</em> so that I can pursue my
Scheme-only experiment.</p><p>Turns out GNU Guix has a <em>deploy</em> command that is able to:</p><ul><li>Deploy a Guix System configuration on a remote machine using SSH.</li><li>Spawn a remote DigitalOcean virtual machine and deploy a Guix System
configuration on it.</li></ul><p>The second option is very tempting, so I had a closer look at the mechanics.
It appears that <em>Guix System</em> is not supported as a distribution by
DigitalOcean. The employed trick for deployment is to spawn an <em>Ubuntu</em> VM, or
<em>droplet</em> as it is fancily called, and install <em>Guix System</em> from there.</p><p>I'm not fond of this approach and I would prefer to have <em>Guix System</em>
directly deployed. Luckily, I discovered that DigitalOcean supports running
<em>custom images</em>, that's exactly what we need.</p><p>I decided to proceed this way:</p><ul><li>Write an <code>os.scm</code> file that describes the configuration of a Guix System
distribution able to host my website.</li><li>Create a disk-image from this file and upload it on DigitalOcean.</li><li>Spawn a <em>droplet</em> using that disk-image and run it.</li></ul><p>Let's try it. We first need the <code>os.scm</code> configuration file. Here it is:</p><pre><code><span class="syntax-open">(</span><span class="syntax-symbol">use-modules</span> <span class="syntax-open">(</span><span class="syntax-symbol">gnu</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">sysadmin</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">use-service-modules</span> <span class="syntax-symbol">certbot</span> <span class="syntax-symbol">networking</span> <span class="syntax-symbol">ssh</span> <span class="syntax-symbol">web</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">use-package-modules</span> <span class="syntax-symbol">certs</span> <span class="syntax-symbol">rsync</span> <span class="syntax-symbol">screen</span> <span class="syntax-symbol">ssh</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-special">define</span> <span class="syntax-open">(</span><span class="syntax-symbol">cert-path</span> <span class="syntax-symbol">host</span> <span class="syntax-symbol">file</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">format</span> <span class="syntax-symbol">#f</span> <span class="syntax-string">"/etc/letsencrypt/live/~a/~a.pem"</span> <span class="syntax-symbol">host</span> <span class="syntax-open">(</span><span class="syntax-symbol">symbol->string</span> <span class="syntax-symbol">file</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">operating-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">host-name</span> <span class="syntax-string">"viso"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">timezone</span> <span class="syntax-string">"Europe/Paris"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">locale</span> <span class="syntax-string">"en_US.utf8"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader</span> <span class="syntax-open">(</span><span class="syntax-symbol">bootloader-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">bootloader</span> <span class="syntax-symbol">grub-bootloader</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">target</span> <span class="syntax-string">"/dev/vda"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">terminal-outputs</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-symbol">console</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">file-systems</span> <span class="syntax-open">(</span><span class="syntax-symbol">cons</span> <span class="syntax-open">(</span><span class="syntax-symbol">file-system</span>
<span class="syntax-open">(</span><span class="syntax-symbol">mount-point</span> <span class="syntax-string">"/"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">device</span> <span class="syntax-string">"/dev/vda1"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">type</span> <span class="syntax-string">"ext4"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%base-file-systems</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">packages</span>
<span class="syntax-open">(</span><span class="syntax-symbol">cons*</span> <span class="syntax-symbol">nss-certs</span> <span class="syntax-symbol">openssh</span> <span class="syntax-symbol">rsync</span> <span class="syntax-symbol">screen</span>
<span class="syntax-symbol">%base-packages</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">services</span>
<span class="syntax-open">(</span><span class="syntax-symbol">append</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">certbot-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">certbot-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">email</span> <span class="syntax-string">"othacehe@gnu.org"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">certificates</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span>
<span class="syntax-open">(</span><span class="syntax-symbol">certificate-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">domains</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"othacehe.org"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">deploy-hook</span>
<span class="syntax-open">(</span><span class="syntax-symbol">program-file</span>
<span class="syntax-string">"nginx-deploy-hook"</span>
<span class="syntax-symbol">#~</span><span class="syntax-open">(</span><span class="syntax-special">let</span> <span class="syntax-open">(</span><span class="syntax-open">(</span><span class="syntax-symbol">pid</span> <span class="syntax-open">(</span><span class="syntax-special">call-with-input-file</span> <span class="syntax-string">"/var/run/nginx/pid"</span> <span class="syntax-symbol">read</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">kill</span> <span class="syntax-symbol">pid</span> <span class="syntax-symbol">SIGHUP</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">dhcp-client-service-type</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">nginx-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">nginx-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">server-blocks</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span>
<span class="syntax-open">(</span><span class="syntax-symbol">nginx-server-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">listen</span> <span class="syntax-symbol">'</span><span class="syntax-open">(</span><span class="syntax-string">"443 ssl"</span> <span class="syntax-string">"[::]:443 ssl"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">server-name</span> <span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-string">"othacehe.org"</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">root</span> <span class="syntax-string">"/var/www/website/"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">locations</span>
<span class="syntax-open">(</span><span class="syntax-symbol">list</span>
<span class="syntax-open">(</span><span class="syntax-symbol">nginx-location-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">uri</span> <span class="syntax-string">"/"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">body</span> <span class="syntax-open">(</span><span class="syntax-symbol">list</span> <span class="syntax-string">"index index.html;"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">ssl-certificate</span> <span class="syntax-open">(</span><span class="syntax-symbol">cert-path</span> <span class="syntax-string">"othacehe.org"</span> <span class="syntax-symbol">'fullchain</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">ssl-certificate-key</span> <span class="syntax-open">(</span><span class="syntax-symbol">cert-path</span> <span class="syntax-string">"othacehe.org"</span> <span class="syntax-symbol">'privkey</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">openssh-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">openssh-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">openssh</span> <span class="syntax-symbol">openssh-sans-x</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">permit-root-login</span> <span class="syntax-symbol">'without-password</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">authorized-keys</span>
<span class="syntax-symbol">`</span><span class="syntax-open">(</span><span class="syntax-open">(</span><span class="syntax-string">"root"</span>
<span class="syntax-symbol">,</span><span class="syntax-open">(</span><span class="syntax-symbol">local-file</span> <span class="syntax-string">"/home/mathieu/.ssh/id_rsa.pub"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">service</span> <span class="syntax-symbol">static-web-site-service-type</span>
<span class="syntax-open">(</span><span class="syntax-symbol">static-web-site-configuration</span>
<span class="syntax-open">(</span><span class="syntax-symbol">git-url</span>
<span class="syntax-string">"https://gitlab.com/mothacehe/website.git"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">directory</span> <span class="syntax-string">"/var/www/website"</span><span class="syntax-close">)</span>
<span class="syntax-open">(</span><span class="syntax-symbol">build-file</span> <span class="syntax-string">".guix.scm"</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span>
<span class="syntax-symbol">%base-services</span><span class="syntax-close">)</span><span class="syntax-close">)</span><span class="syntax-close">)</span></code></pre><p>It is a bit lengthy, but keep in mind that it will allow us to run a custom
<em>Guix System</em> distribution that behaves exactly as we want, out of the box.</p><p>Most of the work is to describe the five services we need:</p><ul><li>A DHCP server, so that the <em>droplet</em> can figure out its public IP address.</li><li>An OpenSSH server, so that we can connect to the <em>droplet</em> via SSH.</li><li>An Nginx web server hosting the website.</li><li>A certbot service so that we have proper HTTPS support.</li><li>A service that will periodically fetch the website sources from the Gitlab
repository, build them and deploy them.</li></ul><p>You can have a look to the documentation
<a href="https://guix.gnu.org/manual/en/html_node/Services.html">here</a> for the exact
syntax of this file.</p><p>Then, we need to produce a disk-image out of this configuration file. It can
be done that way:</p><pre><code>$ guix system disk-image os.scm -L ~/maintenance/hydra/modules/ --image-size=5G
...
/gnu/store/hca2zg51in1dnhaqkljfkvl8gyipmzni-disk-image</code></pre><p>The extra <em>-L</em> option is required here because the
<em>static-web-site-service-type</em> is not defined in GNU Guix itself, but in an
external repository, available
<a href="https://git.savannah.gnu.org/cgit/guix/maintenance.git/">here</a>. As stated
previously, this service will create a cron job that fetches the website
sources from my Gitlab repository, build them and deploy them. To save some
extra space, let's also convert this raw disk-image into a compressed
<strong>qcow2</strong> image:</p><pre><code>$ qemu-img convert -c -f raw -O qcow2 /gnu/store/hca2zg...-disk-image do.qcow2</code></pre><p>The resulting image that weighs around 500MiB can then be uploaded to
DigitalOcean, and a <em>droplet</em> using this custom image can be started from the
web interface.</p><p>A few other manipulations are required to set up the DNS records, but nothing
original.</p><p>That's it, using almost only Scheme, I was able to write and deploy this
blog. This setup should be autonomous and the only work left is to write some
articles, commit them, and wait for the <em>droplet</em> to fetch, build and deploy
them.</p><p>If for some reason I need to edit the configuration of the <em>droplet</em> in the
future, I have two options:</p><ul><li>Create a new disk-image and spawn a <em>droplet</em> from it.</li><li>Reconfigure the existing <em>droplet</em> using <code>guix deploy</code> via SSH.</li></ul><p>The two options are equally valid but the second one should go faster as it
does not involve uploading a new image. Anyway, we will maybe explore those
options in a future article.</p><p>Stay tuned.</p>