Proposal: add command-chain to apps instead of generating opaque wrappers

Of course. Run snapcraft prime with this snapcraft.yaml:

name: test-snap
version: '0.1'
summary: summary
description: description

grade: devel
confinement: strict

apps:
  hello:
    command: echo "hello"

parts:
  my-part:
    plugin: nil

Even this ultra-simple example rewrites the command to use a wrapper:

$ cat prime/meta/snap.yaml | grep hello
  hello:
    command: command-hello.wrapper

$ cat prime/command-hello.wrapper 
#!/bin/sh
export PATH="$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH"
export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH
exec "echo" "hello" "$@"

In many cases, such as this one, the wrapper consists entirely of environment variables, which nowadays should be using the environment property (although hooks don’t support that yet). However, depending on the plugins used, the wrappers can be far more complex, well beyond just exporting variables. Avert your eyes:

#!/bin/sh
export PATH="$SNAP/usr/sbin:$SNAP/usr/bin:$SNAP/sbin:$SNAP/bin:$PATH"
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$SNAP/lib:$SNAP/usr/lib:$SNAP/lib/x86_64-linux-gnu:$SNAP/usr/lib/x86_64-linux-gnu"
export ROS_MASTER_URI=http://localhost:11311
export ROS_HOME=${SNAP_USER_DATA:-/tmp}/ros
export LC_ALL=C.UTF-8
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SNAP/lib:$SNAP/usr/lib:$SNAP/lib/x86_64-linux-gnu:$SNAP/usr/lib/x86_64-linux-gnu
export PYTHONPATH=$SNAP/usr/lib/python2.7/dist-packages${PYTHONPATH:+:$PYTHONPATH}
export PATH=$PATH:$SNAP/usr/bin


# Shell quote arbitrary string by replacing every occurrence of '
# with '\'', then put ' at the beginning and end of the string.
# Prepare yourself, fun regex ahead.
quote()
{
    for i; do
        printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
    done
    echo " "
}

BACKUP_ARGS=$(quote "$@")
set --

if [ -f $SNAP/opt/ros/kinetic/setup.sh ]; then
    _CATKIN_SETUP_DIR=$SNAP/opt/ros/kinetic . $SNAP/opt/ros/kinetic/setup.sh
fi

eval "set -- $BACKUP_ARGS"

export LD_LIBRARY_PATH="$SNAP/opt/ros/kinetic/lib:$SNAP/usr/lib:$SNAP/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH=$SNAP_LIBRARY_PATH:$LD_LIBRARY_PATH
exec "roslaunch" "edukit_bot" "edukit_bot.launch" "$@"

The more plugins involved, the nastier it gets. This proposal is to stop dumping everything into a nasty wrapper, and simply provide an executable instead, telling snapd that it must run before the app by using the command-chain keyword.

This comes back to the templates proposal as well: we want to keep templates solely to generating YAML, not throwing more shell code in an opaque, already-nasty wrapper. This empowers it to simply add a command to the chain.

2 Likes