Skip to content

Generate Package Manifest during Installation

Goal

For a few use cases, it might be necessary to generate a package-manifest depending on the ctrlX CORE or on the setup in is installed. This is possible during installation by using snap hook mechanism. This how-to describes how this can be resolved.

The snap build in this examples takes two random words and uses them to generate a menu entry in the ctrlX CORE sidebar. It will look like this:

Precondition

Basic understanding of:

Creating the snap

Choose an empty folder and initialize the snap enviroment by using snapcraft

snapcraft init

Create the folders as in the following image:

dump/
  bin/
  package-assets/
snap/
  hooks/
  snapcraft.yaml
.gitignore

The folder "dump" contains files that will be copied into our snap. This includes scripts inside the "bin" folder and the package-manifest.template used to blueprint when generating the actual package-manifest. Next to the snapcraft.yaml we need the hooks folder where we will later add the "hook" scripts.

After that open the snapcraft.yaml file and edit it as following

name: changing-world
base: core20
version: '1.0.0'
summary: Simple snap with random menu entry
description: |
  This is a sample snap that generates a random menu entry when getting installed or updated

grade: stable
confinement: strict

parts:
  dump:
    plugin: dump
    source: ./dump
    stage-packages:
      - wamerican
      - jq
apps:
  my-service:
    command: bin/service.sh
    daemon: simple

slots:
  package-assets:
    interface: content
    content: package-assets
    source:
      read:
      - $SNAP_DATA/package-assets/$SNAPCRAFT_PROJECT_NAME

The dump part is used to copy the files from the dump folder into the snap and to install two packages used to generate the package-manifest. The "wamerican" package is a dictionary of american english words, the jq package provides the jq tool to manipulate json files using shell.

The slot "package-assets" is similar to the one described in the SDK, the only difference is that it reference $SNAP_DATA instead of $SNAP, to provide a writeable directory.

We added here a my-service app, this is just a simple daemon which logs a string to stdout for demo purpose, see

#!/bin/bash

while true
do
    echo "Hello changing world"
    sleep 10
done

The script

We will now add the script in the "dump/bin" folder by creating a file called "generate_manifest.sh" and make it executable (chmod +x)

dump/
  bin/
    generate_manifest.sh

Here you will find the content

#!/bin/bash -x

# Update json
NAME=$(shuf -n2  $SNAP/usr/share/dict/words | tr '\n' ' ')
mkdir -p $SNAP_DATA/package-assets/$SNAP_NAME
$SNAP/usr/bin/jq ".menus.sidebar[].title = \"$NAME\"" \
    $SNAP/package-assets/changing-world.package-manifest.json.template > $SNAP_DATA/package-assets/changing-world/changing-world.package-manifest.json

In line 4 we use shuf to select two random words from the dictionary and tr to bring them into one line and store it in "NAME".

In line 5 we prepare the directory to ensure it exists.

In line 6-7 we use jq to change the existing menus.sidebar.title of the template to "NAME" and write it into the package-assets folder.

To make this work we need to create the script template in the "dump/package-assets" folder:

changing-world.package-manifest.json.template

{
  "$schema": "https://json-schema.boschrexroth.com/ctrlx-automation/ctrlx-core/apps/package-manifest/package-manifest.v1.3.schema.json",
  "version": "1.0.0",
  "id": "changing-world",
  "menus": {
    "sidebar": [
      {
        "id": "changing-world",
        "title": "",
        "icon": "bosch-ic-automation",
        "link": "0"
      }
    ]
  }
}

As you can see, the title property is empty.

The hooks

Now we need to execute the script to generate the package-manifest on the installation and every update of the snap. So we need to add the corresponding hooks:

snap/
  hooks/
    install
    post-refresh

Both need to be executable (chmod +x). Both have the same content as below:

#!/bin/bash

# Generate manifest
$SNAP/snap/command-chain/snapcraft-runner  generate_manifest.sh

It executes the generate_manifest.sh mentioned above using the snaps environment by using the snapcraft-runner, it sets all required environment variables (e.g. PATH). It is generated by the snap itself if you have defined an app in your snap.

Interface definitions

Interfaces (slots and plugs) can be declared in two different ways:

  • Specific for each app and hook (Preferred solution)
  • Globally to be valid for all defined apps and hooks

Examples

Specific declaration:

Each app and each hook defines all needed interfaces.

apps:
  example:
    command: bin/sh
    plugs:
      wayland:
      x11:
  example2:
    command: bin/sh2
    plugs:
      wayland:
      x11:
hooks:
  configure:
    plugs:
      wayland:
      x11:

Globally defined:

The plugs wayland and x11 are valid for the apps example, example2 as well as the configure-hook.

apps:
  example:
    command: bin/sh
  example2:
    command: bin/sh2

hooks:
  configure:

plugs:
  wayland:
  x11:

Both definitions lead to the same result.

Nevertheless if you mix these two, this could lead to unexpected behaviour.

Bad Example:

In the following yaml, the global plugs wayland and x11 can only be accessed by the example app. The app example2 and the configure hook have no interfaces declared, which is probably not the desired result.

apps:
  example:
    command: bin/sh
    plugs:
      wayland:
      x11:
      opengl:
  example2:
    command: bin/sh2
hooks:
  configure:
plugs:
  wayland:
  x11:

Therefore declaring all interfaces specific for each app and hook should be the preferred solution. If your snapcraft.yaml does not contain any apps or hooks at all, then declaring interfaces globally is the right approach.

Build and run

Now build the snap using "snapcraft" and install it on your ctrlX CORE, on every reinstall, it will change the name of the menu entry.

Thats it.