<a id="exp-sdk-hooks"></a>

# Runtime hooks

<!-- @artefact SDK -->
<!-- @artefact SDK hook -->
<!-- @artefact restore-state -->
<!-- @artefact save-state -->
<!-- @artefact setup-base -->
<!-- @artefact setup-project -->
<!-- @artefact check-health -->

A *hook* is a bash script
that an SDK ships under its `hooks/` directory.
**Workshop** runs the script at a specific point in the workshop lifecycle
to give the SDK a chance to set up its environment,
report its health,
or persist data across a refresh.
Without hooks,
an SDK is a passive bundle of files;
hooks are how it participates in the running workshop.

**SDKcraft** enumerates an SDK’s hooks automatically when packing it,
so they do not need to be listed in the
[SDK definition](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-sdk-definition).

## The five hooks

**Workshop** recognizes five hooks,
each one answering a different question about the SDK’s relationship
to the workshop.

- `setup-base`:
  System-level preparation for the SDK,
  run as `root`
  inside the workshop container
  before the project directory is mounted
  and before any plug or slot is connected.
  It runs once when the SDK is first installed
  and again when its revision changes.
  A refresh reuses the post-`setup-base`
  [base snapshot](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-workshop-definition-sdks)
  instead of running the hook again,
  but only if the base image
  and the SDKs listed above this one in the definition are unchanged;
  a change to any of these invalidates the snapshot,
  causing `setup-base` to re-run for this SDK
  and the ones below it in the definition.
- `setup-project`:
  Per-project preparation,
  run as the `workshop` user
  after the project directory is mounted
  and after auto-connect has finished.
  This is the SDK’s chance to do setup
  that depends on the project directory,
  the `workshop` user’s home directory,
  or the slot resources the SDK now has access to.
  Use it to *prepare the environment*,
  not to build the project itself;
  daily build activities belong in [actions](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-workshop-definition-actions).
- `check-health`:
  The SDK’s report on whether it can operate in this workshop.
  It runs after `setup-project`
  (and, on a refresh, after `restore-state`),
  reports its result through **workshopctl set-health**,
  and controls whether the SDK becomes *Ready*.
  See [Talking back with workshopctl](#exp-workshopctl-health) below.
- `save-state`:
  Runs during a refresh,
  on the *old* SDK revision,
  before the workshop is stopped and rebuilt.
  A refresh discards the workshop’s writable filesystem,
  so anything the SDK keeps there
  (caches, generated configuration, state
  that the new revision expects to find)
  needs to be explicitly copied under `$SDK_STATE_DIR`
  to survive.
- `restore-state`:
  Runs during the same refresh as `save-state`,
  but on the *new* SDK revision,
  after every SDK’s `setup-project` has finished,
  and reads back from `$SDK_STATE_DIR`.

## Execution contract

Every hook runs in a non-interactive **bash** login session
with the `errexit` and `pipefail` options set,
which means a non-zero exit code
from any command,
or any pipe stage,
ends the hook and surfaces the failure
to the workshop change that triggered it.
When `--verbose` is passed to
**workshop launch** or **workshop refresh**,
the `xtrace` option is also set,
making each command in the script visible in the change output.

The runner provides the `$SDK` directory variable to every hook;
beyond that,
some hooks see additional variables in scope.
The privilege, working directory, and extra variables
differ by hook
(`None` in the table below means *no extras beyond* `$SDK`,
not no environment at all):

| Hook            | Working directory            | Runs as             | Extra environment                                                |
|-----------------|------------------------------|---------------------|------------------------------------------------------------------|
| `setup-base`    | The SDK’s `hooks/` directory | `root`              | None                                                             |
| `setup-project` | `/project/`                  | The `workshop` user | `$HOME`,<br/>`$XDG_RUNTIME_DIR`,<br/>`$DBUS_SESSION_BUS_ADDRESS` |
| `check-health`  | The SDK’s `hooks/` directory | `root`              | None                                                             |
| `save-state`    | The SDK’s `hooks/` directory | `root`              | `$SDK_STATE_DIR`                                                 |
| `restore-state` | The SDK’s `hooks/` directory | `root`              | `$SDK_STATE_DIR`                                                 |

For the precise launch and refresh stages at which each hook fires,
see the [hook reference](https://ubuntu.com/workshop/docs//reference/sdks.md#ref-sdk-hooks).

## Ordering across SDKs

When several SDKs in a workshop define the same hook,
**Workshop** runs them sequentially,
not in parallel,
and waits for each hook to finish
before starting the next.

The order is fixed:
the [system SDK](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-system-sdk) first,
then the user-listed SDKs in the order they appear
in the [workshop definition](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-workshop-definition),
then the sketch SDK if present.
There is no dependency resolution between SDKs;
the listing order is the contract.
If an SDK relies on another SDK’s `setup-base` having finished,
list it later in the workshop definition.

<a id="exp-workshopctl"></a>

<a id="exp-workshopctl-health"></a>

## Talking back with workshopctl

<!-- @artefact workshopctl -->
<!-- @artefact workshop status -->
<!-- @artefact SDK health -->

From inside a hook,
the SDK can call **workshopctl**
to interact with the workshop daemon.

The only possible use at the moment is **workshopctl set-health** from
`check-health`,
which sets the SDK’s health to `okay`, `waiting`, or `error`.
The workshop’s overall [status](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-workshop-status)
is derived from the union of these results:

- The hook reports `okay` and exits with code zero:
  the SDK is *Ready*.
- The hook reports `waiting`:
  **Workshop** sleeps for one second and runs `check-health` again.
  After ten consecutive `waiting` results,
  the SDK is moved to *Error*.
- The hook reports `error`,
  exits with a non-zero code,
  exits without reporting a status,
  or fails to return within five seconds:
  the SDK is moved to *Error*.

## See also

Explanation:

- [Runtime behavior](https://ubuntu.com/workshop/docs//explanation/architecture/runtime-behavior.md#exp-arch-runtime-behavior)
- [Design best practices](https://ubuntu.com/workshop/docs//explanation/sdks/best-practices.md#exp-sdk-best-practices)
- [SDK concepts](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-sdk-concepts)
- [SDKs](https://ubuntu.com/workshop/docs//explanation/index.md#exp-sdks)
- [workshopctl (CLI)](https://ubuntu.com/workshop/docs//explanation/sdks/workshopctl-cli.md#exp-workshopctl-cli)

Reference:

- [SDK hooks](https://ubuntu.com/workshop/docs//reference/sdks.md#ref-sdk-hooks)
- [SDK state](https://ubuntu.com/workshop/docs//reference/sdks.md#ref-sdk-state)
- [workshopctl (CLI)](https://ubuntu.com/workshop/docs//reference/cli/workshopctl.md#ref-workshopctl-cli)

Tutorial:

- [Craft SDKs with SDKcraft](https://ubuntu.com/workshop/docs//tutorial/part-4-craft-sdks.md#tut-craft-sdks)
