<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Rockcraft on Jon Seager</title><link>https://jnsgr.uk/tags/rockcraft/</link><description>Recent content in Rockcraft on Jon Seager</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 21 Jul 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://jnsgr.uk/tags/rockcraft/index.xml" rel="self" type="application/rss+xml"/><item><title>Crafting Your Software</title><link>https://jnsgr.uk/2025/07/crafting-your-software/</link><pubDate>Mon, 21 Jul 2025 00:00:00 +0000</pubDate><guid>https://jnsgr.uk/2025/07/crafting-your-software/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article was originally posted &lt;a href="https://discourse.ubuntu.com/t/crafting-your-software/64809" target="_blank" rel="noreferrer"&gt;on the Ubuntu Discourse&lt;/a&gt;, and is reposted here. I welcome comments and further discussion in that thread.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Packaging software is notoriously tricky. Every language, framework, and build system has its quirks, and the variety of artifact types — from Debian packages to OCI images and cloud images — only adds to the complexity.&lt;/p&gt;
&lt;p&gt;Over the past decade, Canonical has been refining a family of tools called “crafts” to tame this complexity and make building, testing, and releasing software across ecosystems much simpler.&lt;/p&gt;
&lt;p&gt;The journey began on 23rd June 2015 when the first commit was made to &lt;a href="https://github.com/canonical/snapcraft" target="_blank" rel="noreferrer"&gt;Snapcraft&lt;/a&gt;, the tool used to build Snap packages. For years, Snapcraft was &lt;em&gt;the only&lt;/em&gt; craft in our portfolio, but in the last five years, we’ve generalized much of what we learned about building, testing, and releasing software into a number of &amp;ldquo;crafts&amp;rdquo; for building different artifact types.&lt;/p&gt;
&lt;p&gt;Last month, I &lt;a href="https://jnsgr.uk/2025/06/introducing-debcrafters/" target="_blank" rel="noreferrer"&gt;outlined&lt;/a&gt; Canonical&amp;rsquo;s plan to build &lt;code&gt;debcraft&lt;/code&gt; as a next-generation way to build Debian packages. In this post I&amp;rsquo;ll talk about what exactly &lt;em&gt;makes&lt;/em&gt; a craft, and why you should bother learning to use them.&lt;/p&gt;
&lt;h2 id="software-build-lifecycle" class="relative group"&gt;Software build lifecycle &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="#software-build-lifecycle" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;At the heart of all our crafts is &lt;a href="https://canonical-craft-parts.readthedocs-hosted.com/latest/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;craft-parts&lt;/code&gt;&lt;/a&gt;, which according to the &lt;a href="https://canonical-craft-parts.readthedocs-hosted.com/latest/" target="_blank" rel="noreferrer"&gt;documentation&lt;/a&gt; &amp;ldquo;provides a mechanism to obtain data from different sources, process it in various ways, and prepare a filesystem sub-tree suitable for packaging&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Put simply, &lt;code&gt;craft-parts&lt;/code&gt; gives developers consistent tools to fetch, build, and prepare software from any ecosystem for packaging into various formats.&lt;/p&gt;
&lt;h3 id="lifecycle-stages" class="relative group"&gt;Lifecycle stages &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="#lifecycle-stages" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;Every part has a minimum of four lifecycle stages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PULL&lt;/code&gt;: source code or binary artifacts, along with dependencies are pulled from various sources&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BUILD&lt;/code&gt;: software is built automatically by a &lt;code&gt;plugin&lt;/code&gt;, or a set of custom steps defined by the developer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STAGE&lt;/code&gt;: select outputs from the &lt;code&gt;BUILD&lt;/code&gt; phase are copied to a unified staging area for all parts&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PRIME&lt;/code&gt;: files from the staging area are copied to the priming area for use in the final artifact.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;STAGE&lt;/code&gt; and &lt;code&gt;PRIME&lt;/code&gt; steps are similar, except that &lt;code&gt;PRIME&lt;/code&gt; only happens after &lt;em&gt;all&lt;/em&gt; parts of the build are staged. Additionally, &lt;code&gt;STAGE&lt;/code&gt; provides the opportunity for parts to build/supply dependencies for other parts, but that might not be required in the final artifact.&lt;/p&gt;
&lt;h3 id="lifecycle-in-the-cli" class="relative group"&gt;Lifecycle in the CLI &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="#lifecycle-in-the-cli" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;The lifecycle stages aren’t just in the build recipe, they’re also first-class citizens in each craft’s CLI, thanks to the &lt;a href="https://github.com/canonical/craft-cli" target="_blank" rel="noreferrer"&gt;craft-cli&lt;/a&gt; library. This ensures a consistent command-line experience across all craft tools.&lt;/p&gt;
&lt;p&gt;Take the following examples:&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;/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 the full process including PULL, BUILD, STAGE, PRIME and then pack the final artifact&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;snapcraft pack
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;charmcraft pack
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rockcraft pack
&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 the process up to the end of the STAGE step&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rockcraft stage
&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 the process up to the PRIME step&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;charmcraft prime
&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;This design feature supports a smoother iterative development and debugging workflow for building and testing software artifacts.&lt;/p&gt;
&lt;h3 id="part-definition" class="relative group"&gt;Part definition &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="#part-definition" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;The &lt;code&gt;parts&lt;/code&gt; of a build vary in complexity - some require two-three trivial lines, others require detailed specification of dependencies, build flags, environment variables and steps. The best way to understand the flexibility of this system is by looking at some examples.&lt;/p&gt;
&lt;p&gt;First, consider this (annotated) example from my &lt;a href="https://github.com/jnsgruk/icloudpd-snap/blob/beb2c7d2539547dfff5d4fd99687573d75597633/snap/snapcraft.yaml" target="_blank" rel="noreferrer"&gt;icloudpd snap&lt;/a&gt;:&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;/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;icloudpd&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="c"&gt;# Use the &amp;#39;python&amp;#39; plugin to build the&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="c"&gt;# software. This takes care of identifying&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="c"&gt;# Python package dependencies, building the wheel&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="c"&gt;# and ensuring the project&amp;#39;s dependencies are staged&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="c"&gt;# appropriately.&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;plugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;python&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="c"&gt;# Fetch the project from Github, using the tag the matches&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="c"&gt;# the version of the project.&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;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://github.com/icloud-photos-downloader/icloud_photos_downloader&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;source-tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v$SNAPCRAFT_PROJECT_VERSION&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;source-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;git&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;This spec is everything required to fetch, build and stage the important bits required to run the software - in this case a Python wheel and its dependencies.&lt;/p&gt;
&lt;p&gt;Some projects might require more set up, perhaps an additional package is required or a specific version of a dependency is needed. Let&amp;rsquo;s take a look at a slightly more complex example taken from my &lt;a href="https://github.com/jnsgruk/zinc-k8s-operator/blob/5516be2c50e52b33742c674f266c8dfca55e6edf/rockcraft.yaml#L90C3-L100C20" target="_blank" rel="noreferrer"&gt;zinc-k8s-operator&lt;/a&gt; project:&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;/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;kube-log-runner&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="c"&gt;# Use the &amp;#39;go&amp;#39; plugin to build the software.&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;plugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;go&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="c"&gt;# Fetch the source code from Git at the &amp;#39;v0.17.0&amp;#39; tag.&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;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://github.com/kubernetes/release&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;source-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;git&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;source-tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v0.17.8&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="c"&gt;# Change to the specified sub-directory for the build.&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;source-subdir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;images/build/go-runner&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="c"&gt;# Install the following snaps in the build environment.&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;build-snaps&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="l"&gt;go/1.20/stable&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="c"&gt;# Set the following environment variables in the build&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="c"&gt;# environment.&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;build-environment&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;CGO_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&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;GOOS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;linux&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;This instructs &lt;code&gt;rockcraft&lt;/code&gt; to fetch a Git repository at a particular tag, change into the sub-directory &lt;code&gt;images/build/go-runner&lt;/code&gt;, then build the software using the &lt;code&gt;go&lt;/code&gt; plugin. It also specifies that the build required the &lt;code&gt;go&lt;/code&gt; snap from the &lt;code&gt;1.20/stable&lt;/code&gt; track, and sets some environment variables. That&amp;rsquo;s a lot of result for not much YAML. The end result of this is a single binary that&amp;rsquo;s &amp;ldquo;staged&amp;rdquo; and ready to be placed (in this case) into a &lt;a href="https://documentation.ubuntu.com/rockcraft/en/latest/explanation/rocks/" target="_blank" rel="noreferrer"&gt;Rock&lt;/a&gt; (Canonical&amp;rsquo;s name for OCI images).&lt;/p&gt;
&lt;p&gt;And the best part: this exact definition can be used in a &lt;code&gt;rockcraft.yaml&lt;/code&gt; when building a Rock, a &lt;code&gt;snapcraft.yaml&lt;/code&gt; when building a Snap, a &lt;code&gt;charmcraft.yaml&lt;/code&gt; when building a Charm, etc.&lt;/p&gt;
&lt;p&gt;The plugin system is extensive: at the time of writing there are &lt;a href="https://canonical-craft-parts.readthedocs-hosted.com/latest/reference/plugins/" target="_blank" rel="noreferrer"&gt;22 supported plugins&lt;/a&gt;, including &lt;code&gt;go&lt;/code&gt;, &lt;code&gt;maven&lt;/code&gt;, &lt;code&gt;uv&lt;/code&gt;, &lt;code&gt;meson&lt;/code&gt; and more. If your build system of choice isn&amp;rsquo;t supported you can specify manual steps, giving you as much flexibility as you need:&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;/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;wasi-sdk&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="c"&gt;# There is no appropriate plugin for this part, so set&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="c"&gt;# it to &amp;#39;nil&amp;#39; and we&amp;#39;ll specify our own build process&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="c"&gt;# using &amp;#39;override-build&amp;#39;.&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;plugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nil&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="c"&gt;# In this recipe, a previous part named &amp;#39;clang&amp;#39; is&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="c"&gt;# required to build before attempting to build this&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="c"&gt;# part.&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;after&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="l"&gt;clang&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="c"&gt;# Specify any `apt` packages required in the build&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="c"&gt;# environment.&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;build-packages&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="l"&gt;wget&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="c"&gt;# Set some environment variables for the build&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="c"&gt;# environment.&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;build-environment&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;WASI_BRANCH&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;15&amp;#34;&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;WASI_RELEASE&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;15.0&amp;#34;&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="c"&gt;# Define how to pull the software manually.&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;override-pull&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; ROOT=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-$WASI_BRANCH
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; wget $ROOT/wasi-sysroot-$WASI_RELEASE.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; wget $ROOT/libclang_rt.builtins-wasm32-wasi-$WASI_RELEASE.tar.gz&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="c"&gt;# Define how to &amp;#39;build&amp;#39; the software manually&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;override-build&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; craftctl default
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; tar -C $CRAFT_STAGE -xf wasi-sysroot-$WASI_RELEASE.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; tar -C $CRAFT_STAGE/usr/lib/clang/* -xf libclang_rt.builtins-wasm32-wasi-$WASI_RELEASE.tar.gz&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="c"&gt;# Don&amp;#39;t prime anything for inclusion in the&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="c"&gt;# final artifact; this part is only used for&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="c"&gt;# another part&amp;#39;s build process.&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;override-prime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&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;Here, multiple stages of the lifecycle are overridden using &lt;code&gt;override-build&lt;/code&gt;, &lt;code&gt;override-pull&lt;/code&gt; and &lt;code&gt;override-stage&lt;/code&gt;, and we see &lt;code&gt;craftctl default&lt;/code&gt; for the first time, which instructs snapcraft to do whatever it would have done prior being overridden, but allows the developer to provide additional steps either before or after the default actions.&lt;/p&gt;
&lt;h2 id="isolated-build-environments" class="relative group"&gt;Isolated build environments &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="#isolated-build-environments" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Even once a recipe for building software is defined, preparing machines to build software can be painful. Different major versions of the same OS might have varying package availability, your team might run completely different operating systems, and you might have limited image availability in your CI environment.&lt;/p&gt;
&lt;p&gt;The crafts solve this with build &amp;ldquo;backends&amp;rdquo;. Currently the crafts can use &lt;a href="https://canonical.com/lxd" target="_blank" rel="noreferrer"&gt;LXD&lt;/a&gt; or &lt;a href="https://canonical.com/multipass" target="_blank" rel="noreferrer"&gt;Multipass&lt;/a&gt; to create isolated build environments, which makes it work nicely on Linux, macOS and Windows. This functionality is handled automatically by the crafts through the &lt;a href="https://canonical-craft-providers.readthedocs-hosted.com/en/latest/" target="_blank" rel="noreferrer"&gt;&lt;code&gt;craft-providers&lt;/code&gt;&lt;/a&gt; library. The &lt;code&gt;craft-providers&lt;/code&gt; library provides uniform interfaces for creating build environments, configuring base images and executing builds.&lt;/p&gt;
&lt;p&gt;This means if you can run &lt;code&gt;snapcraft pack&lt;/code&gt; on your machine, your teammates can also run the same command without worrying about installing the right dependencies or polluting their machines with software and temporary files that might result from the build.&lt;/p&gt;
&lt;p&gt;One of my favourite features of this setup is the ability to drop into a shell inside the build environment automatically on a few different conditions:&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;/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;# Drop into a shell if any part of the build fails.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;snapcraft pack --debug
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Drop into a shell after the build stage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rockcraft build --shell-after
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Drop to a shell in lieu of the prime stage.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;snapcraft prime --shell
&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;This makes troubleshooting a failing build much simpler, while allowing the developer to maintain a clean separation between the build environment and their local machine. Should the build environment ever become polluted, or otherwise difficult to work with, you can always start from a clean slate with &lt;code&gt;snapcraft|rockcraft|charmcraft clean&lt;/code&gt;. Each build machine is constructed using a cached &lt;code&gt;build-base&lt;/code&gt;, which contains all the baseline packages required by the craft - so recreating the build environment for a specific package only requires that base to be cloned and augmented with project specific concerns - making the process faster.&lt;/p&gt;
&lt;h2 id="saving-space" class="relative group"&gt;Saving space &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="#saving-space" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;When packaging any kind of software, a common concern is the size of the artifact. This might be because you&amp;rsquo;re building an OCI-image that is pulled thousands of times a day as part of a major SaaS deployment, or maybe it&amp;rsquo;s a Snap for an embedded device running &lt;a href="https://ubuntu.com/core" target="_blank" rel="noreferrer"&gt;Ubuntu Core&lt;/a&gt; with a limited flash. In the container world, &amp;ldquo;&lt;a href="https://github.com/GoogleContainerTools/distroless" target="_blank" rel="noreferrer"&gt;distroless&lt;/a&gt;&amp;rdquo; became a popular way to solve this problem - essentially popularising the practice of shipping the barest minimum in a container image, eschewing much of the traditional Unix FHS.&lt;/p&gt;
&lt;p&gt;The parts mechanism has provided a way of &amp;ldquo;filtering&amp;rdquo; what is staged or primed into a final artifact from the start, which already gave developers autonomy to choose exactly what went into their builds.&lt;/p&gt;
&lt;p&gt;In addition to this, Canonical built &amp;ldquo;&lt;a href="https://documentation.ubuntu.com/chisel/en/latest/tutorial/getting-started/" target="_blank" rel="noreferrer"&gt;chisel&lt;/a&gt;&amp;rdquo;, which extends the distroless concept beyond containers to any kind of artifact. With &lt;code&gt;chisel&lt;/code&gt;, developers can slice out just the binaries, libraries, and configuration files they need from the Ubuntu Archive, enabling ultra-small packages without losing the robustness of Ubuntu’s ecosystem.&lt;/p&gt;
&lt;p&gt;We later launched &lt;a href="https://ubuntu.com/blog/chiseled-ubuntu-containers-openjre" target="_blank" rel="noreferrer"&gt;Chiseled JRE&lt;/a&gt; containers, and there are numerous other Rocks that utilise &lt;code&gt;chisel&lt;/code&gt; to provide a balance between shipping &lt;em&gt;tiny&lt;/em&gt; container images, while benefiting from the huge selection and quality of software in the Ubuntu Archive.&lt;/p&gt;
&lt;p&gt;Because the crafts are all built on a common platform, they now all have the ability to use &amp;ldquo;slices&amp;rdquo; from &lt;a href="https://github.com/canonical/chisel-releases" target="_blank" rel="noreferrer"&gt;chisel-releases&lt;/a&gt;, which enables a greater range of use-cases where artifact size is a primary concern. Slices are community maintained, and specified in simple to understand YAML files. You can see the list of available slices for the most recent Ubuntu release (25.04 Plucky Puffin) &lt;a href="https://github.com/canonical/chisel-releases/tree/ubuntu-25.04/slices" target="_blank" rel="noreferrer"&gt;on GitHub&lt;/a&gt;, and further documentation on slices and how they&amp;rsquo;re used in the &lt;a href="https://documentation.ubuntu.com/chisel/en/latest/explanation/mode-of-operation/" target="_blank" rel="noreferrer"&gt;Chisel docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="multi-architecture-builds" class="relative group"&gt;Multi-architecture builds &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="#multi-architecture-builds" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Ubuntu supports six major architectures at the time of writing (&lt;code&gt;amd64&lt;/code&gt;, &lt;code&gt;arm64&lt;/code&gt;, &lt;code&gt;armhf&lt;/code&gt;, &lt;code&gt;ppc64le&lt;/code&gt;, &lt;code&gt;s390x&lt;/code&gt;, &lt;code&gt;riscv64&lt;/code&gt;), and all of our crafts have first-class support for each of them. This functionality is provided primarily by the &lt;a href="https://github.com/canonical/craft-platforms" target="_blank" rel="noreferrer"&gt;craft-platforms&lt;/a&gt; library, and supported by the &lt;a href="https://github.com/canonical/craft-grammar" target="_blank" rel="noreferrer"&gt;craft-grammar&lt;/a&gt; library, which enables more complex definitions where builds may have different steps or requirements for different architectures.&lt;/p&gt;
&lt;p&gt;At a high-level, each artifact defines which architectures or platforms it is built &lt;em&gt;for&lt;/em&gt;, and which it is built &lt;em&gt;on&lt;/em&gt;. These are often, but not always, the same. 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;span class="lnt"&gt;2
&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;platforms&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;amd64&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;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This is shorthand for &amp;ldquo;build the project on &lt;code&gt;amd64&lt;/code&gt; for &lt;code&gt;amd64&lt;/code&gt;&amp;rdquo;, but in a different example taken from a &lt;code&gt;charmcraft.yaml&lt;/code&gt;&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;/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;platforms&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;all&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;build-on&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="l"&gt;amd64]&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;build-for&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="l"&gt;all]&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;In this case the software is built on &lt;code&gt;amd64&lt;/code&gt;, but can run on any of the supported architectures - this can happen with all-Python wheels, &lt;code&gt;bash&lt;/code&gt; scripts and other interpreted languages which don&amp;rsquo;t link platform-specific libraries.&lt;/p&gt;
&lt;p&gt;In some build processes, the process or dependencies might differ per-architecture, which is where &lt;code&gt;craft-grammar&lt;/code&gt; comes in, enabling expressions such as (taken from &lt;a href="https://github.com/canonical/mesa-core22/blob/86060bf66e70d0f5d421fe818d61cdc0f18f9b31/snap/snapcraft.yaml#L265C3-L280C46" target="_blank" rel="noreferrer"&gt;GitHub&lt;/a&gt;):&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;/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;fit-image&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="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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build-packages&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="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="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;wget&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="l"&gt;libjson-c-dev:${CRAFT_ARCH_BUILD_FOR}&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="l"&gt;libcryptsetup-dev:${CRAFT_ARCH_BUILD_FOR}&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="c"&gt;# Only use the following build packages when building for armhf&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;to armhf&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="l"&gt;binutils-arm-linux-gnueabi&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="l"&gt;gcc-arm-linux-gnueabihf&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="l"&gt;pkgconf:armhf&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="c"&gt;# When building for arm64, use a different set&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;to arm64&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="c"&gt;# Dependencies for building *for* arm64 *on* amd64!&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;on amd64&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="l"&gt;gcc-aarch64-linux-gnu&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="l"&gt;pkgconf:arm64&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;on arm64&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="l"&gt;gcc&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;Being able to define how to build on different architectures is only half of the battle, though. It&amp;rsquo;s one thing to define &lt;em&gt;how&lt;/em&gt; to build software on an &lt;code&gt;s390x&lt;/code&gt; machine but few developers have mainframes handy to actually &lt;em&gt;run&lt;/em&gt; the build! This is where the crafts&amp;rsquo; &lt;code&gt;remote-build&lt;/code&gt; capability comes in. The &lt;code&gt;remote-build&lt;/code&gt; command sends builds to Canonical&amp;rsquo;s build farm, which has native support for all of Ubuntu&amp;rsquo;s supported architectures. This is built into all of our crafts, and is triggered with &lt;code&gt;snapcraft remote-build&lt;/code&gt;, &lt;code&gt;rockcraft remote-build&lt;/code&gt;, etc.&lt;/p&gt;
&lt;p&gt;Remote builds are a lifeline for publishers and communities who need to reach a larger audience, but can&amp;rsquo;t necessarily get their own build farm together. One example of this is &lt;a href="https://snapcrafters.org/" target="_blank" rel="noreferrer"&gt;Snapcrafters&lt;/a&gt;, a community-driven organisation that packages popular software as Snaps, who use &lt;code&gt;remote-build&lt;/code&gt; to drive multi-architecture builds from &lt;a href="https://github.com/snapcrafters/ci" target="_blank" rel="noreferrer"&gt;GitHub Actions&lt;/a&gt; as part of their publishing workflow (as seen &lt;a href="https://github.com/snapcrafters/helm/actions/runs/16166314558" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/snapcrafters/terraform/actions/runs/15607983328" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt; for example).&lt;/p&gt;
&lt;h2 id="unified-testing-framework" class="relative group"&gt;Unified testing framework &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="#unified-testing-framework" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Testing is often the missing piece in build tools: developers are forced to rely on separate CI systems or ad-hoc scripts to verify their artifacts. To close this gap, we’re introducing a unified &lt;code&gt;test&lt;/code&gt; sub-command in the crafts.&lt;/p&gt;
&lt;p&gt;We recently added the &lt;code&gt;test&lt;/code&gt; sub-command to our crafts as an experimental (for now!) feature. Under the hood, &lt;code&gt;craft test&lt;/code&gt; will introduce a new lifecycle stage (&lt;code&gt;TEST&lt;/code&gt;). The enables packagers of any artifact type to specify how that artifact should be tested using a common framework across artifact types.&lt;/p&gt;
&lt;p&gt;Craft&amp;rsquo;s testing capability is powered by &lt;a href="https://github.com/canonical/spread" target="_blank" rel="noreferrer"&gt;spread&lt;/a&gt;, a convenient full-system task distribution system. Spread was built to simplify the massive number of integration tests run for the &lt;a href="https://github.com/canonical/snapd" target="_blank" rel="noreferrer"&gt;snapd&lt;/a&gt; project. It enables developers to specify tests in a simple language, and distribute them concurrently to any infrastructure they have available.&lt;/p&gt;
&lt;p&gt;This enables a developer to define tests and test infrastructure, and make it trivial to run the same tests locally, or remotely on cloud infrastructure. This can really speed up the development process - preventing developers from needing to wait on CI runners to spin up and test their code while iterating, they can run the very same integration tests locally using &lt;code&gt;craft test&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are lots of fine details to &lt;code&gt;spread&lt;/code&gt;, and the team is working on artifact-specific abstractions for the crafts that will make testing &lt;em&gt;delightful&lt;/em&gt;. Imagine maintaining the Snap for a GUI application, and being able to enact the following workflow:&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;/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;# Pull the repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/some-gui-app/snap &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; snap
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Make some changes, perhaps fix a bug&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vim snap/snapcraft.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Build the snap, and run the integration tests.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# These tests might include spinning up a headless&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# graphical VM, which actually installs and runs&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 snap, and interacts with it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;snapcraft &lt;span class="nb"&gt;test&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;By integrating a common testing tool into the build tooling, the Starcraft team will be able to curate unique testing experiences for each kind of artifact. A snap might need a headless graphical VM, where an OCI-image simply requires a container runtime, but the &lt;code&gt;spread&lt;/code&gt; underpinnings allow a common test-definition language for each.&lt;/p&gt;
&lt;p&gt;There are a couple of examples of this in the wild already:&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;/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 charmcraft&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo snap install --classic charmcraft
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Clone the repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/jnsgruk/zinc-k8s-operator
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; zinc-k8s-operator
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# List the available tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;charmcraft &lt;span class="nb"&gt;test&lt;/span&gt; --list lxd:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Run the integration testing suite, spinning up&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 small VM, inside which is a full Kubernetes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# instance, with a Juju controller bootstrapped.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# From here the charm will be deployed and tested 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;# ensure it&amp;#39;s integrations with the observability&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# stack and ingress charms are functioning correctly.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;charmcraft &lt;span class="nb"&gt;test&lt;/span&gt; -v lxd:ubuntu-24.04:tests/spread/observability-relations:juju_3_6
&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;The test above is powered by this &lt;a href="https://github.com/jnsgruk/zinc-k8s-operator/blob/main/spread.yaml" target="_blank" rel="noreferrer"&gt;spread.yaml&lt;/a&gt;, and this &lt;a href="https://github.com/jnsgruk/zinc-k8s-operator/blob/5516be2c50e52b33742c674f266c8dfca55e6edf/tests/spread/observability-relations/task.yaml" target="_blank" rel="noreferrer"&gt;test definition&lt;/a&gt;. With a little bit of &lt;a href="https://github.com/jnsgruk/zinc-k8s-operator/blob/5516be2c50e52b33742c674f266c8dfca55e6edf/.github/workflows/build-and-test.yaml#L80-L129" target="_blank" rel="noreferrer"&gt;work&lt;/a&gt;, it&amp;rsquo;s also possible to integrate &lt;code&gt;spread&lt;/code&gt; with GitHub matrix actions, giving you one GitHub job per &lt;code&gt;spread&lt;/code&gt; test - as seen &lt;a href="https://github.com/jnsgruk/zinc-k8s-operator/actions/runs/15638336939" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can see a similar example in our &lt;a href="https://github.com/canonical/postgresql-snap/tree/7e6ee6d3148c20309cc7067dc40520e208f862e5/spread/tests" target="_blank" rel="noreferrer"&gt;PostgreSQL Snap test suite&lt;/a&gt;, and we&amp;rsquo;ll be adding more and more of this kind of test across our Rock, Snap, Charm, Image and Deb portfolio.&lt;/p&gt;
&lt;p&gt;There is work to do, but I&amp;rsquo;m really excited about bringing a common testing framework to the crafts which should make the testing of all kinds of artifacts more consistent and easier to integrate across teams and systems.&lt;/p&gt;
&lt;h2 id="crafting-the-crafts" class="relative group"&gt;Crafting the crafts &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="#crafting-the-crafts" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;As the portfolio expanded from &lt;code&gt;snapcraft&lt;/code&gt;, to &lt;code&gt;charmcraft&lt;/code&gt;, to &lt;code&gt;rockcraft&lt;/code&gt; and is now expanding further to &lt;code&gt;debcraft&lt;/code&gt; and &lt;code&gt;imagecraft&lt;/code&gt; it was clear that we&amp;rsquo;d need a way to make it easy to build crafts for different artifacts, while being rigorous about consistency across the tools. A couple of years ago, the team built the &lt;a href="https://github.com/canonical/craft-application" target="_blank" rel="noreferrer"&gt;craft-application&lt;/a&gt; base library, which now forms the foundation of all our crafts.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;craft-application&lt;/code&gt; library combines many of the existing libraries that were in use across the crafts (listed below), providing a consistent base upon which artifact-specific logic can be built. The allows craft developers to spend less time implementing CLI details, &lt;code&gt;parts&lt;/code&gt; lifecycles and store interactions, and more time on curating a great experience for the maintainers of their artifact type.&lt;/p&gt;
&lt;p&gt;For the curious, &lt;code&gt;craft-application&lt;/code&gt; builds upon the following libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-archives" target="_blank" rel="noreferrer"&gt;craft-archives&lt;/a&gt;: manages interactions with &lt;code&gt;apt&lt;/code&gt; package repositories&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-cli" target="_blank" rel="noreferrer"&gt;craft-cli&lt;/a&gt;: CLI client builder that follows the Canonical&amp;rsquo;s CLI guidelines&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-parts" target="_blank" rel="noreferrer"&gt;craft-parts&lt;/a&gt;: obtain, process, and organize data sources into deployment-ready filesystems.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-grammar" target="_blank" rel="noreferrer"&gt;craft-grammar&lt;/a&gt;: advanced description grammar for parts&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-providers" target="_blank" rel="noreferrer"&gt;craft-providers&lt;/a&gt;: interface for instantiating and executing builds for a variety of target environments&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-platforms" target="_blank" rel="noreferrer"&gt;craft-platforms&lt;/a&gt;: manage target platforms and architectures for craft applications&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-store" target="_blank" rel="noreferrer"&gt;craft-store&lt;/a&gt;: manage interactions with Canonical&amp;rsquo;s software stores&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/canonical/craft-artifacts" target="_blank" rel="noreferrer"&gt;craft-artifacts&lt;/a&gt;: pack artifacts for craft applications&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="examples-and-docs" class="relative group"&gt;Examples and docs &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="#examples-and-docs" aria-label="Anchor"&gt;#&lt;/a&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;Before I leave you, I wanted to reference a few &lt;code&gt;*craft.yaml&lt;/code&gt; examples, and link to the documentation for each of the crafts, where you&amp;rsquo;ll find the canonical (little c!) truth on each tool.&lt;/p&gt;
&lt;p&gt;You can find documentation for the crafts below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://documentation.ubuntu.com/snapcraft/stable/" target="_blank" rel="noreferrer"&gt;Snapcraft docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://canonical-charmcraft.readthedocs-hosted.com/stable/" target="_blank" rel="noreferrer"&gt;Charmcraft docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://documentation.ubuntu.com/rockcraft/en/stable/" target="_blank" rel="noreferrer"&gt;Rockcraft docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://canonical-robotics.readthedocs-hosted.com/en/latest/tutorials/" target="_blank" rel="noreferrer"&gt;Robotics / Snapcraft tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And some example recipes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Snap: &lt;code&gt;icloudpd&lt;/code&gt; - &lt;a href="https://github.com/jnsgruk/icloudpd-snap/blob/main/snap/snapcraft.yaml" target="_blank" rel="noreferrer"&gt;snapcraft.yaml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Snap: &lt;code&gt;parca-agent&lt;/code&gt; - &lt;a href="https://github.com/parca-dev/parca-agent/blob/main/snap/snapcraft.yaml" target="_blank" rel="noreferrer"&gt;snapcraft.yaml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Snap: &lt;code&gt;signal-desktop&lt;/code&gt; - &lt;a href="https://github.com/snapcrafters/signal-desktop/blob/candidate/snap/snapcraft.yaml" target="_blank" rel="noreferrer"&gt;snapcraft.yaml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Charm: &lt;code&gt;ubuntu-manpages-operator&lt;/code&gt; - &lt;a href="https://github.com/canonical/ubuntu-manpages-operator/blob/main/charmcraft.yaml" target="_blank" rel="noreferrer"&gt;charmcraft.yaml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rock: &lt;code&gt;grafana&lt;/code&gt; - &lt;a href="https://github.com/canonical/grafana-rock/blob/main/11.4.0/rockcraft.yaml" target="_blank" rel="noreferrer"&gt;rockcraft.yaml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Rock: &lt;code&gt;temporal-server&lt;/code&gt; - &lt;a href="https://github.com/canonical/temporal-rocks/blob/main/temporal-server/1.23.1/rockcraft.yaml" target="_blank" rel="noreferrer"&gt;rockcraft.yaml&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;The craft ecosystem provides developers with a rigorous, consistent and pleasant experience for building many kinds of artifacts. At the moment, we support Snaps, Rocks and Charms but we&amp;rsquo;re actively developing crafts for Debian packages, cloud images and more.The basic build process, &lt;code&gt;parts&lt;/code&gt; ecosystem and foundations of the crafts are &amp;ldquo;battle tested&amp;rdquo; at this point, and I&amp;rsquo;m excited to see how the experimental &lt;code&gt;craft test&lt;/code&gt; commands shape up across the crafts.&lt;/p&gt;
&lt;p&gt;One of the killer features for the crafts is the ability to reuse part definitions across different artifacts - which makes the pay off for learning the &lt;code&gt;parts&lt;/code&gt; language very high - it&amp;rsquo;s a skill you&amp;rsquo;ll be able to use to build Snaps, Rocks, Charms, VM Images and soon Debs!&lt;/p&gt;
&lt;p&gt;If I look at ecosystems like Debian, where tooling like &lt;code&gt;autopkgtest&lt;/code&gt; is the standard, I think &lt;code&gt;debcraft test&lt;/code&gt; will offer an intuitive entrypoint and encourage more testing, and the same is true of Snaps, both graphical and command-line.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all for now!&lt;/p&gt;</description></item></channel></rss>