“All my centurions develop using snaps.”Julius Caesar
By and large, software development can be an enjoyable process. Until you hit the first error, that is. At that point, you want to get past the stumbling blocks as quickly as possible and resume building your apps. A robust, flexible development framework can make a big difference in how fast you can resolve the issues. Furthermore, good familiarity with how development tools behave can significantly improve the pace at which you iterate.
Snapcraft offers several useful features and options that enable you to speed up your work making snaps. Whether you’re a newbie or a veteran of many a snap, we’d like to show you some practical tips and tricks that ought to make your snap development experience even more streamlined – and snappier.
To base or not to base
Snapcraft 3.0 introduced a new functionality called bases, which somewhat changed how snapcraft builds applications.
A base is a special kind of snap that provides a minimal set of libraries common to most applications. A base snap mounts itself as the root filesystem within your snap. Then, at runtime, the base’s library paths are searched directly after the paths for your specific snap.
Bases are defined by adding the base: keyword to your snapcraft.yaml, followed by the base name. With bases, you can leverage newer libraries and build toolchain from the Ubuntu archive. For example, to specify Core 18, a snap based on the Ubuntu 18.04 LTS and the current standard base for snap building, you would add the following:
Another important addition is the use of Multipass, a system that orchestrates the creation, management and maintenance of virtual machines for simplified development. Prior to version 3.0, snapcraft used the LXD container management tool to create build instances.
For people starting fresh in the snap world, these changes are largely transparent. However, if you have previously packaged snaps using LXD and have not used bases, you may have some initial transition issues, and it is good to know how to handle the new development environment in a seamless manner.
Faster development using Multipass
If you’re lucky (or exceptionally talented), you will have written your snapcraft.yaml perfectly on the first go, and the snap will build without any errors. But this may not always be possible. Typically, your build process will consist of trial and error – you will make a change to snapcraft.yaml, run snapcraft, wait for the build to finish, and then repeat if needed. In some cases, you may also want to start ‘fresh’, in which case, you will run:
Without additional arguments, this will delete the virtual machine and all its contents, including the minimal operating system, the build setup, downloaded sources, and any built parts – it will not delete the snap or the source of your project. In some cases, rebuilding everything from scratch can be a bandwidth- and time-consuming process, and you will most likely want to iterate faster.
It is possible to run snapcraft with the debug option (–debug), which will open a shell in the virtual machine if the build process fails, allowing you to inspect any issues and make changes. For instance:
Launching a VM.
snapcraft 3.2+git2.g9e83f45 from Canonical installed
The ruby plugin is currently in beta, its API may break. Use at your own risk
Skipping pull mdl (already ran)
Cleaning later steps and re-building mdl ('build-packages' property changed)
/bin/sh: 29: rake: not found
Failed to run 'override-build': Exit code was 127.
You will now see an open shell in front of you, e.g.:
This is the actual snap build environment shell within the virtual machine (named snapcraft-”your-snap-name”), and you can inspect its contents, make any changes, and then re-run snapcraft. Now, this is where the time-saving element comes in. You can make changes to snapcraft.yaml outside the build environment shell (in your home directory or wherever your project snapcraft.yaml is located) and then run snapcraft inside the virtual machine. You do not need to make manual changes inside the virtual environment. We will explain how this works in a moment.
Virtual machine contents
Inside the Multipass VM instance, in the /root directory, you will see the following directory structure:
- parts – Contains the parts specified in your snapcraft.yaml (one or more). Each folder will correspond to the part name in your snapcraft.yaml, and inside, there will be build sources and other files.
- prime – Contains the wrapper commands that allow the snap to run correctly inside its confined environment, as well as declarative metadata that define the snap, like snap.yaml file and GUI elements.
- Project – As mentioned earlier, this is your project directory, mounted inside the VM instance, including your snapcraft.yaml file. If you make changes to this file, it will automatically be seen inside the virtual machine, which can help you iterate faster.
- stage – contains all the runtime components (most likely libraries) that will be included into your snap. If you have not specified any in your snapcraft.yaml, this directory will be empty.
- State – this file contains the list of all the assets needed to build the snap.
Don’t hesitate, iterate
Now that we understand what happens in the background, and what a typical Multipass VM instance contains, we can now speed up the development. You can open a shell, manually manipulate or tweak any components (like sources or libraries), make changes to the snapcraft.yaml file, and then run again.
Now, it is possible you may encounter the exact same errors as before, even though you have changed your snapcraft.yaml. In this case, you will need to clean the parts already built, and since you’re inside the VM, you can do it yourself, like:
snapcraft clean ”part name”
Then, when you re-run snapcraft, the part will be built again, using the new YAML declaration.
If the build is successful, you will have the .snap file, and you won’t need to do any manual changes. The snap will be located in your project directory, which is mapped to your home directory (or work environment). You can just exit the VM shell at this stage.
Faster development using LXD
If you require LXD containers (for instance, in a scenario where it is not possible to use nested virtual machines created by Multipass), you can still create them with snapcraft 3.0, so you can test changes to your snaps and gradually adapt your snapcraft.yaml to use bases. This can be done by omitting the use of the keyword base and setting the correct environment variable:
If your build fails, you can step into the container instance by running snapcraft with the –debug option, as we’ve seen with the Multipass option.
Manual creation of LXD containers
Alternatively, if you prefer, you can also manually spin up your own container instances. To that end, you will need to:
- Manually start a container (with lxc launch).
- Copy your snapcraft.yaml into the container (with lxc file push).
- Open an interactive shell (with lxc exec).
- Inside the container, run snapcraft … but you need to run snapcraft with a special flag that will run snapcraft in the current context and not start a new container within your container! This is done using the –destructive-mode flag. Please be careful you run this command in the right shell, so you don’t accidentally do this on your host system. You may end up retrieving various packages and libraries that could potentially conflict with your setup.
- Once you have successfully completed the build, you can retrieve the snap from inside the container (with lxc pull).
- Stop and/or destroy the container instance (with lxc stop).
If you’ve used snapcraft prior to version 3.0, you are probably familiar with the snapcraft cleanbuild option. In the past, if you encountered an error, made a change to your snapcraft.yaml, and you wanted to start fresh, you would run this command, which would create a brand new container instance, setup the minimal OS inside it, and download all the necessary packages to build a snap.
Now, it is possible you are using this option constantly, which can cost time and bandwidth. The option is still available, and you can still combine it with manual container access as described above, but it is advisable to use the –debug option as the faster, more elegant way of troubleshooting and iterating on your snaps.
Other useful pointers
There are still a few other things you can do to make your snapping faster and easier:
- Gather the list of build requirements your application needs.
- Gather the list of runtime components your applications needs.
- Gather the list of resources your application needs – this will translate into the use of interfaces, and you will need to specify the plug end of interface connections in your snapcraft.yaml. For example, your application may need access to the home directory and network, in which case, you would list something like.
- Use devmode until you have verified everything works. Snaps are designed to run in a secure manner, isolated from one another and from the system, using the confinement mechanism. This can create difficulties if your application expects unfettered, system-wide access. In devmode, you will get only soft rather than hard errors, which you can then inspect in the system log. You can also use snappy debug, which we’ve seen in our troubleshooting guide a few weeks ago. Once there are no more errors, and your application behaves correctly, you can change the confinement from devmode to strict.
Snapcraft is designed to be flexible and cater to a wide range of users. That also means that some of the “advanced” features are not instantly visible, but they do offer useful functionality that can enhance and simplify development.
In this article, we touched on the concept of bases, the use of Multipass and LXD tools, debug options, the content of build systems, and expanded on general suggestions that can make your development with snapcraft smoother, faster and ultimately, more enjoyable. If you have any comments or suggestions, please join our forum for a hearty discussion.
No containers or centurions were harmed in the production of this article.