A snap is a bundle of an app and its dependencies that works without modification across many different Linux distributions. Snaps are discoverable and installable from the Snap Store, an app store with an audience of millions. To perform simple installation you could;
snapd install packagename
Snapcraft is a powerful and easy to use command line tool for building snaps. It reads a simple, declarative file and runs the build for us.
Today In this tutorial, we’re going to explore some of snapcraft’s best features before using it to create an ideal first snap. For a more detailed look at building snaps.
What you’ll learn about Snap
In this tutorial, we’ll cover how to: - install the snapcraft tool - create a new project - declare snap metadata - use parts to define an app - build a snap - fix common build issues - upload a snap to the Snap Store
What you’ll need in this Tutorial about Build snap package
- Ubuntu 18.04 LTS (Bionic), or later, or a derivative
- running from a nested VM requires accelerated/nested VM functionality
- basic command line knowledge and how to edit a file
- rudimentary knowledge of snaps
Getting started with building your first snap package
This tutorial has been written to work on Ubuntu 18.04 LTS. However, it should work without modification on later Ubuntu releases and other GNU/Linux distributions derived from an Ubuntu 18.04 base, such as Linux Mint 19.x.
Installing mandatory dependencies
First, open up a terminal and make sure you have snap installed:
$ snap --version
If it’s installed, you’ll see something similar to the following:
Just incase snapd is not installed
Installing snap on Debian and Ubuntu
$ sudo apt update
$ sudo apt install snapd
Now we assume you have snapd installed We can now install Snapcraft with a single command:
sudo snap install --classic snapcraft
NOTE: The --classic
switch enables the installation of a snap that uses classic confinement. We discuss snap security confinement in the following section.
By now we’re all set. Let’s get cracking and build our first snap!
Building a snap is easy
Starting the project
The first thing to do is to create a general snaps directory followed by a working directory for this specific snap project:
$ mkdir -p ~/productionsnap/hello
$ cd ~/productionsnap/hello
It is from within this hello
directory where we will invoke all subsequent commands.
NOTE: Due to a limitation in the project we’re going to build, the path of the directory you put the hello
directory in shouldn’t contain any spaces.
Get started by initialising your snap environment:
$ snapcraft init
This creates a snapcraft.yaml
in which you declare how the snap is built and which properties it exposes to the user. We will edit this later.
The directory structure now looks like this:
mysnaps/
└── hello
└── snap
└── snapcraft.yaml
Note: Any future snaps you want to create should be put within their own directory under
mysnaps
.
Describing the snap
Let’s take a look at the top part of your snapcraft.yaml file. It should look somewhat as shown below:
name: my-snap-name # you probably want to 'snapcraft register <name>'
base: core18 # the base snap is the execution environment for this snap
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
summary: Single-line elevator pitch for your amazing snap # 79 char long summary
description: |
This is my-snap's description. You have a paragraph or two to tell the
most important story about your snap. Keep it under 100 words though,
we live in tweetspace and your description wants to look good in the snap
store.
grade: devel # must be 'stable' to release into candidate/stable channels
confinement: devmode # use 'strict' once you have the right plugs and slots
This part of snapcraft.yaml
is mandatory and is basic metadata for the snap.
Let’s go through this line by line: - name: The name of the snap.
-
base: A foundation snap that provides a run-time environment with a minimal set of libraries that are common to most applications. For most snaps, this should be
core18
, which equates to Ubuntu 18.04 LTS. -
version: The current version of the snap. This is just a human readable string. All snap uploads will get an incremental snap revision, which is independent from this version. It’s separated so that you can upload multiple times the same snap for the same architecture with the same version. See it as a string that indicates to your user the current version, like “stable”, “2.0”, etc.
-
summary: A short, one-line summary or tag-line for your snap.
-
description: A longer description of the snap. It can span over multiple lines if prefixed with the ‘|’ character.
-
grade: Can be used by the publisher to indicate the quality confidence in the build. The store will prevent publishing ‘devel’ grade builds to the ‘stable’ channel.
-
confinement: A snap’s confinement level is the degree of isolation it has from your system, and there are three levels:
strict
,classic
anddevmode
. strict snaps run in complete isolation,classic
snaps have open access to system resources and devmode snaps run as strict but with open access to the system. The latter is ideal for development, but your snap will need move from devmode to be published. See Snap confinement for more details.In this tutorial, we will focus on
devmode
andstrict
confinement.
For more detailed information on this top-level metadata, see Adding global metadata.
And that’s it for the basics. It’s now time to customise the snapcraft.yaml file for our own snap. Taking the above into account, we can change the top of the file to be:
name: hello
base: core18
version: '2.10'
summary: GNU Hello, the "hello world" snap
description: |
GNU hello prints a friendly greeting.
grade: devel
confinement: devmode
Adding a part
Parts are used to describe your application, where its various components can be found, its build and run-time requirements, and those of its dependencies. A snap consists of one or more parts, depending on its complexity.
Here are a few multiple-part snap examples: - snaps with separate logical parts, such as a server snap containing a web server, a database and the application itself - a game which ships the game engine and game data for three different games, each one being defined in its own part - snaps with parts from different locations - parts which are built in a different way
Our hello
snap will be nice and simple. It will consist of only one part for now. In the following pages we are going to gradually extend it.
Two must-haves for every part are the ‘source’ and ‘plugin’ definition. Think of these as the “what” and the “how”, respectively. As source you can, for example, pick a source repository (like git
), a tarball, or a local directory. Snapcraft supports many plugins, allowing you to build a wide variety of project types (e.g. autotools, cmake, go, maven, nodejs, python2, python3).
To build hello
, add the following ‘parts’ stanza to your snapcraft.yaml
file (replace anything else that might be there):
parts:
gnu-hello:
source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
plugin: autotools
So we have added a part called gnu-hello
(its name is arbitrary). For ‘source’, we specified a tarball located on the GNU project’s FTP server. As ‘plugin’ we’ve chosen autotools
which uses the traditional ./configure && make && make install
build steps.
See Supported plugins, or run snapcraft list-plugins
, to get more information on which build-tools and platforms Snapcraft supports.
To build our snap all you need to do is:
$ snapcraft
The first time you run snapcraft, you may be asked for permission to install Multipass. Snapcraft uses Multipass to both simplify the build process and to confine the build environment within a virtual machine. It offers the best build experience, so we highly recommend answering ‘y’. However, if you’d rather not use Multipass, you can also build natively, remotely, and with LXD. See Build options for details.
During the build, snapcraft will show plenty of output, however a successful build will end with:
[...]
Staging gnu-hello
Priming gnu-hello
Snapping 'hello' |
Snapped hello_2.10_amd64.snap
Congratulations! You’ve just built your first snap, which is now ready to be installed:
$ sudo snap install --devmode hello_2.10_amd64.snap
The output should declare:
hello 2.10 installed
To get some info on the installed snap:
$ snap list hello
Sample output:
Name Version Rev Tracking Developer Notes
hello 2.10 x1 - - devmode
Let’s try to execute it:
$ hello
On traditional Ubuntu you will get:
The program 'hello' can be found in the following packages:
* hello
* hello-traditional
Try: sudo apt install <selected package>
Or you might get a different error if you previously installed the hello
snap:
$ hello
Output:
bash: /snap/bin/hello No such file
The command doesn’t exist despite being part of our snap and installed! Indeed, snaps don’t expose anything to the user by default (command, services, etc.). We have to do this explicitly and that’s exactly what you are going to tackle next!
If it does work for you, you should verify that it’s the correct hello
command. Check the output of which hello
- it might list something like /usr/bin/hello
. What we’re after is a binary under the /snap/bin
directory.
Exposing an app via your snap!
Defining commands
In order for services and commands to be exposed to users, you need to specify them in snapcraft.yaml
of course! This will take care of a couple of things for you:
- it will make sure that services are automatically started/stopped
- all commands will be “namespaced”, so that you could, for example, install the same snap from different publishers and still be able to run the snaps separately
Exposing the hello
command is painless. All you need to do is add the following to your snapcraft.yaml
file:
apps:
hello:
command: bin/hello
This defines an app named hello
, which points to the executable bin/hello
in the directory structure shipped by the snap.
We generally advise to put this stanza between the metadata fields and the ‘parts’ field. Technically the order doesn’t matter, but it makes sense to place basic pieces before more complex ones.
Our snapcraft.yaml
file should now resemble this:
name: hello
base: core18
version: '2.10'
summary: GNU Hello, the "hello world" snap
description: |
GNU hello prints a friendly greeting.
grade: devel
confinement: devmode
apps:
hello:
command: bin/hello
parts:
gnu-hello:
source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
plugin: autotools
Iterating over your snap
Now that the command is defined, let’s rebuild the snap. You can do do this by simply running snapcraft again - only the new or changed elements will be built and merged into a new snap.
However, to show a more typical snap-building process, we’re going to use a slightly different command that will allow us to peek into the snap we’re building before the snap is created:
$ snapcraft prime --shell
This command tells snapcraft to run the build up until the “prime” step and open a shell within the snap build environment, with prime being the final stage in a four step process:
- pull: downloads or otherwise retrieves the components needed to build the part
- build: constructs the part from the previously pulled components
- copies the built components into the staging area
- copies the staged components into the priming area, to their final locations for the resulting snap.
Another useful command is snapcraft --debug
. This will open a shell in the build environment when an error occurs, letting you investigate the error before resuming the build.
From within the shell, you can see that while the binary we just added is in the stage directory, it’s not yet in prime:
$ ls stage/bin/
hello
$ ls prime/bin/
ls: cannot access 'prime/bin': No such file or directory
You can continue building your snap from within the build environment using the same snapcraft commands you use outside, with the convenience of having a prompt directly within the environment. To build the prime stage, for example, just type snapcraft prime
:
$ snapcraft prime
Skipping pull gnu-hello (already ran)
Skipping build gnu-hello (already ran)
Skipping stage gnu-hello (already ran)
Priming gnu-hello
The hello binary will now be in the prime/bin directory:
$ sudo snap install --devmode hello_2.10_amd64.snap
$ ls prime/bin
hello
To resume the build and generate the snap, you can type snapcraft
within the build environment, or exit and run snapcraft
again from there. Either way, the resultant snap will be placed in the snap project directory.
We can now re-install the new snap and run hello:
$ hello
Hello, world!
The path for the binary should be correctly set too:
$ which hello
/snap/bin/hello
Well done! You’ve just made your first working snap!
A snap is made of parts
Let’s add another part to make the snap a bit more interesting. In the ‘parts’ definition, make an addition:
parts:
[...]
gnu-bash:
source: http://ftp.gnu.org/gnu/bash/bash-4.3.tar.gz
plugin: autotools
You will notice that this part (named gnu-bash
) works very much like the gnu-hello
part from before: it downloads a tarball and builds it using the autotools
plugin.
As we did before, we need to define the command we want to expose. Let’s do this now. In the ‘apps’ definition, add:
apps:
[...]
bash:
command: bash
This time the command name is different from the snap name. By default, all commands are exposed to the user as <snap-name>.<command-name>
. This binary will thus be hello.bash
. That way, we will avoid a clash with /bin/bash
(system binaries trump binaries shipped by snaps) or any other snaps shipping a bash
command. However, as you may remember, the first binary is named hello
. This is due to the simplification when equals . Instead of hello.hello
, we have the command condensed to hello
.
Our snap will thus result in two binaries being shipped: hello
and hello.bash
.
Note that we set bash
as the command parameter, and not bin/bash
relative to the system snap directory ($SNAP=/snap/hello/current
) as we did for hello
. Both are equally valid because snapcraft
and snapd
create a small wrapper around your executable command which sets some environment variables. Technically, $SNAP/bin
will be prepended to your $PATH
for this snap. This avoids the need to set the path explicitly. This topic will be touched upon in upcoming sections.
Now re-do the build:
$ snapcraft
Only the gnu-bash
part will be built now (as nothing changed in the other part). This makes things quicker but since Bash is itself a significant piece of software this command will still take quite some time to complete.
Install the resulting snap again and check whether the new binary is available:
$ sudo snap install --devmode hello_2.10_amd64.snap
$ hello
Hello, world!
Now try bash:
$ hello.bash
The above should yield:
bash-4.3$ env
[ outputs a list of environment variables ]
Now exit that Bash shell:
bash-4.3$ exit
You will see that the environment variables available from your snap are a little different from your user environment. Some additional variables are added like $SNAP_
and some system environment variables have been altered to point to your snap directory, like $PATH
or $LD_LIBRARY_PATH
. Take the time to get familiar with them!
See Environment variables for further details.
Excellent work! You have it all nice and working!
Removing devmode
One last thing you might want to do before the snap is ready for wider consumption is to remove the devmode
status.
Important:
Users of snaps using devmode
will need to pass --devmode
during the installation, so they explicitly agree to trust you and your snap. Another benefit of removing devmode
is that you will be able to ship your snap on the ‘stable’ or ‘candidate’ channels (you can only release to the other channels, like ‘beta’ or ‘edge’ as your snap is less trusted) and users will be able to search for it using snap find
.
For this to be declared in your snap, let’s set confinement
to strict
in snapcraft.yaml
:
confinement: strict
Now let’s build the snap and install it properly! That is, we are going to call snapcraft
without --devmode
to really test it under confinement:
$ snapcraft
[...]
$ sudo snap install hello_2.10_amd64.snap
Note:
We got the snap package name from the last output line from the snapcraft
command.
Yikes! This gives:
error: cannot find signatures with metadata for snap "hello_2.10_amd64.snap"
Indeed, we tried to install a snap that wasn’t signed by the Snap Store. Previously, we performed local installations via --devmode
which implied (in addition to being run without confinement) that an unsigned snap was OK to be installed. As this is not the case any more we need to indicate that it’s OK to install an unsigned snap. This is done via the --dangerous
option:
$ sudo snap install hello_2.10_amd64.snap --dangerous
Test again!
$ hello
Hello, world!
Creating a new shell
$ hello.bash
…and issue a command there:
bash-4.3$ ls
now gives:
ls: cannot open directory '.': Permission denied
Exit the shell for now:
bash-4.3$ exit
What’s happening here? Your snap is not broken, it’s just confined now and so it can only access its own respective directories.
Note:
For other snaps you might need to declare if commands or services need special permissions (e.g. access to the network or audio). A tutorial on “interfaces”, “slots”, and “plugs” will cover this very topic.
You are done. This snap is ready for publication. Awesome!
Push to the store
Applications are easily uploaded to the Snap Store. Registering an account is easy, so let’s do that first.
Registering an account
Begin by going to the Snapcraft dashboard and clicking on the “Sign in or register” button in the top-right corner:
Command-line authentication
We’ll now log in with the snapcraft command using your new account. The first time you do so you will be asked to enable multi-factor authentication and agree with the developer terms & conditions:
$ snapcraft login
A sample session follows:
Enter your Ubuntu One e-mail address and password.
If you do not have an Ubuntu One account, you can create one at https://snapcraft.io/account
Email: [email protected]
Password:
Second-factor auth: (press Enter if you don't use two-factor authentication):
Login successful.
You can log out any time with snapcraft logout
.
Register a snap name
Before being able to upload a snap, you will need to register (reserve) a name for it. This is done with snapcraft register <some_name>
.
Here, assuming javier is the store username established above, we’ll do:
$ snapcraft register hello-javier
A sample session follows:
We always want to ensure that users get the software they expect
for a particular name.
If needed, we will rename snaps to ensure that a particular name
reflects the software most widely expected by our community.
For example, most people would expect 'thunderbird' to be published by
Mozilla. They would also expect to be able to get other snaps of
Thunderbird as 'thunderbird-$username'.
Would you say that MOST users will expect 'hello-javier' to come from
You, and be the software you intend to publish there? [y/N]: y
Registering hello-javier.
Congratulations! You're now the publisher for 'hello-javier'.
Clearly, the Store prefers the name to be of the format <local snap name>-<store-username>
.
The snap name hello-javier
is different from hello
that we initially placed in our snapcraft.yaml
file. We will need to edit that file accordingly and rebuild the snap. This is also an opportune time to change the ‘grade’ to ‘stable’!
The file should now include the following lines:
name: hello-javier
grade: stable
Rebuild:
$ snapcraft
You should now have a snap package called hello_-javier_2.10_amd64.snap
.
Note:
Recall that you already installed a snap package called hello_2.10_amd64.snap
. Don’t forget to uninstall it with sudo snap remove hello
.
Push and release your snap
It’s time to make this snap available to the world!
Let’s release it to the ‘candidate’ channel for now:
$ snapcraft push hello-javier_2.10_amd64.snap --release=candidate
Output:
Pushing hello-javier_2.10_amd64.snap
After pushing, an attempt will be made to release to 'candidate'
Preparing to push '/home/ubuntu/mysnaps/hello/hello-javier_2.10_amd64.snap' to the store.
Pushing hello-javier_2.10_amd64.snap [=====================================================] 100%
Processing...|
Ready to release!
Revision 1 of 'hello-javier' created.
Track Arch Channel Version Revision
latest amd64 stable - -
candidate 2.10 1
beta ^ ^
edge ^ ^
The 'candidate' channel is now open.
You should receive an email informing you that your snap is pending review (automatic checking). If you are not using any reserved interfaces and security checks are passing, users will be able to install it like so:
$ sudo snap install hello-javier --channel=candidate
As we uploaded an amd64 binary, only people on 64-bit machines will get access to this snap. You can either focus on one architecture to support, manually build a binary for each architecture you wish to support, or use remote build or build.snapcraft.io to push your snapcraft.yaml
, and get resulting snaps built on all architectures for you!
See Build options for more details.
From here, if you are happy with the testing of your snap, you can use the snapcraft release
command to have fine-grained control over what you are releasing and where:
snapcraft release <snap-name> <revision> <channel>
Therefore, to release hello-javier
to the ‘stable’ channel, and make it immediately visible in the Store:
$ snapcraft release hello-javier 1 stable
Remember that snaps with confinement: devmode
can’t be released to the ‘stable’ or ‘candidate’ channels.
The web interface will give you information about the publication status. Take a look to see all the available options!
Comments
Comments are closed.