<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Testing on Jon Seager</title><link>https://jnsgr.uk/tags/testing/</link><description>Recent content in Testing on Jon Seager</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 18 Mar 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://jnsgr.uk/tags/testing/index.xml" rel="self" type="application/rss+xml"/><item><title>Simplifying Test &amp; Release of Snapped GUI Apps</title><link>https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/</link><pubDate>Mon, 18 Mar 2024 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/</guid><description>&lt;h2 id="introduction" class="relative group"&gt;Introduction &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introduction" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;For the past few months, I&amp;rsquo;ve been getting steadily more involved in &lt;a href="https://github.com/snapcrafters" target="_blank" rel="noreferrer"&gt;Snapcrafters&lt;/a&gt;. The Snapcrafters are a community dedicated to the creation and maintenance of ~100 snap packages. They&amp;rsquo;re currently maintaining snaps for applications like &lt;a href="https://snapcraft.io/signal-desktop" target="_blank" rel="noreferrer"&gt;Signal Desktop&lt;/a&gt;, &lt;a href="https://snapcraft.io/discord" target="_blank" rel="noreferrer"&gt;Discord&lt;/a&gt;, &lt;a href="https://snapcraft.io/gimp" target="_blank" rel="noreferrer"&gt;Gimp&lt;/a&gt;, &lt;a href="https://snapcraft.io/terraform" target="_blank" rel="noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://snapcraft.io/search?q=publisher%3Asnapcrafters" target="_blank" rel="noreferrer"&gt;many more&lt;/a&gt;, some with &lt;em&gt;hundreds of thousands of weekly active users&lt;/em&gt;. As with any community organisation, maintainer participation can ebb and flow over time as people find themselves with competing priorities.&lt;/p&gt;
&lt;p&gt;One of my personal goals for participation in the Snapcrafters org was to help them build more automated, sustainable processes for bumping versions of snaps as the upstreams move forward, and find a more robust way to test GUI applications before they&amp;rsquo;re released to the masses.&lt;/p&gt;
&lt;p&gt;The snap store comes with a surprisingly rich delivery mechanism consisting of &lt;a href="https://snapcraft.io/docs/channels" target="_blank" rel="noreferrer"&gt;tracks, risks and branches&lt;/a&gt;. This means that (among other things) changes to applications can be tested by way of an incremental roll out by those willing to help out - by subscribing to the &lt;code&gt;edge&lt;/code&gt; or &lt;code&gt;candidate&lt;/code&gt; channels.&lt;/p&gt;
&lt;h2 id="the-problem" class="relative group"&gt;The Problem &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#the-problem" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;While bumping the versions of the applications in snap packages &lt;a href="https://github.com/snapcrafters/signal-desktop/blob/325c06602d6bbb976afbabe48e16c688f1d70c94/.github/workflows/sync-version-with-upstream.yml" target="_blank" rel="noreferrer"&gt;can be done trivially&lt;/a&gt;, testing that the new application can launch on the Linux desktop and function correctly is more difficult - especially given the &amp;ldquo;headless&amp;rdquo; nature of CI systems, and the inherent complexity of some of the applications involved.&lt;/p&gt;
&lt;p&gt;Electron has, in my opinion, been a huge net win for the Linux desktop. Performance and early Wayland compatibility aside, the selection of mainstream applications available to the Linux desktop user is certainly much greater as a result. One downside is that each app is essentially a browser with lots of complex moving parts which can be difficult to maintain for packagers over time - and particularly so for a team of volunteers who may not be experts in the applications they help to maintain.&lt;/p&gt;
&lt;p&gt;In a &lt;a href="https://jnsgr.uk/2024/02/nixos-vms-in-github-actions/" target="_blank" rel="noreferrer"&gt;previous post&lt;/a&gt; I wrote about how KVM-acceleration is now available on Github Actions runners. While it&amp;rsquo;s certainly possible to just pull in various pieces of the Linux desktop using &lt;code&gt;apt&lt;/code&gt; directly on a Github Actions runner, the resulting configuration normally involves convoluted setup with VNC or similar. Such setups are usually fragile, and can be more difficult to reproduce locally - making it slower to debug any issues that do arise.&lt;/p&gt;
&lt;h2 id="lxd-desktop-vms" class="relative group"&gt;LXD Desktop VMs &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#lxd-desktop-vms" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Before working at Canonical, I must confess to having paid &lt;em&gt;very little&lt;/em&gt; attention to &lt;a href="https://canonical.com/lxd" target="_blank" rel="noreferrer"&gt;LXD&lt;/a&gt;. Since joining, it&amp;rsquo;s become one of my most used tools in my daily workflow. I had some early experience with LXD a few years ago when it essentially &amp;ldquo;just&amp;rdquo; did Ubuntu containers, but it&amp;rsquo;s evolved into a very competent hypervisor in its own right, providing both container and virtual machine images for numerous different Linux distributions. In more recent history, desktop virtual machines were introduced which gives a very fast way to boot into a desktop across multiple distributions.&lt;/p&gt;
&lt;p&gt;To boot into a Ubuntu 22.04 LTS desktop virtual machine, for example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lxc launch images:ubuntu/22.04/desktop ubuntu --vm --console&lt;span class="o"&gt;=&lt;/span&gt;vga
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;As a side note, last year Canonical &lt;a href="https://ubuntu.com/blog/lxd_ui" target="_blank" rel="noreferrer"&gt;announced&lt;/a&gt; the LXD UI, which is a beautiful web UI for managing clusters of LXD servers, and includes a graphical web console for virtual machines - which is incredibly useful for testing software across versions and desktops.&lt;/p&gt;
&lt;h2 id="wrapping-lxd" class="relative group"&gt;Wrapping LXD &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#wrapping-lxd" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;After playing with LXD a bunch locally, I confirmed that between &lt;code&gt;lxc exec&lt;/code&gt;, &lt;code&gt;lxc file pull&lt;/code&gt; I had all I need. The commands were all relatively simple, but I wanted to ensure that the Github Actions I wrote were as maintainable as possible, so I decided to write a small wrapper for LXD, which ended up being named &lt;code&gt;ghvmctl&lt;/code&gt; (Github Virtual Machine Control) because naming is…. hard!&lt;/p&gt;
&lt;p&gt;There is nothing special about &lt;code&gt;ghvmctl&lt;/code&gt;, it is just a &lt;code&gt;bash&lt;/code&gt; script. Perhaps one day I&amp;rsquo;ll implement it in something a little more… rigorous? That said, it&amp;rsquo;s wrapping relatively few shell commands, with very few variables, and it&amp;rsquo;s been solid for several months now. The sum of &lt;code&gt;ghvmctl&lt;/code&gt;&amp;rsquo;s capabilities can be summarised as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Launch Ubuntu desktop VMs&lt;/li&gt;
&lt;li&gt;Dismiss any initial setup wizards&lt;/li&gt;
&lt;li&gt;Ensure that &lt;code&gt;gnome-screenshot&lt;/code&gt; is installed&lt;/li&gt;
&lt;li&gt;Provide a simple way to install and run snaps&lt;/li&gt;
&lt;li&gt;Provide a simple way to screenshot the whole screen, and the active window&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The script is mostly contained in a &lt;a href="https://github.com/snapcrafters/ghvmctl/blob/1ac4a99dd1c6f78226b60eda205cd06c6ac20dfa/src/ghvmctl" target="_blank" rel="noreferrer"&gt;single file&lt;/a&gt;, apart from &lt;a href="https://github.com/snapcrafters/ghvmctl/blob/1ac4a99dd1c6f78226b60eda205cd06c6ac20dfa/src/ghvmctl-runner" target="_blank" rel="noreferrer"&gt;&lt;code&gt;ghvmctl-runner&lt;/code&gt;&lt;/a&gt; which is pushed automatically into any VMs started by &lt;code&gt;ghvmctl&lt;/code&gt;, and provides a way for applications to be run with all the appropriate environment variables such that graphical applications can run when the VM is being controlled headlessly (such as &lt;code&gt;DISPLAY&lt;/code&gt;, &lt;code&gt;WAYLAND_DISPLAY&lt;/code&gt;, &lt;code&gt;XDG_SESSION_TYPE&lt;/code&gt;, etc.).&lt;/p&gt;
&lt;p&gt;There are very few dependencies for the script, but I decided to &lt;a href="https://github.com/snapcrafters/ghvmctl/blob/1ac4a99dd1c6f78226b60eda205cd06c6ac20dfa/snap/snapcraft.yaml" target="_blank" rel="noreferrer"&gt;package it as a snap&lt;/a&gt; to simplify installing it on Github runners. The snap is simple, containing just the two scripts mentioned above, and the LXC client. This also means you can install and use the tool locally should you wish to experiment with it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install ghvmctl&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo snap install ghvmctl
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Allow ghvmctl to access the LXD socket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo snap connect ghvmctl:lxd lxd:lxd
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Launch a VM and prepare for testing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghvmctl prepare
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Install a snap from the store&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghvmctl snap-install signal-desktop
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run the snap on the desktop&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghvmctl snap-run signal-desktop
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Wait a few seconds for the app to start...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Take screenshots and pull them back to $HOME/ghvmctl-screenshots&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghvmctl screenshot-full
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ghvmctl screenshot-window
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;To simplify the installation and setup of &lt;code&gt;ghvmctl&lt;/code&gt;, there is also a &lt;a href="https://github.com/snapcrafters/ci/tree/8eb0566a765cd0196d7223734dd4cc0f3eb4521f/setup-ghvmctl" target="_blank" rel="noreferrer"&gt;Github Action&lt;/a&gt; which takes care of enabling KVM on the runner, initialising LXD, installing &lt;code&gt;ghvmctl&lt;/code&gt; and ensuring it has access to the LXD socket.&lt;/p&gt;
&lt;h2 id="building-an-integrated-workflow" class="relative group"&gt;Building An Integrated Workflow &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#building-an-integrated-workflow" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;By now, the Snapcrafters have quite a &lt;a href="https://github.com/snapcrafters/ci" target="_blank" rel="noreferrer"&gt;sophisticated collection&lt;/a&gt; of Github Actions which are used for managing the release lifecycle of snaps, but for me it was this piece that tied it all together for GUI applications. The actions can be summarised as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Parsing &lt;code&gt;snapcraft.yaml&lt;/code&gt; files for details such as name, architectures, version&lt;/li&gt;
&lt;li&gt;Building snaps locally on Github Actions runners when Pull Requests are made&lt;/li&gt;
&lt;li&gt;Building snaps across architectures on the Launchpad build farm when changes are merged&lt;/li&gt;
&lt;li&gt;Create a &amp;ldquo;Call for Testing&amp;rdquo; Github Issue with details of the &lt;code&gt;candidate&lt;/code&gt; revisions&lt;/li&gt;
&lt;li&gt;Follow up on the issue with screenshots in a comment&lt;/li&gt;
&lt;li&gt;Promote the snap to &lt;code&gt;stable&lt;/code&gt; when a maintainer issues the command&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these things are particularly hard in their own right, but they amount to some complicated juggling of Github Actions artefacts and tokens for various repositories and external services.&lt;/p&gt;
&lt;h2 id="building-a-screenshot-action" class="relative group"&gt;Building A Screenshot Action &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#building-a-screenshot-action" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;The Github Action &lt;a href="https://github.com/snapcrafters/ci/tree/8eb0566a765cd0196d7223734dd4cc0f3eb4521f/get-screenshots" target="_blank" rel="noreferrer"&gt;responsible for collecting screenshots&lt;/a&gt; is used across multiple snaps to give maintainers a bit more confidence when releasing changes into the &lt;code&gt;stable&lt;/code&gt; channels for their snaps.&lt;/p&gt;
&lt;p&gt;The action makes use of &lt;code&gt;ghvmctl&lt;/code&gt; to launch VMs, install the &lt;code&gt;candidate&lt;/code&gt; snaps and collect screenshots of them. This turned out to be simple with the introduction of &lt;code&gt;ghvmctl&lt;/code&gt; - the complicated part turned out to be where to store the screenshots such that they could be published in a comment! Github provides image/file hosting for comments on issue &lt;em&gt;when the comments are made through the web UI&lt;/em&gt;. As far as I can tell, there is no way to submit a comment with an embedded picture from the Github API (let me know!), and I wasn&amp;rsquo;t keen to rely on a third party service such as Imgur.&lt;/p&gt;
&lt;p&gt;The solution we settled on was to create a &lt;a href="https://github.com/snapcrafters/ci-screenshots" target="_blank" rel="noreferrer"&gt;&lt;code&gt;ci-screenshots&lt;/code&gt;&lt;/a&gt;, which could be published to by the workflows of each snap repository. We will, over time, clear out older screenshots.&lt;/p&gt;
&lt;h2 id="end-result" class="relative group"&gt;End Result &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#end-result" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;An example of the end result can be seen in &lt;a href="https://github.com/snapcrafters/signal-desktop/issues/267" target="_blank" rel="noreferrer"&gt;this Github Issue&lt;/a&gt;. Let&amp;rsquo;s break down what happened.&lt;/p&gt;
&lt;p&gt;First, once a change was merged into the Signal Desktop snap repository, and the resulting snap was built for each of its target architectures:&lt;/p&gt;
&lt;p&gt;&lt;a href="01.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_fafbb694af97bb75.webp 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_1ccda47c0fbc34a.webp 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_2580f508fb236d67.webp 1024w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_89c072b70232a46e.webp 1320w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="2238"
height="694"
class="mx-auto my-0 rounded-md"
alt="complete ci workflow on github actions"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_37a169e3d17a36e1.png" srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_dd4a9f7b594b9d4f.png 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_37a169e3d17a36e1.png 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_de66138f407ef2a9.png 1024w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/01_hu_6f5bce1fb8f383af.png 1320w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As part of this process an issue was automatically created containing information about the new &lt;code&gt;candidate&lt;/code&gt; versions:&lt;/p&gt;
&lt;p&gt;&lt;a href="02.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_d11ec7d0d05027c9.webp 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_9ec42ca1fc2f112e.webp 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_21bad717e4953f76.webp 1024w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_2a1e4b2bd386a874.webp 1249w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="1249"
height="1260"
class="mx-auto my-0 rounded-md"
alt="example call for testing post for Signal Desktop"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_80067b83b7fe31e7.png" srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_44490b647e67e3b3.png 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_80067b83b7fe31e7.png 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02_hu_6330c88e415662a4.png 1024w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/02.png 1249w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A couple of minutes later, the bot followed up with a comment containing screenshots of the application running on the desktop:&lt;/p&gt;
&lt;p&gt;&lt;a href="03.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_17944afe0d371816.webp 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_44dd85c8159616ed.webp 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_b658abbdec44e80c.webp 946w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_b658abbdec44e80c.webp 946w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="946"
height="1458"
class="mx-auto my-0 rounded-md"
alt="Github Issue comment containing screenshots from an automated test run"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_72ccc750b515132e.png" srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_94f7e00cd1c90f0c.png 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03_hu_72ccc750b515132e.png 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03.png 946w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/03.png 946w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once I was content that the snap was working correctly, and I&amp;rsquo;d tested the revision out locally, I then issued a command to promote the snap into the &lt;code&gt;stable&lt;/code&gt; channel, where it was slowly rolled out across the user base:&lt;/p&gt;
&lt;p&gt;&lt;a href="04.png"&gt;
&lt;figure&gt;
&lt;picture
class="mx-auto my-0 rounded-md"
&gt;
&lt;source
srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_9c93800eb9b1f694.webp 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_7482b899ecead064.webp 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_a34852695c1eb548.webp 939w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_a34852695c1eb548.webp 939w
"
sizes="100vw"
type="image/webp"
/&gt;
&lt;img
width="939"
height="391"
class="mx-auto my-0 rounded-md"
alt="Github Issue comment showing revision promotion workflow"
loading="lazy" decoding="async"
src="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_ed2dc7c734c9f31d.png" srcset="https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_62f7b81e9d05693c.png 330w,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04_hu_ed2dc7c734c9f31d.png 660w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04.png 939w
,https://jnsgr.uk/2024/03/simplifying-snap-gui-testing/04.png 939w
"
sizes="100vw"
/&gt;
&lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can see examples of this working across multiple snaps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/snapcrafters/gimp/issues/260" target="_blank" rel="noreferrer"&gt;gimp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snapcrafters/discord/issues/184" target="_blank" rel="noreferrer"&gt;discord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snapcrafters/sublime-text/issues/59" target="_blank" rel="noreferrer"&gt;sublime-text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snapcrafters/sublime-merge/issues/31" target="_blank" rel="noreferrer"&gt;sublime-merge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snapcrafters/mattermost-desktop/issues/100" target="_blank" rel="noreferrer"&gt;mattermost-desktop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;We&amp;rsquo;re still in the process of refining and rolling out this process, but I hope that it will help reduce burden on maintainers over time, and result in fresher and more reliable desktop snaps in the &lt;a href="https://snapcraft.io" target="_blank" rel="noreferrer"&gt;Snap Store&lt;/a&gt;. If you&amp;rsquo;d like to get involved in Snapcrafters, reach out to me, post on the &lt;a href="https://forum.snapcraft.io/t/snapcrafters-reboot/24625" target="_blank" rel="noreferrer"&gt;Snapcraft Discourse&lt;/a&gt; or join the &lt;a href="https://matrix.to/#/#snapcrafters:matrix.org" target="_blank" rel="noreferrer"&gt;Matrix room&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Integration testing with NixOS in Github Actions</title><link>https://jnsgr.uk/2024/02/nixos-vms-in-github-actions/</link><pubDate>Sat, 10 Feb 2024 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2024/02/nixos-vms-in-github-actions/</guid><description>&lt;h2 id="introduction" class="relative group"&gt;Introduction &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#introduction" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;While in my own time I&amp;rsquo;ve tended toward NixOS over the past 18 months, in my day-to-day work for &lt;a href="https://canonical.com" target="_blank" rel="noreferrer"&gt;Canonical&lt;/a&gt; I&amp;rsquo;m required to interact with a fair few of our products - and particularly build tools.&lt;/p&gt;
&lt;p&gt;I frequently need to use some combination of &lt;a href="https://snapcraft.io/docs" target="_blank" rel="noreferrer"&gt;Snaps&lt;/a&gt;, &lt;a href="https://juju.is/docs/juju/charmed-operator" target="_blank" rel="noreferrer"&gt;Charms&lt;/a&gt; and &lt;a href="https://ubuntu.com/server/docs/rock-images/introduction" target="_blank" rel="noreferrer"&gt;Rocks&lt;/a&gt;. Each of these have their own &amp;ldquo;craft&amp;rdquo; build tools (&lt;a href="https://github.com/snapcore/snapcraft" target="_blank" rel="noreferrer"&gt;&lt;code&gt;snapcraft&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/canonical/charmcraft" target="_blank" rel="noreferrer"&gt;&lt;code&gt;charmcraft&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/canonical/rockcraft" target="_blank" rel="noreferrer"&gt;&lt;code&gt;rockcraft&lt;/code&gt;&lt;/a&gt;), which are distributed exclusively as Snap packages and thus a little tricky to consume from NixOS.&lt;/p&gt;
&lt;h2 id="the-problem" class="relative group"&gt;The Problem &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#the-problem" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Packaging the tools for Nix was a little repetitive, but not particularly difficult. They&amp;rsquo;re all built with Python, and share a common set of libraries. Testing that the packages were working correctly (i.e. could actually build software) &lt;em&gt;on NixOS&lt;/em&gt; using the &lt;em&gt;NixOS version of LXD&lt;/em&gt; in Github Actions proved more difficult.&lt;/p&gt;
&lt;p&gt;Github Actions defaults to Ubuntu as the operating system for its runners - an entirely sensible choice, but not one that was going to help me test packages could work together on NixOS.&lt;/p&gt;
&lt;p&gt;I could have hosted my own Github Actions runners to solve the problem, but I didn&amp;rsquo;t want to maintain such a deployment.&lt;/p&gt;
&lt;p&gt;For a while I relied on just testing each of the crafts locally before pushing, and the CI simply installed the Nix package manager on the runners (using the &lt;em&gt;excellent&lt;/em&gt; &lt;a href="https://github.com/DeterminateSystems/nix-installer" target="_blank" rel="noreferrer"&gt;Nix installer from Determinate Systems&lt;/a&gt;) and ensured that the build could succeed, but this left a lot to be desired - particularly when I accidentally (and somewhat inevitably) broke one of the packages.&lt;/p&gt;
&lt;h2 id="kvm-for-github-actions" class="relative group"&gt;KVM for Github Actions &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#kvm-for-github-actions" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Some time later I came across &lt;a href="https://github.blog/changelog/2023-02-23-hardware-accelerated-android-virtualization-on-actions-windows-and-linux-larger-hosted-runners/" target="_blank" rel="noreferrer"&gt;this post&lt;/a&gt; on the Github Blog, stating the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting on February 23, 2023, Actions users [&amp;hellip;] will be able to make use of hardware acceleration [&amp;hellip;].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What follows is an example of a relatively simple addition to a Github Workflow to enable KVM on Github Actions runners:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Enable KVM group perms&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#39;KERNEL==&amp;#34;kvm&amp;#34;, GROUP=&amp;#34;kvm&amp;#34;, MODE=&amp;#34;0666&amp;#34;, OPTIONS+=&amp;#34;static_node=kvm&amp;#34;&amp;#39; | sudo tee /etc/udev/rules.d/99-kvm4all.rules
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; sudo udevadm control --reload-rules
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; sudo udevadm trigger --name-match=kvm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Given the ability to relatively easily create NixOS VMs from a machine configuration, this should enable me to run a NixOS VM inside my Github Actions runners, and use that VM to run end to end tests of my craft packages.&lt;/p&gt;
&lt;p&gt;After some quick tests, I confirmed that the above snippet worked just fine on the freely available runners that are assigned to public projects. After &lt;a href="https://hachyderm.io/@jnsgruk/111449289662026017" target="_blank" rel="noreferrer"&gt;tooting excitedly&lt;/a&gt; about this, it was also picked up by the folks at Determinate Systems who &lt;a href="https://octodon.social/@grahamc/111450168028125913" target="_blank" rel="noreferrer"&gt;promptly added support&lt;/a&gt; for this in their Nix install Github Action - enabling the feature by default.&lt;/p&gt;
&lt;h2 id="building-vms-with-nix" class="relative group"&gt;Building VMs with Nix &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#building-vms-with-nix" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;A really nice feature of NixOS that I discovered relatively late, is that given a NixOS machine configuration &lt;a href="https://gist.github.com/FlakM/0535b8aa7efec56906c5ab5e32580adf" target="_blank" rel="noreferrer"&gt;it&amp;rsquo;s trivial to build a virtual machine&lt;/a&gt; image for that configuration. This has the nice property that one can actually boot a VM-equivalent of any previously defined machines. You could, for example, boot a VM-equivalent of my laptop with the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix run github:jnsgruk/nixos-config#nixosConfigurations.freyja.config.system.build.vm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;In order to test my craft tools, I needed a relatively simple NixOS VM that had LXD enabled, and my craft tools installed. My test VM &lt;a href="https://github.com/jnsgruk/crafts-flake/blob/f63f315ee2832a112e0777b8af575297c8c9e62d/test/vm.nix" target="_blank" rel="noreferrer"&gt;configuration&lt;/a&gt; looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;modulesPath&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flake&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# A nice helper that handles creating the VM launch script, which in turn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ensures the disk image is created as required, and QEMU is launched&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# with sensible parameters.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="n"&gt;modulesPath&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/virtualisation/qemu-vm.nix&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Define the version of NixOS and the architecture.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stateVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;23.11&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostPlatform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;x86_64-linux&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# This overlay is provided by the crafts-flake, and ensures that&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# &amp;#39;pkgs.snapcraft&amp;#39;, &amp;#39;pkgs.charmcraft&amp;#39;, &amp;#39;pkgs.rockcraft&amp;#39; all resolve to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# the packages in the flake.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;nixpkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;flake&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outputs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overlay&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# These values are tuned such that the VM performs on Github Actions runners.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;virtualisation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;forwardPorts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;host&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2222&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;guest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;memorySize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5120&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;diskSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10240&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Configure the root user without password and enable SSH.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# This VM will only ever be used in short-lived testing environments with&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# no inbound networking permitted, so there is minimal (if any) risk.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# If you put this VM on the internet, you can keep the pieces! :)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;networking&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;firewall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openssh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PermitRootLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extraUsers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Ensure that LXD is installed, and started on boot.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;virtualisation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lxd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Include the `craft-test` script, ensuring the craft apps are installed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# and included in its PATH.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;systemPackages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkgs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeShellApplication&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;craft-test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;runtimeInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;unixtools&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xxd&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;snapcraft&lt;/span&gt; &lt;span class="n"&gt;charmcraft&lt;/span&gt; &lt;span class="n"&gt;rockcraft&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;builtins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readFile&lt;/span&gt; &lt;span class="sr"&gt;./craft-test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Anybody can build and launch this VM trivially:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix run github:jnsgruk/crafts-flake#testVM
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="writing-a-github-workflow" class="relative group"&gt;Writing a Github workflow &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#writing-a-github-workflow" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;All the building blocks are in place! I wanted to keep the actual workflow definition for the tests as clean and understandable as possible, so I put together the &lt;a href="https://github.com/jnsgruk/crafts-flake/blob/f63f315ee2832a112e0777b8af575297c8c9e62d/test/craft-test" target="_blank" rel="noreferrer"&gt;&lt;code&gt;craft-test&lt;/code&gt;&lt;/a&gt; script as a small helper which automates the building of real artefacts. An example invocation might be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash craft-test snapcraft
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;On each invocation, the script creates temporary directory, clones some representative build files for the selected craft tool, and launches the craft. The repos it uses for the representative packages are hard-coded for each craft for now.&lt;/p&gt;
&lt;p&gt;I wrote one more &lt;a href="https://github.com/jnsgruk/crafts-flake/blob/f63f315ee2832a112e0777b8af575297c8c9e62d/test/vm-exec" target="_blank" rel="noreferrer"&gt;small helper script&lt;/a&gt; to simplify connecting to the VM with the required parameters. It&amp;rsquo;s a wrapper around &lt;code&gt;ssh&lt;/code&gt; and &lt;code&gt;sshpass&lt;/code&gt; that&amp;rsquo;s hard-coded with the credentials of the test VM (don&amp;rsquo;t @ me!), and executes commands over SSH in the test VM. Using this script, one can &lt;code&gt;bash vm-exec -- craft-test snapcraft&lt;/code&gt; and the &lt;code&gt;craft-test&lt;/code&gt; script will be executed over SSH in the VM.&lt;/p&gt;
&lt;p&gt;With all that said and done, the resulting &lt;a href="https://github.com/jnsgruk/crafts-flake/blob/f63f315ee2832a112e0777b8af575297c8c9e62d/.github/workflows/test.yaml" target="_blank" rel="noreferrer"&gt;workflow&lt;/a&gt; is pleasingly simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# ...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;package&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;charmcraft&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rockcraft&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;snapcraft&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout flake&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Install nix&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DeterminateSystems/nix-installer-action@v9&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Build and run the test VM&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; nix run .#testVm -- -daemonize -display none&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Test ${{ matrix.package }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; nix run .#testVmExec -- craft-test ${{ matrix.package }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;A separate job is run for each of the crafts, and a real artefact is built in each, giving reasonable confidence that the consumers of my flake will be successful when building snaps, rocks and charms natively on NixOS. A successful run can be seen &lt;a href="https://github.com/jnsgruk/crafts-flake/actions/runs/7772604925" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="summary" class="relative group"&gt;Summary &lt;span class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100"&gt;&lt;a class="group-hover:text-primary-300 dark:group-hover:text-neutral-700" style="text-decoration-line: none !important;" href="#summary" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;In this article we&amp;rsquo;ve covered:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enabling KVM on Github Runners&lt;/li&gt;
&lt;li&gt;Building NixOS VMs using Flakes&lt;/li&gt;
&lt;li&gt;Booting NixOS VMs in Github Actions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;rsquo;d like to build snaps, rocks or charms and you&amp;rsquo;re running NixOS, you can run the tools individually from my flake:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run charmcraft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix run github:jnsgruk/crafts-flake#charmcraft
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run rockcraft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix run github:jnsgruk/crafts-flake#rockcraft
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run snapcraft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix run github:jnsgruk/crafts-flake#snapcraft
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Or you can check out the &lt;a href="https://github.com/jnsgruk/crafts-flake" target="_blank" rel="noreferrer"&gt;README&lt;/a&gt; for instructions on how to integrate into your Nix config using overlays!&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all for now! 🤓&lt;/p&gt;</description></item></channel></rss>