How to snap a self-contained rust cli app

A self-contained rust app is an app that can be built from source with a single cargo build command. This also means that the snapcraft rust plugin defaults are likely to work, and the final snapcraft.yaml is likely to be minimal.

Starting point

Let’s take gitui for example. If you follow the rust snap tutorial, you will end up with a snapcraft.yaml similar to this one:

name: gitui
version: '0.24.0'
summary: Terminal UI for git
description: |
  Blazing 💥 fast terminal-ui for git written in rust 🦀
license: MIT
website: https://github.com/extrawurst/gitui
confinement: devmode
base: core22

parts:
  gitui:
    plugin: rust
    source: https://github.com/extrawurst/gitui.git
    source-tag: 'v0.24.0'
    source-depth: 1

apps:
  gitui:
    command: bin/gitui
  • The name should ideally match the project’s name, but note that it is unique within the ecosystem.
  • For this example we can just copy the summary and description as-is from the project page.
  • The version field should match the app’s version being built. The resulting snap will be named gitui_0.24.0_amd64.snap, and when installed, the snap info gitui will show that version number. It should match the version displayed when running gitui --version. One way to achieve this is to rely on the maintainer’s tagging practice and always make sure that the .version field matches the .parts.gitui.source-tag field.

Test the snap

Build the snap by simply running snapcraft in the same folder. This will create a file called gitui_0.24.0_amd64.snap. Install it:

sudo snap install --devmode *.snap

Now, gitui is ready to use

$ which gitui
/snap/bin/gitui

$ gitui --version
gitui 0.24.0

Choose the right confinement

Having confinement: devmode is fine for testing and debugging, but for a snap to be public it must either be classically or strictly confined.

Arguably, in gitui's case, the benefit of having a strictly confined snap outweighs potential usability disadvantages.

For comparison:

  • Text editor snaps, e.g. nvim and helix, are classically confined. This makes sense because one would want one’s text editor to be able to display any file on the system.
  • Gaming apps such as 0ad and dosbox-x are strictly confined, because they don’t really need access to the entire system.
  • Other git UIs such as gitkraken and lazygit have classic and strict confinement, respectively.

Let’s take a look at the list of interfaces we would need to plug into the snap for it to work well in strict confinement:

Interface Description Rationale
home access non-hidden files in the home directory Most repos are likely to reside somewhere under $HOME
personal-files read or write files in the user’s home directory Read .gitconfig etc.
network enables network access Pull, push, etc.
removable-media read/write files on removable storage devices Some users may want to interact with repos on removable devices

If you’re not too familiar with the app, you can use strace to try to figure out what interfaces the app may need. For example:

strace -e trace=openat,newfstatat git fetch

Build a strictly confined snap

To confine the app, we need to:

  • Specify confinement: strict.
  • Declare some plugs (interfaces), such as personal-files, at the root level.
  • List all plugs per app under the apps section.
name: gitui
version: '0.24.0'
summary: Terminal UI for git
description: |
  Blazing 💥 fast terminal-ui for git written in rust 🦀
license: MIT
website: https://github.com/extrawurst/gitui
confinement: strict
base: core22

parts:
  gitui:
    plugin: rust
    source: https://github.com/extrawurst/gitui.git
    source-tag: 'v0.24.0'
    source-depth: 1

plugs:
  gitfiles:
    interface: personal-files
    read:
      - $HOME/.git-credentials
      - $HOME/.gitconfig

apps:
  gitui:
    command: bin/gitui
    plugs:
      - home
      - gitfiles
      - removable-media
      - network

The snap is not signed (yet), so needs to be installed with --dangerous (previously, --devmode implied --dangerous):

sudo snap install --dangerous *.snap
2 Likes