Snapcraft hook support

A hook is an executable file that runs within a snap’s confined environment when a specific action occurs.

The filename of the executable is based on the name of the hook. If this file exists, snapd will execute the file when required by that hook’s action. See Supported snap hooks for more details on which hooks are supported.

Snapcraft can integrate hooks into a snap using two methods:

1. Using a project-wide snap/hooks directory

The hook executable can be placed in a directory called snap/hooks relative to where the snapcraft command is executed. This will typically mean creating a hooks directory in the same directory that contains the snapcraft.yaml file for your project. The following, for example, shows the location of a configure hook executable:

.
└── snap
    ├── hooks
    │   └── configure
    └── snapcraft.yaml

Hook executables in snap/hooks are automatically copied into the snap during Snapcraft’s prime step (see Parts lifecycle for details).

2. From within snapcraft.yaml

A part within snapcraft.yaml can also generate and install hook executables into $SNAPCRAFT_PART_INSTALL/snap/hooks/. These are automatically copied into the snap during the prime step.

configure-hook:
    plugin: dump
    source: configure-again/
    organize:
        configure: snap/hooks/configure

Plugs

By default, hooks run with no plugs. If a hook needs more privileges, it can use the top-level hooks attribute in snapcraft.yaml to request plugs:

hooks: # Top-level YAML attribute, parallel to `apps`
  configure: # Hook name, corresponds to executable name
    plugs: [network] # Or any other plugs required by this hook

Hooks are called with no parameters. See Using the snapctl tool for details on the internal command they can use to provide and retrieve data to and from snapd.

1 Like

The code example at 2 doesn’t seem to have any Python plugin :thinking: Does it mean I just need to add something like

organize:
        configure: snap/hooks/configure

to whatever part (regardless of the plugin) to have my hooks in the right place and aware of my binaries? :blush:

Regardless of env variables, can I access and run my binaries or do I need a part-specific hook?

I’m curious about Step 2 above, which clearly copies the configure hook script from the source location configure-again/. But if you already have the hook source available to you, why not just use Technique 1 and put it in snap/hooks to begin with? I’m having trouble seeing the value of Technique 2 here.

1 Like

I wonder whether the passages above are valid? Is “the part’s environment” mean the build-environment defined in the providing part’s definition, or the environment applied by the plugin building these parts(like python)?

I unable to find PYTHONPATH or similar environment variables in the python plugin’s source code.

I’ve tweaked this page slightly after experimenting with environment scope - I agree that the build environment is only going to be relevant during the build for that part (which may be used to inform the values used to create a hook script, I’m guessing) but not when the hook is eventually executed. I edited option two slightly to suggest a part can generate its own hooks, rather than simply dump them from outside.

So, to be clear, the only real reason to use Technique 2 is if you, in some way, needed to dynamically generate the contents of the hook script during snapcraft run time.

I think it also helps if hooks aren’t (or can’t be) sourced from snap/hooks, for whatever reason.

Is there documentation on this that I haven’t found yet? I’ve just figured out that the “user” in $SNAP_USER_COMMON during the pre-refresh hook is root rather than the installed snap user. I’d like to know more about what all the environment variables are during hooks so I can accomplish my goal of backup up certain files in $SNAP_USER_COMMON of the installed snap before a refresh.

After reading through a few more posts I’ve decided to make user-specific tasks happen as part of the snap’s own execution rather than through a root-level hook. That seems to be the more appropriate solution.

It would still be helpful to explicitly say in the docs that the snap hooks run as root user. It’s obvious now in hindsight, but it wasn’t to me in the beginning. Maybe snap could also consider user-level hooks? Somehow the system knows about specific users when making snapshots, so maybe that knowledge could be leveraged for hooks, too?

Hi! Thanks for commenting on this. You’re right that we don’t make it clear hooks are running as root (through snapd, which is always running as root). I’ll add something to explain it better.

I could also tie this closer to our environment variables document, especially SNAP_USER_COMMON which as you’ve found is within a user’s home directory.

In that last section, Plugs, in addition to plugs subkeys, hooks supports three other subkeys – passthrough, environment and command-chain, https://github.com/snapcore/snapcraft/blob/main/snapcraft/projects.py#L354. Should these not be mentioned as well? If not here, where?