<a id="ref-workshop-definition"></a>

# Workshop definition

<!-- @artefact project -->
<!-- @artefact workshop definition -->

A *workshop definition* is the YAML file
that **Workshop** reads to launch and refresh a workshop.
It names the base image, lists the SDKs to install,
declares any extra plugs, slots, or connections,
and records reusable shell actions.
The file is authored by the workshop’s user.

## Filename and location

<!-- @artefact project workshops -->
<!-- @artefact workshop name -->

A project may store a single workshop definition at its root,
or several under `.workshop/`.

- A single workshop: `workshop.yaml` or `.workshop.yaml`
  in the project directory.
- Multiple workshops: `.workshop/<NAME>.yaml`, one file per workshop.
  The `<NAME>` part of the filename
  must equal the workshop’s `name` field.
- A workshop name must start with a lowercase letter
  and may contain lowercase letters, digits, and hyphens between them.
  Up to 40 characters.

## Top-level fields

| Key               | Value   | Description                                                                                                                                                                                                                                    |
|-------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` (required) | string  | Workshop identifier. Subject to the naming rules above.<br/>Must match the filename when the definition is under `.workshop/`.                                                                                                                 |
| `base` (required) | string  | Base operating system image.<br/>One of `ubuntu@20.04`, `ubuntu@22.04`, `ubuntu@24.04`,<br/>or `ubuntu@26.04`.<br/><br/>SDKs that declare a `base` must use the same value;<br/>SDKs without a `base` are accepted on any workshop.            |
| `sdks`            | array   | Ordered list of SDK entries.<br/>Each entry references an existing SDK and configures it for the workshop.<br/>The system SDK is installed first implicitly and is not required here.<br/>See [SDK entry](#ref-workshop-definition-sdk-entry). |
| `connections`     | array   | Explicit connections between plugs and slots,<br/>applied on top of **Workshop**’s auto-connect logic.<br/>See [Connection entry](#ref-workshop-definition-connection-entry).                                                                  |
| `actions`         | object  | Named shell scripts available via **workshop run**.<br/>See [Action entry](#ref-workshop-definition-action-entry).                                                                                                                             |

## Nested structures

<a id="ref-workshop-definition-sdk-entry"></a>

### SDK entry

<!-- @artefact plug binding -->
<!-- @artefact $SDK -->

Each item in `sdks` is an object with these fields:

| Key               | Value   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
|-------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` (required) | string  | SDK identifier. The underlying name must contain<br/>at least one lowercase letter and may consist of<br/>lowercase letters, digits, and hyphens between them.<br/>`agent` is reserved.<br/><br/>Use a prefix to select the source:<br/><br/>- no prefix: an SDK from the SDK Store (default).<br/>- `try-<NAME>`:<br/>  a locally tried SDK in the [try area](https://ubuntu.com/workshop/docs//reference/cli/sdkcraft.md#ref-sdkcraft-try).<br/>- `project-<NAME>`:<br/>  an in-project SDK defined under `.workshop/<NAME>/`.<br/>- `system`:<br/>  the built-in system SDK; listing it explicitly is rarely needed.<br/><br/>The fully prefixed name is at most 40 characters without a prefix,<br/>44 with `try-`, and 48 with `project-`. |
| `channel`         | string  | Store channel from which to retrieve the SDK<br/>at [launch](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-launch)<br/>and [refresh](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-refresh).<br/>Uses the [snap channel format](https://snapcraft.io/docs/channels/):<br/>`<TRACK>/<RISK>/<BRANCH>`,<br/>with all three parts optional except that at least one must be present.<br/><br/>Default: `latest/stable`.<br/>Has no effect for `try-`, `project-`, and `system` SDKs,<br/>but must still be well formed.<br/><br/>#### NOTE<br/>Quote channel values in YAML when they look numeric<br/>(for example, `channel: "1.26"`)<br/>to avoid type coercion.                           |
| `plugs`           | object  | Plug bindings or additional plug definitions grafted onto the SDK<br/>by this workshop.<br/>See [Plug or slot entry (under an SDK)](#ref-workshop-definition-plug-slot)<br/>and [Interfaces](#ref-workshop-definition-interfaces).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `slots`           | object  | Additional slot definitions grafted onto the SDK by this workshop.<br/>Each entry specifies the `interface`<br/>and any interface-specific attributes.<br/>See [Interfaces](#ref-workshop-definition-interfaces).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |

<a id="ref-workshop-definition-plug-slot"></a>

### Plug or slot entry (under an SDK)

Each plug under an SDK is either an inline plug definition
or a binding to another plug.
Slots under an SDK are always inline slot definitions;
slots cannot be bound.

| Key                     | Value   | Description                                                                                                                                                                                                                                                                                                                                                       |
|-------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `interface`             | string  | Required for an inline plug definition; identifies the interface<br/>(for example, `mount`, `tunnel`).<br/>See [Interfaces](#ref-workshop-definition-interfaces)<br/>for the attributes each interface accepts.                                                                                                                                                   |
| `bind`                  | string  | Reference to a target plug, in the form `<SDK>:<PLUG>`.<br/>The `<SDK>` part must name a non-system SDK,<br/>since bound plugs cannot target the system SDK.<br/><br/>A bound plug must not carry any other attributes,<br/>cannot belong to the system SDK, cannot chain<br/>(bind to a plug that is itself bound),<br/>and cannot also appear in `connections`. |
| any interface attribute | varies  | Inline plug definitions accept the attributes<br/>documented under [Interfaces](#ref-workshop-definition-interfaces).                                                                                                                                                                                                                                             |

<a id="ref-workshop-definition-connection-entry"></a>

### Connection entry

Each item in `connections` links a plug to a slot of the same interface:

| Key               | Value   | Description                                                                                                                                                                                                                     |
|-------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `plug` (required) | string  | Plug reference, in the form `<SDK>:<PLUG>`.<br/>The `<SDK>` part may be empty (for example, `:ssh-agent`)<br/>to refer to the system SDK.<br/>The referenced SDK must appear in `sdks` or be implicit<br/>(`system`, `sketch`). |
| `slot` (required) | string  | Slot reference, in the form `<SDK>:<SLOT>`.<br/>Same rules as `plug`.                                                                                                                                                           |

A plug that has a `bind` set under its SDK entry
cannot also be listed in `connections`.

<a id="ref-workshop-definition-action-entry"></a>

### Action entry

<!-- @artefact workshop actions -->

Each entry in `actions` maps an action name to a shell script body:

| Key         | Value   | Description                                                                                                                                                                                                                        |
|-------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| action name | string  | Must start with a lowercase letter and may contain lowercase letters,<br/>digits, and hyphens between them.                                                                                                                        |
| action body | string  | A **bash** script.<br/>**Workshop** sets `errexit` and `pipefail` before running it.<br/>Arguments passed after **workshop run <WORKSHOP>** are available<br/>as the standard positional parameters `"$@"`, `"$1"`,<br/>and so on. |

Actions are interpreted lazily:
edits to `actions` are available immediately,
without **workshop refresh**.

<a id="ref-workshop-definition-interfaces"></a>

## Interfaces

The attributes accepted by inline plug and slot definitions
depend on the interface.
These same attributes appear in SDK definitions
([SDK definition](https://ubuntu.com/workshop/docs//reference/definition-files/sdk-definition.md#ref-sdk-definition) and [SDKcraft project definition](https://ubuntu.com/workshop/docs//reference/definition-files/sdkcraft-definition.md#ref-sdkcraft-definition));
a workshop may graft additional plugs and slots that follow them.

<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### Camera interface

<!-- @artefact camera interface -->

The camera interface exposes a host camera device.

- Plug attributes: none.
- Plug name: must be `camera`.
- Plug owner: any regular SDK; not the system SDK.
- Slot: the system SDK provides a single `system:camera` slot. Other SDKs cannot declare camera slots.

<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### Custom device interface

<!-- @artefact custom-device interface -->

The custom device interface exposes host devices
that belong to a Linux kernel subsystem.

A custom device plug is described by this attribute:

<!-- @artefact custom-device interface attributes -->

| Key                    | Value   | Description                                                                                         |
|------------------------|---------|-----------------------------------------------------------------------------------------------------|
| `subsystem` (required) | string  | The Linux kernel subsystem of the host devices to expose,<br/>for example `input`, `tty`, or `usb`. |

Plug owner: any regular SDK; not the system SDK.

Slot: the system SDK provides a single `system:custom-device` slot.
Other SDKs cannot declare custom device slots.

<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### Desktop interface

<!-- @artefact desktop interface -->

The desktop interface exposes the host display server.

- Plug attributes: none.
- Plug name: must be `desktop`.
- Plug owner: any regular SDK; not the system SDK.
- Slot: the system SDK provides a single `system:desktop` slot. Other SDKs cannot declare desktop slots.

<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### GPU interface

<!-- @artefact GPU interface -->

The GPU interface exposes host GPU devices.

- Plug attributes: none.
- Plug name: must be `gpu`.
- Plug owner: any regular SDK; not the system SDK.
- Slot: the system SDK provides a single `system:gpu` slot.
  Other SDKs cannot declare GPU slots.

<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### Mount interface

<!-- @artefact mount interface -->
<!-- @artefact $SDK -->

The mount interface exposes a directory between a slot owner and a plug owner.

A mount plug is described by these attributes:

| Key                          | Value   | Description                                                                                                                                                                                                                             |
|------------------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `workshop-target` (required) | string  | Path inside the workshop used as the plug’s target directory.<br/>Must be an absolute path;<br/>`$SDK` expands to the SDK’s installation path in the workshop.                                                                          |
| `mode`                       | integer | File permissions, in octal, applied when creating `workshop-target`<br/>and any missing parent directories.<br/>Defaults to `0o775` for regular users.<br/>When `uid` is zero, defaults to `0o755`.                                     |
| `uid`                        | integer | User ID applied when creating `workshop-target`<br/>and any missing parent directories.<br/>Defaults to `1000` when `workshop-target` is under<br/>`/home/workshop/`, `/project/`, or `/run/user/1000/`.<br/>Defaults to `0` otherwise. |
| `gid`                        | integer | Group ID applied when creating `workshop-target`<br/>and any missing parent directories.<br/>Defaults to `1000` or `0`<br/>by the same path rule as `uid`,<br/>even when `uid` is set explicitly.                                       |
| `read-only`                  | Boolean | Whether the target directory should be read-only.<br/>Defaults to `false`.                                                                                                                                                              |

Plug owner: any regular SDK; not the system SDK.

The system SDK provides one mount slot, `system:mount`,
with a dynamic `host-source` attribute
that can be configured only at [remount](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-remount).
It is the only mount slot whose source is on the host filesystem.

A mount slot on a regular SDK is described by this attribute:

| Key                          | Value   | Description                                                                                                                                                    |
|------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `workshop-source` (required) | string  | Path inside the workshop used as the slot’s source directory.<br/>Must be an absolute path;<br/>`$SDK` expands to the SDK’s installation path in the workshop. |
<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### SSH interface

<!-- @artefact SSH interface -->

The SSH interface exposes the user’s SSH agent socket.

- Plug attributes: none.
- Plug name: must be `ssh-agent`.
- Plug owner: any regular SDK; not the system SDK.
- Slot: the system SDK provides a single `system:ssh-agent` slot. Other SDKs cannot declare SSH slots.

<!-- Single-sourced snippet. Included by workshop-definition.rst,
sdk-definition.rst, and sdkcraft-definition.rst.
Do not add a top-level label; the including page provides the anchor. -->

### Tunnel interface

<!-- @artefact tunnel interface -->

The tunnel interface forwards a network address or Unix domain socket.

Both tunnel plugs and tunnel slots take a single attribute:

| Key        | Value   | Description                                                                                                                       |
|------------|---------|-----------------------------------------------------------------------------------------------------------------------------------|
| `endpoint` | string  | Network address or Unix domain socket that forms one end of the tunnel.<br/>Defaults to `localhost/tcp` for both plugs and slots. |

The `endpoint` value follows this grammar:

| Field    | Format                                                                                                                                                                                                                                                                                                        |
|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Endpoint | `<ADDRESS>/<PROTOCOL>` for network endpoints;<br/>may be shortened to `<ADDRESS>` or `<PROTOCOL>` alone.<br/><br/>`<PATH>` or `@<STRING>` for Unix domain sockets.                                                                                                                                            |
| Address  | `<HOST>:<PORT>`; may be shortened to `<HOST>` or `<PORT>`.                                                                                                                                                                                                                                                    |
| Protocol | Either `tcp` or `udp`. Defaults to `tcp`.                                                                                                                                                                                                                                                                     |
| Host     | An IPv4 or IPv6 address.<br/>When a port is supplied, IPv6 addresses must be enclosed in square brackets.<br/><br/>Supported aliases: `localhost`, `ip6-localhost`, and `ip6-loopback`.<br/>Defaults to `localhost`.                                                                                          |
| Port     | A TCP or UDP port number (1-65535).<br/>May be omitted, but only on one side of a connection; both sides then use the same port.<br/><br/>For security, tunnel plugs in the system SDK cannot use privileged ports (1-1023).                                                                                  |
| Path     | Absolute path to a Unix domain socket.<br/><br/>`$HOME` expands to the user’s home directory<br/>and `$XDG_RUNTIME_DIR` expands to the user runtime directory<br/>(typically `/run/user/1000`).<br/><br/>For security, tunnel plugs in the system SDK cannot listen on sockets outside these two directories. |
| String   | An abstract socket name.                                                                                                                                                                                                                                                                                      |

Endpoints that start with `[` or `@` must be quoted in YAML:

```yaml
endpoint: '[::1]:8080/tcp'
endpoint: '@abstract.sock'
```

## JSON Schema

<!-- @artefact workshop schema -->

The following JSON Schema describes the structure above:

### Workshop definition schema

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://canonical.com/workshop.yaml",
  "title": "Workshop",
  "description": "Workshop definition.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Workshop name. Must start with a lowercase letter and may contain lowercase letters, digits, and hyphens between them. Up to 40 characters. Must match the definition file basename if the workshop is stored under .workshop/.",
      "pattern": "^[a-z](?:-?[a-z0-9])*$",
      "maxLength": 40,
      "errorMessage": "A workshop's name must start with a lowercase letter and can only include digits, lowercase letters, and hyphens joining them."
    },
    "base": {
      "type": "string",
      "description": "Base operating system image for the workshop. Must be one of the supported Ubuntu releases. SDKs with a declared base must match the workshop base; SDKs without a base are accepted on any workshop.",
      "enum": [
        "ubuntu@20.04",
        "ubuntu@22.04",
        "ubuntu@24.04",
        "ubuntu@26.04"
      ],
      "errorMessage": "The base must be one of the supported values: ubuntu@20.04, ubuntu@22.04, ubuntu@24.04, ubuntu@26.04."
    },
    "sdks": {
      "type": "array",
      "description": "Ordered list of SDKs to install on top of the base. Each entry references an existing SDK; names must be unique within the list. The system SDK is installed first implicitly and need not be listed.",
      "uniqueItems": true,
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "SDK name. Prefix with try- for a locally tried SDK, project- for an in-project SDK, or use system for the built-in system SDK; an unprefixed name resolves to an SDK from the Store. The underlying name must contain at least one lowercase letter, may consist of lowercase letters, digits, and hyphens between them, and cannot be agent. Without a prefix the name is at most 40 characters; with try- at most 44; with project- at most 48.",
            "if": {
              "pattern": "^try-"
            },
            "then": {
              "maxLength": 44,
              "pattern": "^(?!(?:try-|project-)?agent$)try-(?!try-|project-)(?:[a-z0-9]-?)*[a-z](?:-?[a-z0-9])*$"
            },
            "else": {
              "if": {
                "pattern": "^project-"
              },
              "then": {
                "maxLength": 48,
                "pattern": "^(?!(?:try-|project-)?agent$)project-(?!try-|project-)(?:[a-z0-9]-?)*[a-z](?:-?[a-z0-9])*$"
              },
              "else": {
                "maxLength": 40,
                "pattern": "^(?!(?:try-|project-)?agent$)(?!try-|project-)(?:[a-z0-9]-?)*[a-z](?:-?[a-z0-9])*$"
              }
            },
            "errorMessage": "An SDK's name must contain a letter, may have a single 'try-' or 'project-' prefix, must not chain prefixes, and the underlying name cannot be 'agent'."
          },
          "channel": {
            "type": "string",
            "description": "Store channel used to retrieve the SDK at launch and refresh. Snap-like format: [<TRACK>/]<RISK>[/<BRANCH>], or <TRACK>, or <RISK>/<BRANCH>. Risk is one of stable, candidate, beta, edge. Track is at most 28 characters; branch is at most 128 characters; neither may equal a risk name. Default is latest/stable. Only applies to SDKs from the Store; for try-, project-, and system SDKs the value has no effect but must still be well formed.",
            "pattern": "^(?:(?:[a-zA-Z0-9](?:[_.-]?[a-zA-Z0-9])*/)?(?:stable|candidate|beta|edge)(?:/[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9])?|[a-zA-Z0-9](?:[_.-]?[a-zA-Z0-9])*)$",
            "errorMessage": "Channel must look like [<track>/]<risk>[/<branch>] or [<track>]."
          },
          "plugs": {
            "type": "object",
            "description": "Plug bindings and additional plug definitions for the SDK. Each key must start with a lowercase letter and contain only lowercase letters, digits, and hyphens between them.",
            "additionalProperties": false,
            "patternProperties": {
              "^[a-z](?:-?[a-z0-9])*$": {
                "description": "Either an inline plug definition (a mapping whose keys are interface-specific attributes such as interface, workshop-target, endpoint), a string naming the interface, null to use the key as the interface name, or a binding to another plug declared in the same workshop. To bind, provide a single bind key whose value is the target plug reference; bound plugs cannot define other attributes and cannot also appear in connections.",
                "properties": {
                  "bind": {
                    "type": "string",
                    "description": "Plug reference in the form <sdk>:<plug>. The target plug must use the same interface as this plug, must not itself be bound, and must not belong to the system SDK (so the <sdk> portion must name a non-system SDK).",
                    "pattern": "^(?!system:)(([a-z0-9]-?)*[a-z](-?[a-z0-9])*):[a-z](-?[a-z0-9])*$",
                    "errorMessage": "Bind reference must follow the pattern <sdk>:<plug>, where <sdk> names a non-system SDK."
                  }
                },
                "if": {
                  "type": "object",
                  "required": [
                    "bind"
                  ]
                },
                "then": {
                  "type": "object",
                  "properties": {
                    "bind": true
                  },
                  "additionalProperties": false,
                  "errorMessage": "When 'bind' is set, no other attributes are allowed on the plug."
                }
              }
            }
          },
          "slots": {
            "type": "object",
            "description": "Additional slot definitions grafted onto the SDK by the workshop. Each key must start with a lowercase letter and contain only lowercase letters, digits, and hyphens between them. Each value is either an inline slot definition (a mapping with interface and any interface-specific attributes), a string naming the interface, or null to use the key as the interface name.",
            "patternProperties": {
              "^[a-z](?:-?[a-z0-9])*$": {
                "type": ["object", "string", "null"],
                "description": "Slot definition: an inline mapping with interface and any interface-specific attributes, a string naming the interface, or null to take the interface from the key."
              }
            },
            "additionalProperties": false
          }
        },
        "required": [
          "name"
        ],
        "errorMessage": {
          "required": {
            "name": "Each SDK must specify a name."
          }
        },
        "additionalProperties": false
      }
    },
    "connections": {
      "type": "array",
      "description": "Explicit connections from plugs to slots, applied on top of auto-connection. Both endpoints must reference an SDK that is present in the workshop or is implicit (system, sketch), and must share the same interface. A plug that is bound elsewhere cannot also appear here.",
      "items": {
        "type": "object",
        "properties": {
          "plug": {
            "type": "string",
            "description": "Plug reference in the form <sdk>:<plug>. The <sdk> portion may be empty (for example, :ssh-agent) to refer to the system SDK.",
            "pattern": "^(([a-z0-9]-?)*[a-z](-?[a-z0-9])*)?:[a-z](-?[a-z0-9])*$",
            "errorMessage": "Plug reference must follow the pattern <sdk>:<plug> (the <sdk> portion may be empty for the system SDK)."
          },
          "slot": {
            "type": "string",
            "description": "Slot reference in the form <sdk>:<slot>. The <sdk> portion may be empty (for example, :ssh-agent) to refer to the system SDK.",
            "pattern": "^(([a-z0-9]-?)*[a-z](-?[a-z0-9])*)?:[a-z](-?[a-z0-9])*$",
            "errorMessage": "Slot reference must follow the pattern <sdk>:<slot> (the <sdk> portion may be empty for the system SDK)."
          }
        },
        "required": [
          "plug",
          "slot"
        ],
        "errorMessage": {
          "required": {
            "plug": "Each connection must specify a plug.",
            "slot": "Each connection must specify a slot."
          }
        },
        "additionalProperties": false
      }
    },
    "actions": {
      "type": "object",
      "description": "Named shell scripts available via workshop run. Action names must start with a lowercase letter and contain only lowercase letters, digits, and hyphens between them. Each script body runs under bash as a login shell with errexit and pipefail set; positional parameters $@, $1, $2, ... receive the arguments passed after the workshop name.",
      "patternProperties": {
        "^[a-z](?:-?[a-z0-9])*$": {
          "type": "string",
          "description": "Shell script body executed by bash inside the workshop."
        }
      },
      "additionalProperties": false,
      "errorMessage": "Action names must start with a lowercase letter and contain only lowercase letters, digits, and hyphens between them."
    }
  },
  "required": [
    "name",
    "base"
  ],
  "additionalProperties": false,
  "errorMessage": {
    "required": {
      "name": "The 'name' field is required.",
      "base": "The 'base' field is required."
    }
  }
}
```

## Examples

Minimal workshop with one Store SDK and two actions:

```yaml
name: golang
base: ubuntu@22.04
sdks:
  - name: go
    channel: "1.26"
actions:
  lint: |
    go vet
    golangci-lint run
  tests: go test "$@"
```

Workshop with an in-project SDK and a plug binding between SDKs:

```yaml
name: go-dev
base: ubuntu@22.04
sdks:
  - name: go
    channel: edge
  - name: project-cache
    plugs:
      data:
        bind: go:mod-cache
```

Workshop that grafts a plug and a slot onto its SDKs
and adds explicit connections;
besides using the fictional
`tensorflow`, `imagenet` and `cuda` SDKs,
it defines an additional slot under the `imagenet` SDK,
a plug under `tensorflow`,
and two connections:

- One that connects the `tensorflow:images` plug
  to the newly defined `imagenet:images` slot.
- Another that connects the `tensorflow:cuda` plug
  to the preexisting `cuda:libs`.

```yaml
name: digits-cuda
base: ubuntu@22.04
sdks:
  - name: tensorflow
    plugs:
      cuda:
        interface: mount
        workshop-target: /usr/local/cuda/lib64
  - name: imagenet
    slots:
      images:
        interface: mount
        workshop-source: $SDK/images
  - name: cuda
connections:
  - plug: tensorflow:cuda
    slot: cuda:libs
  - plug: tensorflow:images
    slot: imagenet:images
```

Workshop that pulls an SDK from the try area:

```yaml
name: try-go
base: ubuntu@24.04
sdks:
  - name: try-go
```

## See also

Explanation:

- [Base image](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-base)
- [In-project SDKs](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-in-project-sdk)
- [SDKs](https://ubuntu.com/workshop/docs//explanation/index.md#exp-sdks)
- [System SDK](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-system-sdk)
- [Testing and trying SDKs](https://ubuntu.com/workshop/docs//explanation/sdks/concepts.md#exp-test-try-sdk)
- [Workshop definition](https://ubuntu.com/workshop/docs//explanation/workshops/concepts.md#exp-workshop-definition)

Reference:

- [SDK definition](https://ubuntu.com/workshop/docs//reference/definition-files/sdk-definition.md#ref-sdk-definition)
- [SDKcraft project definition](https://ubuntu.com/workshop/docs//reference/definition-files/sdkcraft-definition.md#ref-sdkcraft-definition)
- [workshop info](https://ubuntu.com/workshop/docs//reference/cli/workshop.md#ref-workshop-info)
