Learn to make a snap

Snapcraft is a snappy packaging tool.

Requirements

To take this tour of snaps and Snapcraft, you'll need to have Snapcraft 2.11 or later installed. If you've already installed Snapcraft you can check the version:

$ snapcraft --version
2.22

If you haven't installed Snapcraft, it's available on Ubuntu 16.04 LTS:

$ sudo apt install snapcraft

Note: to get the latest version of Snapcraft and future updates, make sure the xenial-updates/universe repository is enabled on your system. Snapcraft will also run on any Linux distribution with an up-to-date version of LXD, the container hypervisor.

Some of these examples also require you to have the build-essential package installed:

$ sudo apt install build-essential

Take the snapcraft tour

This tour walks you through the making of snaps. It starts with an overview of the snap format before introducing you to Snapcraft, the most widely used tool for creating snaps. After running through the basics of using Snapcraft the tour takes an in depth look at the key components that make up a snap. By the end of the tour you'll know how to create snaps so, if you want to, you can create them by hand or make your own snappy tools.

The tour includes all the example source code and files in a series of subdirectories, which you load using the Snapcraft tool:

$ snapcraft tour
Snapcraft tour initialized in ./snapcraft-tour/
Instructions are in the README, or http://snapcraft.io/create/#tour

Each stage of the tour is in a separate subdirectory, with number prefixes showing the sequence.

Unless you specify a different directory name, the tour will be created in a new snapcraft-tour subdirectory in your current working directory. The directory structure will look like:

