How to build an SDK¶
A standalone SDK is a SDKcraft project
with its own sdkcraft.yaml,
its own hooks/ tree,
and the parts and interfaces
that describe what it ships
and how it integrates with workshops.
Build one by laying out the project,
declaring parts and interfaces,
authoring the lifecycle hooks,
and exercising the result locally
before any thought of publishing.
Prerequisites¶
Before starting, ensure the following are in place:
SDKcraft is installed.
LXD 6.6 or later is running on the host.
Workshop is installed and configured so that you can launch a workshop to try the SDK against.
Start from the template¶
New SDKs start from canonical/template-sdk, a GitHub-template repository that ships the project skeleton:
a
sdkcraft.yamlready to be filled in,a
hooks/tree with stubs for the lifecycle hooks,a
VERSIONfile pinning the upstream release the SDK wraps,a
renovate.jsonthat tracks upstream releases on a long-lived version branch and opens PRs to bumpVERSIONas they ship,CI workflows under
.github/workflows/that build on pull requests and upload to the SDK Store on push to the version branch,a README template aligned to the rest of the project shape.
Use it via GitHub’s “Use this template” button, or git clone if you don’t host on GitHub. The choice that follows is how to fill the template in.
With the sdk-designer skill¶
template-sdk
also ships an agentic skill named sdk-designer.
The skill runs an interactive scaffolding conversation:
it asks about the software to package,
the target platforms,
and which interfaces and hooks are needed,
then writes the corresponding files into the template.
Aim the agent at the new repository.
Run
/sdk-designerand answer the prompts.Review the generated files and adjust where the skill’s defaults don’t match your case.
By editing the template directly¶
Without the agentic skill,
edit the template files in place.
Fill sdkcraft.yaml per Fill in the metadata,
replace each hook stub under hooks/ per Author the hooks,
update VERSION to the upstream release you intend to ship first,
and adjust renovate.json if the upstream project lives somewhere
other than the GitHub release page the default config targets.
Fill in the metadata¶
SDKcraft needs four pieces of metadata to identify and build the SDK:
name, version, a one-line summary,
and the platforms to build for.
Add license to declare the SDK’s licensing terms,
and description for a multi-line write-up:
name: <NAME>
version: "<VERSION>"
summary: One-line description of the SDK
description: |
A longer description that explains
what the SDK packages
and any noteworthy behavior.
license: MIT
platforms:
ubuntu@22.04:amd64:
ubuntu@24.04:amd64:
Use the SDK’s upstream version for version
when the SDK wraps a single tool;
keep it quoted so that values like 1.0 aren’t parsed as floats.
SDKcraft builds one artifact per entry in platforms.
Each entry pairs an Ubuntu base
with a CPU architecture from the Debian naming scheme
(amd64, arm64, and so on).
For SDKs that don’t ship compiled binaries,
use all instead of a specific architecture.
Define parts¶
Parts describe how SDKcraft obtains the SDK’s payload at build time. A small SDK often gets by with a single part; larger SDKs split work along functional boundaries.
For a binary downloaded from a release page,
use the dump plugin with a tarball source:
parts:
<NAME>:
plugin: dump
source: https://example.com/releases/v${CRAFT_PROJECT_VERSION}/<NAME>-linux-${CRAFT_ARCH_BUILD_FOR}.tar.gz
source-type: tar
$CRAFT_PROJECT_VERSION and $CRAFT_ARCH_BUILD_FOR
expand at build time
from the version field
and the platform SDKcraft is currently building for.
If the SDK ships supporting files,
add them as separate parts with the file source type:
parts:
<NAME>:
plugin: dump
source: https://example.com/releases/v${CRAFT_PROJECT_VERSION}/<NAME>-linux-${CRAFT_ARCH_BUILD_FOR}.tar.gz
source-type: tar
service-unit:
plugin: dump
source: <NAME>.service
source-type: file
For source-built SDKs,
the rust, go, and python plugins
take over from dump.
The Craft Parts
plugin reference
lists every plugin and its options.
Declare plugs and slots¶
Plugs and slots wire the SDK to host resources and to other SDKs in the workshop. A plug requests access to something the workshop provides; a slot offers something the SDK exposes.
The most common patterns are:
A
mountplug for cache or model directories that should survive workshop refresh.A
gpuplug for SDKs that need GPU acceleration.A
tunnelslot for services that expose a network endpoint.
For example, an SDK that runs a long-lived HTTP service
and caches data under ~/.cache/<NAME>/
declares both a plug and a slot:
plugs:
cache:
interface: mount
workshop-target: /home/workshop/.cache/<NAME>
slots:
api:
interface: tunnel
endpoint: 8080
The workshop-target value is the in-workshop path
that Workshop backs with persistent host storage;
SDKs can’t pick the host path directly,
which prevents them from reaching arbitrary host files.
Workshop users can override the host side at run time
with workshop remount.
Try the SDK¶
Once the definition and hooks are in place, build and install the SDK into a workshop with sdkcraft try:
$ sdkcraft try
SDKcraft packs the SDK for each declared platform
into files of the form <NAME>_<ARCH>_<BASE>.sdk
and copies them into the try area.
Add the SDK to a workshop definition
using the try- prefix:
name: dev
base: ubuntu@24.04
sdks:
- name: try-<NAME>
The base must match one of the SDK’s platforms.
Then launch the workshop with verbose output
and a wait-on-error breakpoint
so that any hook failure leaves a usable container behind for inspection:
$ workshop launch --verbose --wait-on-error
Pay particular attention to:
Hook output in workshop changes and workshop tasks.
The SDK’s
statusin workshop info; awaitingorerrorstate is the SDK telling you something is wrong.The interaction between this SDK and any other SDKs it’s meant to be installed alongside.
On success,
workshop info reports the SDK
and a status of okay.
On failure,
workshop changes and workshop tasks
point at the hook that failed;
see How to debug issues in workshops for the full troubleshooting flow.
Test the SDK¶
If the SDK ships a tests/ directory with
spread tests,
run them against the freshly packed artifacts:
$ sdkcraft test
SDKcraft provisions a clean LXD container for each test, installs the packed SDK into a workshop, and runs the declared scenarios end-to-end.
Tests live under tests/,
organised in suites declared by tests/spread.yaml.
The starter test at tests/main/launch/ illustrates the layout;
add more tests next to the starter,
each in its own subdirectory of the same suite:
summary: SDK installs and reports healthy
execute: |
workshop launch --verbose --wait-on-error
workshop info | grep -E 'status:\s+okay'
Iterate¶
Normally, you would use the workshop sketch-sdk command to iterate on an SDK locally. However, even when it doesn’t fit your purpose, the build-try-fix loop is fast:
Edit the definition or a hook.
Run sdkcraft clean && sdkcraft try to rebuild from a clean state.
Run workshop refresh to reapply the SDK in the existing workshop, or workshop launch --verbose --wait-on-error for a fresh start.
sdkcraft clean is optional; omit it when the change is small enough that SDKcraft can incrementally rebuild. For build internals, see the Craft Parts lifecycle documentation.
Next steps¶
When the SDK behaves correctly under sdkcraft try and its test suite passes, proceed to How to publish an SDK to register the SDK name on the SDK Store and upload a revision.
See also¶
Explanation:
Reference:
Tutorial: