Skip to content

Commit f54f4db

Browse files
committed
add raw markdown text for the Create a new snap tutorial.
1 parent e1fd1dd commit f54f4db

File tree

1 file changed

+307
-0
lines changed

1 file changed

+307
-0
lines changed
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
In this tutorial you will build a snap package for a Python application called [liquitctl](https://github.com/liquidctl/liquidctl) using Snapcraft, which is the build ecosystem for creating, publishing and maintaining snaps.
2+
3+
The concepts covered in this tutorial are applicable to all snaps, regardless of their complexity. We'll cover everything from creating the build environment and the configuration file, to troubleshooting missing libraries and which interfaces may be required.
4+
5+
- [Requirements](#heading--requirements)
6+
- 1\. [Snapcraft setup](#heading--setup)
7+
* 1.1 [Snapcraft build environment](#heading--build-environment)
8+
* 1.2 [Create a YAML template](#heading--yaml)
9+
* 1.3 [Build a template snap](#heading--build-template)
10+
- 2\. [Modify the snapcraft.yaml](#heading--modify)
11+
* 2.1 [Create a new part](#heading--part)
12+
* 2.2 [Build the part](#heading--build-part)
13+
- 3\. [Create an app section](#heading--expose)
14+
* 3.1 [Install the snap in developer mode](#heading--developer-mode)
15+
- 4\. [Test the snap](#heading--test)
16+
* 4.1 [Missing dependencies](#heading--missing)
17+
* 4.2 [System access](#heading--system-access)
18+
- 5\. [Update the confinement level](#heading--confinement)
19+
20+
<a id='heading--requirements'></a>
21+
22+
### Requirements
23+
24+
Snapcraft can be installed on various Linux distributions, as well as on macOS and Windows operating systems. For this tutorial, however, we recommend using [Ubuntu 22.04 LTS (Jammy Jellyfish)](https://releases.ubuntu.com/jammy/) or later.
25+
26+
This tutorial does not require any programming or specific Linux knowledge, but you will need some familiarity with the Linux command line. All the instructions are run as commands from the [Terminal](https://ubuntu.com/tutorials/command-line-for-beginners#1-overview) application.
27+
28+
You system also needs to have at least 20GB of storage available.
29+
30+
<a id='heading--setup'></a>
31+
32+
## 1. Snapcraft setup
33+
34+
From the terminal, type the following to install Snapcraft:
35+
36+
```bash
37+
sudo snap install snapcraft --classic
38+
```
39+
40+
<h3 id='heading--build-environment'>1.1 Snapcraft build environment</h3>
41+
42+
Snapcraft builds snaps within an [LXD](https://ubuntu.com/lxd) container environment by default. This keeps a snap build isolated from your system and ensures that any dependencies the snap requires are only provided by the build process.
43+
44+
To install LXD, type the following:
45+
46+
```bash
47+
sudo snap install lxd
48+
```
49+
50+
You also need to add your current user to the `lxd` group to give yourself permission to access its resources:
51+
52+
```bash
53+
sudo usermod -a -G lxd $USER
54+
```
55+
56+
Logout and re-open your user session for the new group to become active.
57+
58+
LXD can now be initialised with the 'lxd init' command:
59+
60+
```bash
61+
lxd init --minimal
62+
```
63+
64+
See [How to install LXD](https://documentation.ubuntu.com/lxd/en/latest/installing/#installing) for further installation options and troubleshooting.
65+
66+
<h3 id='heading--yaml'>1.2 Create a YAML template</h3>
67+
68+
Start by creating a new directory to hold the snap data, and then `cd` into this directory:
69+
70+
```bash
71+
mkdir mysnap
72+
cd mysnap
73+
```
74+
75+
To create a new YAML template for a working snap, run `snapcraft init` within this directory:
76+
77+
```
78+
snapcraft init
79+
```
80+
81+
The YAML template file is called `snapcraft.yaml` and it can be found within a new `snap` sub-directory.
82+
83+
<h3 id='heading--build-template'>1.3 Build a template snap</h3>
84+
85+
The template file contains enough information to build a snap without any further modifications. This can be accomplished by running the `snapcraft` command in the parent directory:
86+
87+
```
88+
snapcraft
89+
```
90+
91+
In the background, Snapcraft will create a new LXD container, install into this whatever the template file contains, and build a snap. The output will look similar to the following and the resultant snap can be found in the current directory:
92+
93+
```bash
94+
Launching instance...
95+
Executed: pull my-part
96+
Executed: build my-part
97+
Executed: stage my-part
98+
Executed: prime my-part
99+
Executed parts lifecycle
100+
Generated snap metadata
101+
Created snap package my-snap-name_0.1_amd64.snap
102+
```
103+
104+
<h2 id='heading--modify'>2. Modify the snapcraft.yaml</h2>
105+
106+
The `snap/snapcraft.yaml` file describes the application, its dependencies and how it should be built. It currently contains the following metadata:
107+
108+
```yaml
109+
name: my-snap-name # you probably want to 'snapcraft register <name>'
110+
base: core22 # the base snap is the execution environment for this snap
111+
version: '0.1' # just for humans, typically '1.2+git' or '1.3.2'
112+
summary: Single-line elevator pitch for your amazing snap # 79 char long summary
113+
description: |
114+
This is my-snap's description. You have a paragraph or two to tell the
115+
most important story about your snap. Keep it under 100 words though,
116+
we live in tweetspace and your description wants to look good in the snap
117+
store.
118+
119+
grade: devel # must be 'stable' to release into candidate/stable channels
120+
confinement: devmode # use 'strict' once you have the right plugs and slots
121+
122+
parts:
123+
my-part:
124+
# See 'snapcraft plugins'
125+
plugin: nil
126+
```
127+
128+
The above metadata is enough to build a snap, but the snap has no functionality. To create a functional snap, we need expand the `parts:` section and add a new section called `app:`.
129+
130+
<h3 id='heading--part'>2.1 Create a new part</h2>
131+
132+
A snap is assembled from one or more _parts_ and each part describes a component required for the snap to function. This component could be a library or an executable, for example, and parts use _plugins_ to construct and organise whatever components are needed.
133+
134+
Our application is built with Python, and Snapcraft includes a [Python plugin](/t/the-python-plugin/8529) to automatically handle its dependencies and install requirements.
135+
136+
Open `snap/snapcraft.yaml` with your favourite text editor and navigate to the bottom line, `plugin: nil`. Replace `nil` with `python` and add the following `source-type` and `source` lines:
137+
138+
```yaml
139+
plugin: python
140+
source-type: git
141+
source: https://github.com/liquidctl/liquidctl
142+
```
143+
144+
This is all that is required for Snapcraft to access, clone locally, and build the upstream source code of the project.
145+
146+
Running `snapcraft` again would build the application and create a new snap. However, this new snap would still not function because we have not yet told Snapcraft which executable to expose and run.
147+
148+
<h3 id='heading--build-part'>2.2 Build the part</h3>
149+
150+
A snap is built in several stages, collectively known as the _parts lifecycle_, as shown in Snapcraft's build output.
151+
152+
1. **Pull** retrieves whatever is required for each part to be built
153+
1. **Build** constructs each part using each respective plugins
154+
1. **Stage** copies built components into a shared staging area
155+
1. **Prime** moves staged files and directories into their final locations
156+
157+
This is important because you can stop a build at any stage to look inside the build container.
158+
159+
Run the following `snapcraft` command to both start a new snap build and run the build up to the _prime_ step. The command will also open a shell within the build environment.
160+
161+
```bash
162+
snapcraft prime --shell
163+
```
164+
165+
> :information_source: If you've already built the same snap, run `snapcraft clean` first to reset the build environment.
166+
167+
168+
From the build shell prompt inside the container, type `cd $HOME` to change to Snapcraft's build directory, and `ls` to see its contents:
169+
170+
```
171+
environment.sh parts prime project snap stage
172+
```
173+
174+
These directories hold the data for each build stage, while the `environments.sh` file contains the environment variable configuration.
175+
176+
The executable name is `liquidctl`, which we can now search for:
177+
178+
```bash
179+
$ find . -name liquidctl
180+
./project/squashfs-root/bin/liquidctl
181+
./parts/my-part/build/build/lib/liquidctl
182+
./parts/my-part/build/liquidctl
183+
./parts/my-part/src/liquidctl
184+
./parts/my-part/install/bin/liquidctl
185+
./parts/my-part/install/lib/python3.10/site-packages/liquidctl
186+
./stage/bin/liquidctl
187+
./stage/lib/python3.10/site-packages/liquidctl
188+
```
189+
190+
The above output shows how the Python plugin has built and installed the executable within the container. The final binary is in `./stage/bin`.
191+
192+
Type `exit` to quit the build environment shell.
193+
194+
<a id='heading--expose'></a>
195+
196+
## 3. Create an app section
197+
198+
Using the location of the binary, and to permit access, it needs to be declared within an `app:` section of the snapcraft.yaml:
199+
200+
```yaml
201+
apps:
202+
my-snap-name:
203+
command: bin/liquidctl
204+
```
205+
206+
If the sub-section name matches the snap name it becomes the default executable for the snap.
207+
208+
This means that when our snap is installed, typing `my-snap-name` will run the `bin/liquidctl` binary . It's more usual for a snap name to match the name of the executable.
209+
210+
<h3 id='heading--developer-mode'>3.1 Install the snap in developer mode</h3>
211+
212+
The snap can now be rebuilt to produce what should be an installable and executable snap package.
213+
214+
Running `snapcraft` will produce a snap package called `my-snap-name_0.1_amd64.snap` (depending on your system architecture).
215+
216+
```bash
217+
Created snap package my-snap-name_0.1_amd64.snap
218+
```
219+
220+
This snap package can be installed locally with the snap command, invoking both `--devmode` and `--dangerous` options to permit system access and installation without verification:
221+
222+
```bash
223+
sudo snap install ./my-snap-name_0.1_amd64.snap --dangerous --devmode
224+
```
225+
226+
With the snap installed, the `my-snap-name` command can now be run to execute `liquidctl`:
227+
228+
```bash
229+
$ my-snap-name
230+
Usage:
231+
liquidctl [options] list
232+
liquidctl [options] initialize [all]
233+
[...]
234+
```
235+
236+
<a id='heading--test'></a>
237+
238+
## 4. Test the snap
239+
240+
To test a snap properly, it needs to be run as intended. The `liquidctl` command, for example, accesses USB devices to read and set proprietary sensor, fan and LEDs values.
241+
242+
Even without such devices connected, the `list` will attempt to discover any connected devices:
243+
244+
```bash
245+
$ my-snap-name list
246+
usb.core.NoBackendError: No backend available
247+
```
248+
249+
This produces an error, and unless you know the project code, it's difficult to say from the error whether it's a problem with the snap, a problem with not having the hardware, or a problem with our test system.
250+
251+
<h3 id='heading--missing'>4.1 Missing dependencies</h3>
252+
253+
If you're a Python developer, or reasonably good at searching the internet, it's relatively straightforward to work out that the `usb.core.NoBackendError` issue is caused by a missing `python3-usb` package. This can be added through a new `stage-packages` section for the part. Stage packages are those packages you wish to be installed alongside the application:
254+
255+
```yaml
256+
stage-packages:
257+
- python3-usb
258+
```
259+
260+
After building and installing the new snap, the error will have gone. If you had any compatible devices, you would now see output similar to the following:
261+
262+
```bash
263+
Device #0: Corsair HX750i
264+
Device #1: Corsair Hydro H100i v2
265+
```
266+
267+
<h3 id='heading--system-access'>4.2 System access</h3>
268+
269+
Running our snap with real hardware will result in an insufficient permissions error and this is because snaps limit system access by default. [Interfaces](/t/supported-interfaces/7744) are used to permit access to individual resources through _plugs_ and _slots_.
270+
271+
Plugs declares which [interfaces](/t/supported-interfaces/7744) an app needs to function, such as [home](/t/the-home-interface/7838) to access local files, or [network](/t/the-network-interface/7880) to access the network. In this case, _liquidctl_ needs access to USB devices, which can be satisfied with the [raw-usb](/t/the-raw-usb-interface/7908) interface for device input and output, [uhid](/t/the-uhid-interface/7931) for user access, and [hardware-observe](/t/the-hardware-observe-interface/7833) to enable the system to see which devices are connected.
272+
273+
These can added with the creation of a new `plugs:` sections beneath the command name for the app:
274+
275+
```
276+
apps:
277+
my-snap-name:
278+
command: bin/liquidctl
279+
plugs:
280+
- raw-usb
281+
- uhid
282+
- hardware-observe
283+
```
284+
285+
When the snap is installed, the interfaces are can be activated manually with the following commands:
286+
287+
288+
```bash
289+
sudo snap connect my-snap-name:uhid
290+
sudo snap connect my-snap-name:raw-usb
291+
sudo snap connect my-snap-name:hardware-observe
292+
```
293+
294+
The snap can now be run without encountering any further errors or missing functionality.
295+
296+
<a id='heading--confinement'></a>
297+
298+
## 5. Update confinement level
299+
300+
The final step when building any snap is to change its grade to `stable` and its confinement to `strict`. Both of these values are at the top of the snapcraft.yaml file and they default to developer-friendly options so that errors only report themselves rather than stop functionality. They're useful when building a snap but are far less secure when you want to share it.
301+
302+
```yaml
303+
grade: stable # must be 'stable' to release into candidate/stable channels
304+
confinement: strict # use 'strict' once you have the right plugs and slots
305+
```
306+
307+
The snap is now fully functional and can be rebuilt and installed. At this point, your own snaps could be published and shared.

0 commit comments

Comments
 (0)