$ ls snapcraft-tour/**
snapcraft-tour/README.md

snapcraft-tour/00-SNAPCRAFT:
01-easy-start  02-parts

snapcraft-tour/10-SNAPS:
01-service  02-service-confined

snapcraft-tour/20-PARTS-PLUGINS:
01-reusable-part

You can cd into each directory and work through the README, or follow along on this page.

00 - SNAPCRAFT

01-easy-start: a single snapcraft.yaml describes a snap

Snapcraft uses a single text file to describe the entire build process for a snap: the snapcraft.yaml file. Over the course of this tour you'll learn how to create this file for your own snaps. You can see an example in this directory:

$ cd snapcraft-tour/00-SNAPCRAFT/01-easy-start
$ ls
snapcraft.yaml

Notice that snapcraft.yaml is the only file in the directory. Snapcraft doesn't need anything else as it will fetch everything it needs to build the snap, using the snapcraft.yaml description for the snap. Take a look inside:

$ more snapcraft.yaml
name: hello
version: "2.10"
summary: GNU Hello, the "hello world" snap
description: GNU hello prints a friendly greeting.
  This is part of the snapcraft tour at http://snapcraft.io/create/
confinement: strict

apps:
  hello:
    command: hello

parts:
  gnu-hello:
    plugin: autotools
    source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz

Don't worry about everything in there just yet. The snapcraft tool will interpret this file, fetch and install everything that is needed, build it, and give you a snap.

Start by building this snap, noting that you’ll be prompted for your system password if snapcraft needs to download dependencies from the archive:

$ snapcraft
[sudo] password for [your user name]:  ***
Preparing to pull gnu-hello
Pulling gnu-hello
Preparing to build gnu-hello
Building gnu-hello
...
Snapped hello_2.10_amd64.snap

You'll have seen that Snapcraft fetched the source code, configured and built it. The snap is now available in your directory, along with new parts, prime, and stage directories.

$ ls
hello_2.10_amd64.snap  parts  prime  snapcraft.yaml  stage

As you can see a snap is a single file. The name of the file is based on information in the snapcraft.yaml (its name and version) and the architecture you're building on: <name>_<version>_<arch>.snap.

Here, the snap was built on an amd64 machine, so the architecture used in the file name reflects that. The following commands use globs so you don't need to replace the architecture accordingly.

Now, everything you need for the hello app is inside the snap file. You can install it locally:

$ sudo snap install --dangerous hello_2.10_*.snap
hello 2.10 installed

$ snap info hello
name:      hello
summary:   "GNU Hello, the \"hello world\" snap"
publisher:
description: |
  GNU hello prints a friendly greeting. This is part of the snapcraft tour at
  http://snapcraft.io/create/
tracking:
installed: 2.10 (x1) 745kB -
refreshed: 2016-12-14 17:06:35 +0100 CET

Congratulations, you've just made and installed your first snap.

Note that the snap has both a version and a revision.

The version is purely human-readable, it isn't used for any particular purpose other than keeping you aware of what's on your system. Two different snaps can claim to have the same version (in fact, when you have daily builds of a snap, it will almost always have a version like "2.1b3" for every build until the version is updated).

The revision for a snap is unique. In this example, because you built the snap locally, it gets a local alphanumeric revision that is unique on this machine only. When you load a snap from the store, the revision is an integer number that exactly identifies the snap that is installed. While you can't do this for local revisions, with snaps loaded from the store you can compare revisions on two machines to determine if they have exactly the same content installed.

When you install a snap from the store, it includes an "assertion" that the snap was published by a trusted third party. However, in this case you're installing a snap straight from your hard drive; that assertion hasn't been generated. We use the --dangerous option to tell snapd to trust it anyway.

You can also confirm details of all the snaps you've installed:

$ snap list
Name         Version               Rev  Developer  Notes
hello        2.10                  X1              -
ubuntu-core  16.04.1               423  canonical  -

Finally, run your snap:

$ hello
Hello, world!

Next, you might want to do some housekeeping and get rid of all the files that were created in the process of building the snap:

$ snapcraft clean
Cleaning priming area for gnu-hello
Cleaning staging area for gnu-hello
Cleaning build for gnu-hello
Cleaning pulled source for gnu-hello
Cleaning up snapping area

$ ls
hello_2.10_amd64.snap  snapcraft.yaml

In some trees it may be preferable to name the snapcraft YAML file .snapcraft.yaml. Snapcraft will look for snapcraft.yaml or .snapcraft.yaml, and will use either one, but it will error out if you've got both there. If you're uncertain, the recommendation is to use snapcraft.yaml.

One last thing to be aware of is that the snapcraft command is shorthand for the real command, which is snapcraft snap. You can see this from the help for snapcraft:

$ snapcraft help
Usage:
  snapcraft [options] [--enable-geoip --no-parallel-build]
  snapcraft [options] init
  snapcraft [options] pull [<part> ...]  [--enable-geoip]
  snapcraft [options] build [<part> ...] [--no-parallel-build]
  snapcraft [options] stage [<part> ...]
  snapcraft [options] prime [<part> ...]
  snapcraft [options] strip [<part> ...]
  snapcraft [options] clean [<part> ...] [--step <step>]
  snapcraft [options] snap [<directory> --output <snap-file>]
  snapcraft [options] cleanbuild
  snapcraft [options] login
  snapcraft [options] logout
  snapcraft [options] list-keys
  snapcraft [options] keys
  snapcraft [options] create-key [<key-name>]
  snapcraft [options] register-key [<key-name>]
  snapcraft [options] register <snap-name> [--private]
  snapcraft [options] sign-build <snap-file> [--key-name=<key-name>] [--local]
  snapcraft [options] upload <snap-file>
  snapcraft [options] push <snap-file> [--release <channels>]
  snapcraft [options] release <snap-name> <revision> <channel>
  snapcraft [options] status <snap-name> [--series=<series>] [--arch=<arch>]
  snapcraft [options] history <snap-name> [--series=<series>] [--arch=<arch>]
  snapcraft [options] close <snap-name> <channel_names>...
  snapcraft [options] list-plugins
  snapcraft [options] tour [<directory>]
  snapcraft [options] update
  snapcraft [options] gated <snap-name>
  snapcraft [options] validate <snap-name> <validation>... [--key-name=<key-name>]
  snapcraft [options] define <part-name>
  snapcraft [options] search [<query> ...]
  snapcraft [options] enable-ci [<ci-system>] [--refresh]
  snapcraft [options] help (topics | <plugin> | <topic>) [--devel]
  snapcraft (-h | --help)
  snapcraft --version

By the time you've finished this tour, you'll know your way around most of these.

Here are the things you should be comfortable with, before you move on. A snapcraft.yaml describes the snap, and it should be at the top of your directory tree.

The Snapcraft tool will do all the work if you just type snapcraft or snapcraft snap.

The resulting snap is a single file called name_version_arch.snap.

You can install snaps directly with snap install --dangerous <snapfile.snap>.

Locally created snaps have their own local revisions that are specific to that system.

Snap revisions uniquely identify the exact build of a snap that is installed. snapcraft clean will remove all the build artifacts.

The snapcraft.yaml file can optionally be called .snapcraft.yaml.

02-parts: Snapcraft makes snaps out of parts

When you make a snap, you bundle together all the pieces you want your app to provide for itself, rather than consume them from the base operating system. Normally, that means combining several different pieces of code, because your app probably isn't so simple that it consists of a single piece of code that runs without any supporting components.

In the snapcraft.yaml of the hello app you'll have noticed a section called parts:

  gnu-hello:
    plugin: autotools
    source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz

This is the simplest possible snap. It has only one part, called gnu-hello. This part is also just about the simplest kind of part. It only has two attributes; the source code location (which is instructing Snapcraft to pull the source from the gnu.org website) and the plugin to use when building this part, namely autotools.

Many parts will specify an explicit file version for the source, as this one does. This ensures that everybody who builds this part gets exactly the same code, because it's fetched from the same file. However, you could point to a source that will change every time there is a new release:

parts:
  foo-stable:
    plugin: autotools
    source: http://releases.foo.org/download/foo-stable.tar.gz

Now, every time someone runs Snapcraft with the foo-stable part, they might get a different codebase from the previous person to build the same part, because the foo-stable.tar.gz file might have changed. It's generally preferable to be explicit with your parts, so people know what they are getting and can reproduce the snap build process easily.

You can refer to local source as well. For example, you can make a part that refers to a subdirectory where the part source code is kept:

parts:
  foo-forked:
    plugin: autotools
    source: ./src/foo

You can ask Snapcraft itself for a full specification of all the ways to refer to source:

$ snapcraft help sources
Common 'source' options.

Unless the part plugin overrides this behaviour, a part can use these
'source' keys in its definition. They tell snapcraft where to pull source
code for that part, and how to unpack it if necessary.

  - source: url-or-path

    A URL or path to some source tree to build. It can be local
    ('./src/foo') or remote ('https://foo.org/...'), and can refer to a
    directory tree or a tarball or a revision control repository
    ('git:...').

  - source-type: git, bzr, hg, svn, tar, deb, rpm, or zip

    In some cases the source string is not enough to identify the version
    control system or compression algorithm. The source-type key can tell
    snapcraft exactly how to treat that content.

  - source-depth: <integer>

    By default clones or branches with full history, specifying a depth
    will truncate the history to the specified number of commits.

  - source-branch: <branch-name>

    Snapcraft will checkout a specific branch from the source tree. This
    only works on multi-branch repositories from git and hg (mercurial).

  - source-commit: <commit>

    Snapcraft will checkout the specific commit from the source tree revision
    control system.

  - source-tag: <tag>

    Snapcraft will checkout the specific tag from the source tree revision
    control system.

  - source-subdir: path

    Snapcraft will checkout the repository or unpack the archive referred to
    by the 'source' keyword into parts/<part-name>/src/ but it will only
    copy the specified subdirectory into parts/<part-name>/build/

Note that plugins might well define their own semantics for the 'source'
keywords, because they handle specific build systems, and many languages
have their own built-in packaging systems (think CPAN, PyPI, NPM). In those
cases you want to refer to the help text for the specific plugin.

  snapcraft help <plugin>

The more interesting attribute of gnu-hello is the plugin, autotools. Snapcraft has a plugin system so that it can drive different build systems for different parts. In this case, gnu-hello uses the autotools build conventions — essentially ./configure, make, and make install. You can generally snap anything that follows that convention very easily, just use the autotools plugin for the part.

Snapcraft can tell you which plugins it has installed:

$ snapcraft list-plugins
ant        copy    kernel  nodejs   tar-content
autotools  go      make    python2
catkin     jdk     maven   python3
cmake      kbuild  nil     scons

Each of these plugins knows how to drive a particular set of conventions for a particular build system. The important distinction is that Snapcraft is not itself a build system. It doesn't replace make, autoconf, or scons. Instead, a part describes which of those build systems to use, so that Snapcraft can drive the build system that the part author selected.

The next example is a more interesting snap that uses two parts:

$ cd ../02-parts
$ more snapcraft.yaml
name: hello-debug
version: "2.10"
summary: GNU Hello with Bash for debugging
description: GNU hello prints a friendly greeting.
  This is part of the snapcraft tour at http://snapcraft.io/create/

apps:
  hello:
    command: hello
  bash:
    command: bash

parts:
  gnu-hello:
    plugin: autotools
    source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz
  gnu-bash:
    plugin: autotools
    configflags: ["--infodir=/var/bash/info"]
    source: http://ftp.gnu.org/gnu/bash/bash-4.3.tar.gz

Now build and install it:

$ snapcraft
...
$ sudo snap install --dangerous hello-debug_2.10_*.snap
...

The result is that two apps are exposed:

  • The hello app pointing to the hello binary (the command) inside the snap. The command can be executed using hello-debug.hello
  • The bash app pointing to the bash binary (command) and is executed using hello-debug.bash

    $ hello-debug.hello $ hello-debug.bash

You'll also see that Snapcraft has created various subdirectories:

  • parts/
  • stage/
  • prime/

parts/ contains one subdirectory for each part: gnu-bash and gnu-hello. Each part's subdirectory has a src/ directory (to where the code is pulled), build/ where the build happens, and install/ where make install is processed.

stage/ is where all the content from parts/<part_name>/install is copied and consolidated in a single directory.

prime/ is the actual snap content. The stage/ directory is copied there. Binaries are stripped, wrappers are generated by Snapcraft, and the snap metadata files are also generated there. The final .snap file is just a squashfs archive of this prime/ directory. You can use and debug the snap's content from here.

Snapcraft commands

Snapcraft also lets you work with individual commands so you can update and customize your snap. This section looks at some of those options and shows how you could clean and rebuild the gnu-bash part.

clean: resetting individual parts or the whole snap

snapcraft clean removes the directories and data created by Snapcraft. By default it will delete all the directories and data created by Snapcraft from your snap's directory. However, you can use snapcraft clean <part> to delete a specific part:

$ snapcraft clean gnu-bash
Cleaning priming area for gnu-bash
Cleaning staging area for gnu-bash
Cleaning build for gnu-bash
Cleaning pulled source for gnu-bash

As you can see snapcraft clean has now deleted all your directories from the gnu-bash part.

$ ls parts
gnu-hello

pull: pulling dependencies to refresh code

When the source code in an online or local repository changes you can use snapcraft pull to pull the updated source code from that repository, remembering to do a snapcraft clean first. You can pull the code for all your dependencies or only the parts you know have changed by using snapcraft pull <part>.

$ snapcraft pull
Skipping pull gnu-hello (already ran)
Preparing to pull gnu-bash
Pulling gnu-bash 

The gnu-hello had already been pulled, when you first built the snap, so it's not pulled again.

As you can see snapcraft pull has created some new directories and populated src/:

$ ls parts/gnu-bash/**
parts/gnu-bash/bin:
pkg-config

parts/gnu-bash/build:

parts/gnu-bash/install:

parts/gnu-bash/src:
[...] lot of files
parts/gnu-bash/state:
pull

parts/gnu-bash/ubuntu:

build: build a specific part, or all of them

You can use snapcraft build to build any parts that haven't been built and install them in their part directory, or specify specific parts to be built:

$ snapcraft build
Skipping pull gnu-bash (already ran)
Skipping pull gnu-hello (already ran)
Preparing to build gnu-bash
Building gnu-bash
./configure --prefix= --infodir=/var/bash/info
[...]
Skipping build gnu-hello (already ran)

As you can see snapcraft build has now populated both the build/ and install/ directories:

$ ls parts/gnu-bash/build/
[...] lots of files

$ ls parts/gnu-bash/install/
[...] lots of files

stage: move the files installed from different parts into a single directory

Having built all the parts for your snap, use snapcraft stage to move all the relevant content into stage/ (to prepare for creating the snap package):

$ snapcraft stage
Skipping pull gnu-bash (already ran)
Skipping pull gnu-hello (already ran)
Skipping build gnu-bash (already ran)
Skipping build gnu-hello (already ran)
Staging gnu-bash
Skipping stage gnu-hello (already ran)
As you can see snapcraft stage has now added stage/bin/bash:
$ ls stage/**
stage/bin:
bash  bashbug  hello

stage/share:
doc  info  locale  man

stage/var:
bash

prime: move the files needed for the snap into place

Once you're satisfied with the snap created in the stage/ directory, create the final snap in its correct location using snapcraft prime:

$ snapcraft prime
Skipping pull gnu-hello (already ran)
Skipping pull gnu-bash (already ran)
Skipping build gnu-hello (already ran)
Skipping build gnu-bash (already ran)
Skipping stage gnu-hello (already ran)
Skipping stage gnu-bash (already ran)
Skipping prime gnu-hello (already ran)
Priming gnu-bash 

As you can see snapcraft prime has copied the content of stage/ to prime/ and added some wrapper and metadata scripts:

$ ls prime/
bin  command-bash.wrapper  command-hello.wrapper  meta  share  var

However, in most cases once you've cleaned a part from your snap directories you can rebuild the part and snap using snapcraft, which by default will only rebuild any parts missing from your snap.

snap: compress any directory into a single .snap squashfs file

The snapcraft snap command is essential, especially if you craft your own prime/ directory. The directory name itself doesn't matter, you just need to ensure you provide any required wrapper and meta (snap.yaml) files, from your build system. Then snapcraft snap will create a snap from the folder content.

The default action in the Snapcraft lifecycle is to compress the prime/ directory into a single .snap squashfs file.

$ snapcraft snap prime/
Snapping 'hello-debug' -
Snapped hello-debug_2.10_amd64.snap

Recap

In this section of the tour you've seen how a snap:

  • Can deliver an application
  • Is defined by a single snapcraft.yaml file
  • Can (and in most cases will) be made up of several parts
  • Is created in a number of stages that pull and build parts, before creating the snap package

10 - SNAPS

Now you'll build on the knowledge you've gained about snaps, by looking at the other types of snap app types and some of the key technology that underpins snaps.

01-service: defining services and commands

The example snap in the first part of this guide delivered a simple terminal application. However, you can deliver any type of application or service with a snap that you might do with a standard deb package. This example shows how you do this for a service:

$ cd ../../10-SNAPS/01-service
$ snapcraft
$ sudo snap install --dangerous hello-world-service_0.1_*.snap --devmode
hello-world-service 0.1 installed
$ snap info hello-world-service
name:      hello-world-service
summary:   "A hello world style nodejs webserver app"
publisher:
description: |
  This example demonstrates how to have nodejs webserver. This is part of the
  snapcraft tour at http://snapcraft.io/create/
tracking:
installed: 0.1 (x1) 11MB devmode
refreshed: 2016-12-15 09:20:04 +0100 CET

--devmode will be explained in the next chapter, so for now open http://localhost:8000 in your browser and you'll find the running web service.

The key components of snapcraft.yaml that enabled this are:

parts:
  hello:
    plugin: nodejs
    source: .

Here the hello part uses the nodejs plugin, a plugin to handle Node.js projects. It takes all the npm information from package.json, including dependencies (if any) and installs them alongside the service's JavaScript code.

apps:
  hello-service:
    command: hello-world
    daemon: simple

Here the hello-service app is declared and specified as a simple (i.e. non-forking) daemon (a service that starts at boot and keeps running on your system). This daemon executes the hello-world command.

02-service-confined: confinement and devmode

You may have noticed the --devmode option was used in snap install. Reinstall the snap without it:

$ sudo snap install --dangerous hello-world-service_0.1_*.snap
error: cannot perform the following tasks:
- Mount snap "hello-world-service" (unset) (snap "hello-world-service" requires devmode or confinement override)

The snap has failed to install because its snapcraft.yaml declares it needs to run in devmode:

$ more snapcraft.yaml
[...]
confinement: devmode

All snaps run confined by default. They're able to access any functions in the snap and data written to their own private writable area, but have restricted and secured access to the system.

The hello world service needs to listen to the network (a restricted and secured part of the system), but hasn't been setup with the additional permissions to do so yet.

To simplify development, --devmode enables you to run the snap outside the confinement sandbox and get unrestricted access to system resources, in this case, listening to the network. However, you do need to tell the snap that it requires devmode, by making a devmode declaration in snapcraft.yaml.

This enables quick iteration to perfect the functionality of the snap before you need to worry about making it work under confinement. It should be noted that snaps uploaded to the store with confinement set to devmode are not visible with the snap find command. However, devmode snaps can still be installed if you specify the exact snap name.

plugs-and-slots: integration with the system and other snaps

However, once you're happy with the functionality of your snap you'll need to focus on its confinement.

To enable snaps to access external resources (or make their own resources available to other snaps) a system of plugs and slots is used. A plug is a request for access to an external interface, while a slot is an interface provided by the snap.

The hello-world-service snap needs to have access to the network, so it can listen for and serve requests, this access is requested using the [network-bind] plug. This plug is then declared in the snapcraft.yaml:

plugs: [network-bind]

Specifying the network-bind plug essentially pokes a hole in the otherwise-complete confinement, telling snappy that this snap requires access to the network through the network-bind interface. The plug then connects to the network-bind slot provided by the base OS as an helper.

So now, without using --devmode, you can run the confined version of the hello world service:

$ cd ../02-service-confined
$ snapcraft
$ sudo snap install --dangerous hello-world-service_0.1_*.snap
hello-world-service 0.1 installed

Open your browser at http://localhost:8000, the application looks identical to the previous example.

Finally, there has been one further very important change made to the snapcraft.yaml:

confinement: strict

To indicate that this snap is now confined.

structure: the directory layout of a snap

Now you've got to grips with some basic and advanced snap features, it's worth gaining an understanding of what's inside a snap. And seeing the snap's content is easy, as it's all in the prime/ directory:

$ ls prime/**
prime/CHANGELOG.md  prime/command-hello-service.wrapper  prime/LICENSE  prime/README.md

prime/bin:
hello-world  node  npm

prime/etc:

prime/include:
node

prime/lib:
node_modules

prime/meta:
snap.yaml

prime/share:
doc  man  systemtap

Most of the content should be self explanatory, the obvious components to deliver the Node.js capabilities and the service. However the two highlighted files may not be so obvious.

wrapper: defining how the binaries are launched

This file (prime/command-hello-service.wrapper) provides a small wrapper that will launch the real binaries: typically a small shell script that sets PATH, LD_LIBRARY_PATH or other runtime specific environment variables. Snapcraft generated these wrappers for you.

metadata: snap.yaml defines the properties of a snap

The snap.yaml file provides the configuration file for the snap's content and is similar to the snapcraft.yaml file, with two exceptions:

$ more prime/meta/snap.yaml
apps:
  hello-service:
    command: command-hello-service.wrapper
    daemon: simple
    plugs:
    - network-bind
  architectures:
  - amd64
confinement: strict
description: This example demonstrates how to have nodejs webserver. This is part
  of the snapcraft tour at http://snapcraft.io/create/
grade: stable
name: hello-world-service
summary: A hello world style nodejs webserver app
version: 0.1

The first exception is that snap.yaml includes a specification of the architecture that the snap has been built for (highlighted). The second difference is that there is no parts definition (as the parts are now in the snap and no longer need to be defined).

Armed with this information you can, if you wish, build your snap package more directly by:

  • Creating a prime/ directory
  • Adding the built assets and code
  • Adding a wrapper file
  • Adding meta/snap.yaml
  • Running snapcraft snap prime/

And you'll have a hand crafted snap file.

Recap

You've now added some depth to your knowledge of snaps and learned about:

  • Delivering services (in addition to apps) in your snaps
  • How all snaps are confined (unable to access system resources) but can be unconfined with --devmode to simplify development
  • How plugs and slots enable snaps to consume resources outside the confinement (or make their own resources available to other snaps)
  • The structure of a snap's content and the option to hand build a snap

20 - PARTS & PLUGINS

You first met parts at the start of this guide and have been working with them in all the examples so far. So, it's time to take a closer look at how parts work and the options they provide you. Then there are a couple of references for plugins, in case you would like to explore further.

parts-intro: a "part" is a reusable component of a snap

Parts are reusable components that can be used when building a snap. They're analogous to a library that you would call in your program. There are three types of parts:

Parts from local source that use local files on your machine. For example (as seen in 10-SNAPS/01-service/):

parts:
  hello:
    plugin: nodejs
    source: .

Parts from online sources, such as github, bzr, tarball, or any code repository you like. For example:

parts:
  godd:
   plugin: go
    source: https://github.com/mvo5/godd.git
  gnu-hello:
   plugin: autotools
    source: http://ftp.gnu.org/gnu/hello/hello-2.10.tar.gz

You've already seen the last part in 00-SNAPCRAFT/01-easy-start/.

To see parts built and shared by others — https://wiki.ubuntu.com/snapcraft/parts

The features of parts shared by this last method are the focus of our next example:

$ cd ../../20-PARTS-PLUGINS/01-reusable-part
$ snapcraft
$ sudo snap install --dangerous hello-world-desktop_0.1_*.snap

Which can be run with the hello-world-desktop command as defined in the snapcraft.yaml: apps:

hello-world-desktop:
  command: qt5-launch hello-world-desktop

But notice this involves an extra tool qt5-launch, which prepares the environment for launching the real application. These qt5-launch and hello-world-desktop commands come from:

parts:
  hello-world:
    plugin: cmake
    source: src/
    build-packages:
      - qtbase5-dev
    stage-packages:
      # Here for the plugins-- they're not linked in automatically.
      - libqt5gui5
    after: [qt5conf] # A part in the cloud

In this part definition:

  • build-packages lists the dependencies needed to build the contents of the snap. These aren't packed into the final snap. qtbase5-dev has been specified here since that package contains the headers, libraries, and tools needed to build the app
  • stage-packages lists the dependencies needed to actually run the contents of the snap. They'll be packed into the final snap. Here, the requirement is for the hello-world part to download and unpack libqt5gui5 with all its dependencies. This method can reuse any of the 48000 .deb packages that traditional Ubuntu provides. It's really that easy: just specify the packages you need embedded into your snap
  • after: [qt5conf] lists the parts that must be staged before this part can be built. However, you may have noticed that this YAML doesn't actually contain the qt5conf part. That's because it's a part in the cloud, which is a way for collaborating, reusing, and sharing already-written parts. The previously mentioned qt5-launch tool comes from the qt5conf part, without any additional effort from you. This way you can make use of and build upon what others have created

sharing: sharing your parts with other developers

If you would like to publish your own parts, you can contribute them on the wiki at https://wiki.ubuntu.com/snapcraft/parts.

Your contributions will always be welcome!

plugins: each part is built using a snapcraft plugin

Plugins tell Snapcraft how to build the content of the snap's parts. You'll probably use the supplied plugins for most of your snaps. However, you can add your own plugins for other languages. Details of how to do this are beyond this get started, but if you would like to explore more here are a couple of plugin examples and basic building details:

autoconf-make: the standard "configure, make, make install" plugin

See an example on Github

Get more info from:

$ snapcraft help autotools
$ snapcraft help make

golang: the Golang plugin

See an example on Github

Get more info from:

$ snapcraft help go
x-plugins: Local plugins

You can build your own plugin and reference it from your part. To do this, copy your plugin into parts/plugins/x-<plugin_name>.py. Get inspiration from https://github.com/ubuntu-core/snapcraft/blob/master/snapcraft/plugins/go.py

30 - STORE

In order to share your snaps with the world, you will need to publish them in the Store. First, create an account on https://myapps.developer.ubuntu.com/. This is your developer portal. Here you can customize how your snap is presented, review each new upload, and control publishing.

You'll need to choose a unique developer namespace as part of the account creation process. In a future version, developer branches of reserved name snaps will be referred to by this name (for example firefox@mark).

Once you've confirmed your account, you're ready to start pushing your snaps to the Store. Make sure Snapcraft and snap know about you by logging in using the email address you provided at signup:

$ snapcraft login
$ sudo snap login you@yourdomain.com

devspace: you can generally publish your own version of a snap

You can publish your own version of a snap, provided you do so under a name you have rights to. New names can be registered by clicking the New Snap button on the developer portal, or by visiting: https://myapps.developer.ubuntu.com/dev/click-apps/register-name/

Use "hello-" suffixed by your name for this exercise (for example hello-mark), since you don't have rights to the "hello" snap name.

names: you can reserve names for software you publish

You can follow the same registration process for names that you have rights.

We can, if needed, rename snaps to ensure they match the expectations of most users. If you are the publisher most users expect for a name then claim it at: https://myapps.developer.ubuntu.com/dev/click-apps/register-name-dispute/

revisions: you get a new revision every time you push

Once you've registered your snap name, go back to your snapcraft.yaml file and update the name field. Run Snapcraft again to quickly rebuild with the new name. With that done, the new revision of your snap can be uploaded to the Store:

$ snapcraft push hello-world_1.0_*.snap

publication: users only see your published revisions

Uploading your snap won't make it immediately available for installation. You'll have to choose the channel(s) you wish to release into.

There are four channels available for your use:

  • Stable is what most users will consume and as the name suggests, should be your most polished and tested versions
  • Candidate is used to vet uploads that should require no further code changes before moving to stable
  • Beta is used to provide preview releases of semi-stable changes
  • Edge is for your most recent changes, probably untested and with no guarantees attached

Open the URL that was returned from snapcraft push or open the snap from the developer portal front page: https://myapps.developer.ubuntu.com

You should see the 1.0 version in the lefthand column. Clicking on that link will bring up further details about this revision. Here you can edit the list of channels it should be released into.

Click on the Edit link next to the Channels field. In the page that appears, select Stable and click Save Changes.

Your upload is now ready to be released into the stable channel. Click the Publish button on the page you return to.

The build you uploaded is now the selected revision in the stable channel, meaning that installs of your snap will select this upload by default.

You can confirm this by installing the snap locally, replacing 'hello-mark' with your snap name:

$ snap install hello-mark
Name        Version  Rev     Developer
hello-mark  1.0      1       sabdfl

Note that, now that the snap has been published in the store, we no longer need the --dangerous flag.

40 - CONFINEMENT

confinement: specify how the snap can be used with the security sandbox

By default snaps run in a restrictive sandbox to ensure that snaps only access the system and other snaps in controlled ways. confinement is used in snapcraft.yaml to specify whether or not the snap is expected to work correctly when the specified interfaces are connected and the snap is confined. You specify strict to indicate the snap works properly when confined or devmode to indicate it only works properly when unconfined. If confinement is unspecified, the snap is assumed to work correctly when confined since developers are expected to develop their snaps for running in the sandbox. You're only allowed to upload snaps to the stable channel when strict confinement is used.

When first making a snap it is often useful to specify confinement: devmode in your YAML like so:

name: hello-world-service
confinement: devmode
apps:
  hello-service:
    command: command-hello-service.wrapper
    daemon: simple
    plugs:
    - network-bind
...

You then would specify --devmode when installing the snap for testing and development (you must always specify --devmode when installing in devmode regardless of how confinement is set in your yaml). Once the application is working well in devmode, install it without specifying --devmode and iterate until it is working properly under confinement. When satisfied, you may indicate it works under confinement with:

name: hello-world-service
confinement: strict
apps:
  hello-service:
    command: command-hello-service.wrapper
    daemon: simple
    plugs:
    - network-bind
...

When debugging your snap when it is installed in strict mode (without --devmode), you can look in /var/log/syslog for sandbox denials. Alternatively, you may use the 'snappy-debug' snap to assist you:

$ sudo snap install snappy-debug
$ sudo snap connect snappy-debug:log-observe ubuntu-core:log-observe
$ sudo /snap/bin/snappy-debug.security scanlog hello-world-service

This scanlog command will tail the syslog and make suggestions on what interfaces to use and suggest changes to make to your snap so it will work within the sandbox.

Learn more

Want to know more about snaps, snapd, Snapcraft and Ubuntu Core?

Contribute

Interested in helping build Snapcraft and expand its reach?