Proposal: support command-chain in apps and hooks

Background

Over the years it has become a fact of life that a snapped application requires a good chunk of environment variables and in some cases setup code in order to run successfully. Today, the snapcraft CLI satisfies this requirement by generating shell wrappers for defined apps, which have a few problems:

  • They are opaque, complex, and confusing to the developer introspecting the snap once built
  • They are completely transparent to snapd: it doesn’t know about them. As a result, things like snap run --shell can’t take them into account, dropping the developer into an environment that isn’t truly representative of the app.

Snapd supports an environment keyword for apps (although not hooks, which should probably be changed) that snapcraft could be using for the environment variables it needs to define. However, that doesn’t cover the use-case of having logic that needs to run before the app, such as desktop helpers, ROS setup scripts, etc.

Proposal

Snapd should grow support for chaining commands together in order to actually run an app or a hook, where the syntax would look like this:

# <snip metadata

# For an app
apps:
  my-app:
    command-chain: [command1, command2]
    command: my-app-command

# For a hook
hooks:
  configure:
    command-chain: [command1, command2]

# <snip parts>

When running my-app or the configure hook, it actually ends up running the command chain in order before finally executing the command, e.g. command1 command2 my-app-command. Additionally, when running snap run --shell my-app-command, snapd will run the command chain prior to dropping the user into a shell, e.g. command1 command2 bash.

With this proposal implemented, snapcraft can move away from wrappers completely and start using a combination of the environment and command-chain keywords.

While I like the idea, I’m curious about any potential arguments passing into a chained command. For example, if I want to pass an argument specific to command1 how would this be achieved?

Consider if I wanted the following chain:

command1 an-argument-for-command1 command2 my-app-command

@lucyllewy while I don’t have any immediate objections to just throwing args into the command chain element, is there actually a use-case for doing it? When would you as a snap developer choose to specify a chain with arguments instead of just altering your app?

I’m thinking of use-cases where we have a shared part which is combined with a launch script which takes arguments to tailor it’s behaviour - consider currently our desktop helpers are separate builds with slightly different script content. I’d like to see the build of that script simplified and the conditional parts included as logic in the launcher rather than “magic from the build”. So we’d have a command like desktop-launch --gtk3 or maybe just desktop-launch gtk3. The idea here is the template will take care of including the desktop-helpers part and adding the desktop-launch gtk3 command to the chain so the snap developer just includes a reference to desktop-gtk3 template (note, template, not remote-part - the remote-part is added by the template’s yaml)

Yeah that’s fair. Doesn’t seem to add any complication to me, but of course, I’m writing this proposal for someone else’s project :wink: . We’ll see what they say!

Took a crack at this here:

@lucyllewy I’m afraid command-chain args was NAK’d.

Hook support:

@kyrofa Just +1d the second PR. Looks very nice.

There’s one minor that I’d like to address quickly before the next release: can we please drop the --skip-command-chain option as it is now? I’d like to introduce a variant of it that allows defining the depth instead. Something like:

# Equivalent to --skip-command-chain:
$ snap run --chain=0 foo

# Run only the first command in the chain:
$ snap run --chain=1 foo

# Run all but the last command in the chain:
$ snap run --chain=-1 foo

How does that sound?

It’s okay to not do that now as well, but let’s please not have the current --skip version so we can agree on something along those lines in the near future.

Sure @niemeyer, here’s a quick PR that removes the skip capability. We can revisit after higher-priority items are out of the way:

Thanks, that’s merged.

FYI, now that the relevant PRs have landed, I’ve updated the review-tools for command-chain. This isn’t in production yet, but on its way.

did you see I tried to upload gnome-twitch on Monday (or Tuesday?) with a command-chain reference? I was just curious whether that was what prompted you to look at the review tools because it was flagged due to the “unsupported” feature :slight_smile:

1 Like

Thanks @jdstrand, I’m sorry, I definitely should have had you in the loop on that one and totally dropped the ball.

Yes, this is what alerted me to the feature. I manually approved it but you’ll need to release it still.

2 Likes

Perhaps I do something wrong or misunderstand what the command-chain fature should accomplish, but it seems, that the command-chain syntax will not accept spaces(?)
So this will work:
command-chain: [$SNAP/bin/command1.bash, $SNAP/bin/command2.bash]

But this will not:
command-chain: [$SNAP/bin/command1.bash -v, $SNAP/bin/command2.bash param1 param2]

@let4hi please see this comment above:

I’m also wondering why you omitted the args in the implementation. Most of the commands I’m thinking to put there have arguments. What commands are you referring to with [command1, command2]? Other apps?

Overall I’m trying to understand this quote:

Daniel had a good response to that question, and I wrote support for them as a result, but that support was removed during review.