diff --git a/imagecraft/models/volume.py b/imagecraft/models/volume.py
index 54ab97bf..a432e5e6 100644
--- a/imagecraft/models/volume.py
+++ b/imagecraft/models/volume.py
@@ -161,6 +161,9 @@ class Role(str, enum.Enum):
SYSTEM_BOOT = "system-boot"
"""The partition stores the image's boot assets."""
+ SYSTEM_SEED = "system-seed"
+ """The partition stores the image's initial seed data used during first boot."""
+
class StructureItem(CraftBaseModel):
"""Structure item of the image."""
diff --git a/imagecraft/plugins/_setup.py b/imagecraft/plugins/_setup.py
index ff0c2aab..cdeb0e13 100644
--- a/imagecraft/plugins/_setup.py
+++ b/imagecraft/plugins/_setup.py
@@ -18,6 +18,8 @@
from craft_parts.plugins.plugins import PluginType
from .mmdebstrap_plugin import MmdebstrapPlugin
+from .snap_preseed_plugin import SnapPreseedPlugin
+from .uc_prepare_plugin import UcPreparePlugin
def get_app_plugins() -> dict[str, PluginType]:
@@ -25,7 +27,11 @@ def get_app_plugins() -> dict[str, PluginType]:
:returns: A dict mapping plugin names to plugins
"""
- return {"mmdebstrap": MmdebstrapPlugin}
+ return {
+ "mmdebstrap": MmdebstrapPlugin,
+ "snap-preseed": SnapPreseedPlugin,
+ "uc-prepare": UcPreparePlugin,
+ }
def setup_plugins() -> None:
diff --git a/imagecraft/plugins/snap_preseed_plugin.py b/imagecraft/plugins/snap_preseed_plugin.py
new file mode 100644
index 00000000..2de23eb4
--- /dev/null
+++ b/imagecraft/plugins/snap_preseed_plugin.py
@@ -0,0 +1,93 @@
+# This file is part of imagecraft.
+#
+# Copyright 2026 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 3, as published
+# by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see .
+
+"""The snap-preseed plugin."""
+
+import shlex
+from typing import Literal, cast
+
+from craft_parts.plugins import Plugin, PluginProperties
+from typing_extensions import override
+
+
+class SnapPreseedPluginProperties(PluginProperties, frozen=True):
+ """Properties for the 'snap-preseed' plugin."""
+
+ plugin: Literal["snap-preseed"] = "snap-preseed"
+
+ snap_preseed_snaps: list[str]
+ snap_preseed_channel: str | None = None
+ snap_preseed_model_assert: str = ""
+ snap_preseed_validation: Literal["ignore", "enforce"] = "ignore"
+ snap_preseed_assertions: list[str] = []
+ snap_preseed_revisions: str | None = None
+
+
+class SnapPreseedPlugin(Plugin):
+ """Prepare snaps for Ubuntu Classic images using 'snap prepare-image'."""
+
+ properties_class = SnapPreseedPluginProperties
+
+ @override
+ def get_build_snaps(self) -> set[str]:
+ """Return a set of required snaps to install in the build environment."""
+ return set()
+
+ @override
+ def get_build_packages(self) -> set[str]:
+ """Return a set of required packages to install in the build environment."""
+ return set()
+
+ @override
+ def get_build_environment(self) -> dict[str, str]:
+ """Return a dictionary with the environment to use in the build step."""
+ return {}
+
+ @override
+ def get_build_commands(self) -> list[str]:
+ """Return a list of commands to run during the build step."""
+ options = cast(SnapPreseedPluginProperties, self._options)
+ cmd = [
+ "snap",
+ "prepare-image",
+ "--classic",
+ f"--arch={self._part_info.target_arch}",
+ f"--validation={options.snap_preseed_validation}",
+ ]
+ if options.snap_preseed_channel:
+ cmd.append(f"--channel={options.snap_preseed_channel}")
+
+ if options.snap_preseed_revisions:
+ cmd.append(f"--revisions={options.snap_preseed_revisions}")
+
+ cmd.extend(
+ f"--assert={assertion}" for assertion in options.snap_preseed_assertions
+ )
+
+ cmd.extend(
+ f"--snap={self._resolve_snap(snap)}" for snap in options.snap_preseed_snaps
+ )
+
+ cmd.append(options.snap_preseed_model_assert)
+ cmd.append(str(self._part_info.part_install_dir))
+ return [shlex.join(cmd)]
+
+ def _resolve_snap(self, snap: str) -> str:
+ snap = snap.strip()
+ if "/" in snap and not snap.endswith(".snap"):
+ name, channel = snap.split("/", 1)
+ snap = f"{name}={channel}"
+ return snap
diff --git a/imagecraft/plugins/uc_prepare_plugin.py b/imagecraft/plugins/uc_prepare_plugin.py
new file mode 100644
index 00000000..4cd15022
--- /dev/null
+++ b/imagecraft/plugins/uc_prepare_plugin.py
@@ -0,0 +1,135 @@
+# This file is part of imagecraft.
+#
+# Copyright 2026 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 3, as published
+# by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see .
+
+"""The uc-prepare plugin."""
+
+from typing import Literal, cast
+
+from craft_parts.plugins import Plugin, PluginProperties
+from pydantic import model_validator
+from typing_extensions import Self, override
+
+
+class UcPreparePluginProperties(PluginProperties, frozen=True):
+ """Properties for the uc-prepare plugin."""
+
+ plugin: Literal["uc-prepare"] = "uc-prepare"
+
+ uc_prepare_model_assert: str
+ uc_prepare_snaps: list[str] = []
+ uc_prepare_channel: str | None = None
+ uc_prepare_validation: Literal["ignore", "enforce"] = "ignore"
+ uc_prepare_assertions: list[str] = []
+ uc_prepare_revisions: str | None = None
+ uc_prepare_preseed: bool = False
+ uc_prepare_preseed_sign_key: str | None = None
+ uc_prepare_apparmor_features_dir: str | None = None
+ uc_prepare_sysfs_overlay: str | None = None
+
+ @model_validator(mode="after")
+ def sign_key_requires_preseed(self) -> Self:
+ """preseed-sign-key requires preseed to be enabled."""
+ if self.uc_prepare_preseed_sign_key and not self.uc_prepare_preseed:
+ raise ValueError(
+ "uc-prepare-preseed-sign-key cannot be used without uc-prepare-preseed"
+ )
+ return self
+
+ @model_validator(mode="after")
+ def sysfs_overlay_requires_preseed(self) -> Self:
+ """sysfs-overlay requires preseed to be enabled."""
+ if self.uc_prepare_sysfs_overlay and not self.uc_prepare_preseed:
+ raise ValueError(
+ "uc-prepare-sysfs-overlay cannot be used without uc-prepare-preseed"
+ )
+ return self
+
+
+class UcPreparePlugin(Plugin):
+ """Prepare snaps for Ubuntu Core using 'snap prepare-image'."""
+
+ properties_class = UcPreparePluginProperties
+
+ @override
+ def get_build_snaps(self) -> set[str]:
+ """Return a set of required snaps to install in the build environment."""
+ return set()
+
+ @override
+ def get_build_packages(self) -> set[str]:
+ """Return a set of required packages to install in the build environment."""
+ options = cast(UcPreparePluginProperties, self._options)
+ if (
+ options.uc_prepare_preseed
+ and self._part_info.host_arch != self._part_info.target_arch
+ ):
+ return {"qemu-user-static"}
+ return set()
+
+ @override
+ def get_build_environment(self) -> dict[str, str]:
+ """Return a dictionary with the environment to use in the build step."""
+ return {}
+
+ @override
+ def get_build_commands(self) -> list[str]:
+ """Return a list of commands to run during the build step."""
+ options = cast(UcPreparePluginProperties, self._options)
+
+ cmd = ["snap", "prepare-image"]
+
+ if options.uc_prepare_preseed:
+ cmd.append("--preseed")
+
+ if options.uc_prepare_preseed_sign_key:
+ cmd.append(f"--preseed-sign-key={options.uc_prepare_preseed_sign_key}")
+
+ if options.uc_prepare_apparmor_features_dir:
+ cmd.append(
+ f"--apparmor-features-dir={options.uc_prepare_apparmor_features_dir}"
+ )
+
+ if options.uc_prepare_sysfs_overlay:
+ cmd.append(f"--sysfs-overlay={options.uc_prepare_sysfs_overlay}")
+
+ cmd.append(f"--validation={options.uc_prepare_validation}")
+
+ if options.uc_prepare_channel:
+ cmd.append(f"--channel={options.uc_prepare_channel}")
+
+ if options.uc_prepare_revisions:
+ cmd.append(f"--revisions={options.uc_prepare_revisions}")
+
+ cmd.extend(
+ f"--assert={assertion}" for assertion in options.uc_prepare_assertions
+ )
+
+ cmd.extend(
+ f"--snap={self._resolve_snap(snap)}" for snap in options.uc_prepare_snaps
+ )
+
+ cmd.append(
+ f"{options.uc_prepare_model_assert} {self._part_info.part_install_dir}"
+ )
+
+ return [" ".join(cmd)]
+
+ def _resolve_snap(self, snap: str) -> str:
+ snap = snap.strip()
+ if "/" in snap and not snap.endswith(".snap"):
+ name, channel = snap.split("/", 1)
+ snap = f"{name}={channel}"
+ return snap
diff --git a/tests/spread/plugins/snap-preseed/imagecraft.yaml b/tests/spread/plugins/snap-preseed/imagecraft.yaml
new file mode 100644
index 00000000..ebd8198e
--- /dev/null
+++ b/tests/spread/plugins/snap-preseed/imagecraft.yaml
@@ -0,0 +1,33 @@
+name: snap-preseed-test
+version: "0.1"
+summary: Test snap-preseed plugin
+description: Test snap-preseed plugin
+base: bare
+build-base: ubuntu@24.04
+
+platforms:
+ amd64:
+
+volumes:
+ disk:
+ schema: gpt
+ structure:
+ - name: rootfs
+ role: system-data
+ type: 0FC63DAF-8483-4772-8E79-3D69D8477DE4
+ filesystem: ext4
+ filesystem-label: writable
+ size: 2G
+
+filesystems:
+ default:
+ - mount: /
+ device: (volume/disk/rootfs)
+
+parts:
+ snaps:
+ plugin: snap-preseed
+ snap-preseed-snaps:
+ - hello-world/latest/stable
+ organize:
+ "var/*": (overlay)/var/
diff --git a/tests/spread/plugins/snap-preseed/task.yaml b/tests/spread/plugins/snap-preseed/task.yaml
new file mode 100644
index 00000000..e80ef227
--- /dev/null
+++ b/tests/spread/plugins/snap-preseed/task.yaml
@@ -0,0 +1,15 @@
+summary: Test snap-preseed plugin
+
+systems: [ubuntu-24.04-64]
+
+execute: |
+ imagecraft pack --verbose --destructive-mode
+
+ test -f disk.img
+
+ test -d prime/var/lib/snapd/seed/
+ test -f prime/var/lib/snapd/seed/snaps/hello-world_*.snap
+
+restore: |
+ imagecraft clean --destructive-mode
+ rm -rf disk.img || true
diff --git a/tests/spread/plugins/uc-prepare/imagecraft.yaml b/tests/spread/plugins/uc-prepare/imagecraft.yaml
new file mode 100644
index 00000000..676723cb
--- /dev/null
+++ b/tests/spread/plugins/uc-prepare/imagecraft.yaml
@@ -0,0 +1,36 @@
+name: uc-prepare-test
+version: "1.0"
+summary: Test uc-prepare plugin
+description: Test uc-prepare plugin
+base: bare
+build-base: ubuntu@24.04
+
+platforms:
+ amd64:
+
+filesystems:
+ default:
+ - mount: /
+ device: (volume/disk/ubuntu-seed)
+
+parts:
+ uc-seed:
+ plugin: uc-prepare
+ source: .
+ uc-prepare-model-assert: model.assert
+ organize:
+ "system-seed/*": (volume/disk/ubuntu-seed)/
+ prime:
+ - -kernel
+ - -gadget
+ - -resolved-content
+
+volumes:
+ disk:
+ schema: gpt
+ structure:
+ - name: ubuntu-seed
+ role: system-seed
+ type: C12A7328-F81F-11D2-BA4B-00A0C93EC93B
+ filesystem: vfat
+ size: 1500M
diff --git a/tests/spread/plugins/uc-prepare/model.assert b/tests/spread/plugins/uc-prepare/model.assert
new file mode 100755
index 00000000..411b8f85
--- /dev/null
+++ b/tests/spread/plugins/uc-prepare/model.assert
@@ -0,0 +1,48 @@
+type: model
+authority-id: canonical
+series: 16
+brand-id: canonical
+model: ubuntu-core-24-amd64
+architecture: amd64
+base: core24
+grade: signed
+snaps:
+ -
+ default-channel: 24/stable
+ id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
+ name: pc
+ type: gadget
+ -
+ default-channel: 24/stable
+ id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
+ name: pc-kernel
+ type: kernel
+ -
+ default-channel: latest/stable
+ id: dwTAh7MZZ01zyriOZErqd1JynQLiOGvM
+ name: core24
+ type: base
+ -
+ default-channel: latest/stable
+ id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
+ name: snapd
+ type: snapd
+ -
+ default-channel: 24/stable
+ id: ASctKBEHzVt3f1pbZLoekCvcigRjtuqw
+ name: console-conf
+ presence: optional
+ type: app
+timestamp: 2024-04-19T08:42:32+00:00
+sign-key-sha3-384: 9tydnLa6MTJ-jaQTFUXEwHl1yRx7ZS4K5cyFDhYDcPzhS7uyEkDxdUjg9g08BtNn
+
+AcLBXAQAAQoABgUCZiLKhgAKCRDgT5vottzAEmmwD/9xNRSiN7yoPMsERG2aSOLr+2ZTJddBhdj3
+bEuS6ifCb1K/t7CpNhmJt1iMasdcUl7q1DJCUr/uunLgoVKODxE0Vx4k3UCtoEH2v2Mj6oqjPdAh
+d90gsEBP0nVhf+LjoS34zF3O7ScrJU69mjUPhcDx0mgOslSY5aHbFTIKsT9InJ3tKgg41+RFxvwZ
+V1Ksf4ITkuy9ap7CjqhczFyNMSMn0cmF9OKK39jvmtfpXRVmjTDCoF0mLXmJ+RDyKFbjMuQU1O3S
+opv6gJPE//hWj9Hn5S3DIVgjcpWWOqGk+wREFGgMs/g9aT5DoLMp/FvcMVPH8sZNLiZF3wrVJCjZ
+Tm+fD3SGBlht5GJGKWL5eAlGRVOUNh0cmX1WTYuZKw+OOnNuQyPykfkEFE2OKwSQwXP+ZICuGIcX
+KlO56GB8OLs8hdQQrOZH8ysO5qM3Ee0Iq/keJiQZIYIv63ODLn4cPkTa2Es33alVf59xiFvUFAfd
+2hWOAFDbxSJSETNmCReSMNXR01U8YTKpQMB9CA5tlX7RlrHCeLrPld8ytSCLUf29ofsJE3yavgTK
+2wt5eeX6M5okmq2TR78iH6xbX3qeK8J6Ld1ElhGIikivnXMPyA5BApdQa6aoHtHVx9jHRlgWNOZs
+X5uwovE8pMriAGI4l+AWYFG6SeVyY90qqlAmzaybsw==
diff --git a/tests/spread/plugins/uc-prepare/task.yaml b/tests/spread/plugins/uc-prepare/task.yaml
new file mode 100644
index 00000000..e2dc8be1
--- /dev/null
+++ b/tests/spread/plugins/uc-prepare/task.yaml
@@ -0,0 +1,17 @@
+summary: Test uc-prepare plugin
+
+systems: [ubuntu-24.04-64]
+
+execute: |
+ imagecraft pack --verbose --destructive-mode
+
+ test -f disk.img
+
+ test -f prime/snaps/pc_*.snap
+ test -f prime/snaps/pc-kernel_*.snap
+ test -f prime/snaps/core24_*.snap
+ test -f prime/snaps/snapd_*.snap
+
+restore: |
+ imagecraft clean --destructive-mode
+ rm -rf disk.img || true
diff --git a/tests/unit/models/test_volume.py b/tests/unit/models/test_volume.py
index cc2eee3f..b94ef49f 100644
--- a/tests/unit/models/test_volume.py
+++ b/tests/unit/models/test_volume.py
@@ -140,7 +140,7 @@ def test_volume_valid():
},
),
(
- "1 validation error for Volume\nstructure.0.role\n Input should be 'system-data' or 'system-boot'",
+ "1 validation error for Volume\nstructure.0.role\n Input should be 'system-data', 'system-boot' or 'system-seed'",
ValidationError,
{
"schema": "gpt",
diff --git a/tests/unit/plugins/test_snap_preseed_plugin.py b/tests/unit/plugins/test_snap_preseed_plugin.py
new file mode 100644
index 00000000..93c98375
--- /dev/null
+++ b/tests/unit/plugins/test_snap_preseed_plugin.py
@@ -0,0 +1,102 @@
+# This file is part of imagecraft.
+#
+# Copyright 2026 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+
+import pytest
+from craft_parts import PartInfo, ProjectInfo
+from craft_parts.parts import Part
+from imagecraft.plugins.snap_preseed_plugin import (
+ SnapPreseedPlugin,
+ SnapPreseedPluginProperties,
+)
+from pydantic import ValidationError
+
+
+@pytest.fixture
+def part_info(new_dir):
+ project_info = ProjectInfo(application_name="test_snap_preseed", cache_dir=new_dir)
+ return PartInfo(project_info=project_info, part=Part("my-part", {}))
+
+
+def test_missing_snaps_key():
+ with pytest.raises(ValidationError, match="snap-preseed-snaps"):
+ SnapPreseedPluginProperties.unmarshal({})
+
+
+@pytest.fixture
+def cmd_prefix(part_info):
+ return f"snap prepare-image --classic --arch={part_info.target_arch} --validation=ignore"
+
+
+def test_get_build_commands(part_info, cmd_prefix):
+ properties = SnapPreseedPluginProperties.unmarshal(
+ {"snap-preseed-snaps": ["core24", "hello-world/latest/stable"]}
+ )
+
+ plugin = SnapPreseedPlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"{cmd_prefix} --snap=core24 --snap=hello-world=latest/stable '' {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_model_assertion(part_info, cmd_prefix):
+ properties = SnapPreseedPluginProperties.unmarshal(
+ {
+ "snap-preseed-snaps": ["core24"],
+ "snap-preseed-model-assert": "model.assert",
+ }
+ )
+
+ plugin = SnapPreseedPlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"{cmd_prefix} --snap=core24 model.assert {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_assertions(part_info, cmd_prefix):
+ properties = SnapPreseedPluginProperties.unmarshal(
+ {
+ "snap-preseed-snaps": ["core24"],
+ "snap-preseed-assertions": ["system-user.assert", "account.assert"],
+ }
+ )
+
+ plugin = SnapPreseedPlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"{cmd_prefix} --assert=system-user.assert --assert=account.assert --snap=core24 '' {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_revisions(part_info, cmd_prefix):
+ properties = SnapPreseedPluginProperties.unmarshal(
+ {
+ "snap-preseed-snaps": ["core24"],
+ "snap-preseed-revisions": "./revisions.txt",
+ }
+ )
+
+ plugin = SnapPreseedPlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"{cmd_prefix} --revisions=./revisions.txt --snap=core24 '' {part_info.part_install_dir}"
+ )
diff --git a/tests/unit/plugins/test_uc_prepare_plugin.py b/tests/unit/plugins/test_uc_prepare_plugin.py
new file mode 100644
index 00000000..fda7baf1
--- /dev/null
+++ b/tests/unit/plugins/test_uc_prepare_plugin.py
@@ -0,0 +1,165 @@
+# This file is part of imagecraft.
+#
+# Copyright 2026 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+
+import pytest
+from craft_parts import PartInfo, ProjectInfo
+from craft_parts.parts import Part
+from imagecraft.plugins.uc_prepare_plugin import (
+ UcPreparePlugin,
+ UcPreparePluginProperties,
+)
+from pydantic import ValidationError
+
+
+@pytest.fixture
+def part_info(new_dir):
+ project_info = ProjectInfo(application_name="test_uc_prepare", cache_dir=new_dir)
+ return PartInfo(project_info=project_info, part=Part("my-part", {}))
+
+
+def test_missing_model_assertion():
+ with pytest.raises(ValidationError, match="uc-prepare-model-assert"):
+ UcPreparePluginProperties.unmarshal({})
+
+
+def test_preseed_sign_key_without_preseed():
+ with pytest.raises(ValueError, match="cannot be used without uc-prepare-preseed"):
+ UcPreparePluginProperties.unmarshal(
+ {
+ "uc-prepare-model-assert": "model.assert",
+ "uc-prepare-preseed-sign-key": "sign-key",
+ }
+ )
+
+
+def test_sysfs_overlay_without_preseed():
+ with pytest.raises(ValueError, match="cannot be used without uc-prepare-preseed"):
+ UcPreparePluginProperties.unmarshal(
+ {
+ "uc-prepare-model-assert": "model.assert",
+ "uc-prepare-sysfs-overlay": "./sysfs",
+ }
+ )
+
+
+def test_get_build_packages_without_preseed(part_info):
+ properties = UcPreparePluginProperties.unmarshal(
+ {"uc-prepare-model-assert": "model.assert"}
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert plugin.get_build_packages() == set()
+
+
+def test_get_build_packages_with_preseed_same_arch(part_info, mocker):
+ mocker.patch.object(part_info, "host_arch", "amd64")
+ mocker.patch.object(part_info, "target_arch", "amd64")
+ properties = UcPreparePluginProperties.unmarshal(
+ {"uc-prepare-model-assert": "model.assert", "uc-prepare-preseed": True}
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert plugin.get_build_packages() == set()
+
+
+def test_get_build_packages_with_preseed_different_arch(part_info, mocker):
+ mocker.patch.object(part_info, "host_arch", "amd64")
+ mocker.patch.object(part_info, "target_arch", "arm64")
+ properties = UcPreparePluginProperties.unmarshal(
+ {"uc-prepare-model-assert": "model.assert", "uc-prepare-preseed": True}
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert plugin.get_build_packages() == {"qemu-user-static"}
+
+
+def test_get_build_commands(part_info):
+ properties = UcPreparePluginProperties.unmarshal(
+ {"uc-prepare-model-assert": "model.assert"}
+ )
+
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"snap prepare-image --validation=ignore model.assert {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_preseed(part_info):
+ properties = UcPreparePluginProperties.unmarshal(
+ {
+ "uc-prepare-model-assert": "model.assert",
+ "uc-prepare-preseed": True,
+ }
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"snap prepare-image --preseed --validation=ignore model.assert {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_preseed_sign_key(part_info):
+ properties = UcPreparePluginProperties.unmarshal(
+ {
+ "uc-prepare-model-assert": "model.assert",
+ "uc-prepare-preseed": True,
+ "uc-prepare-preseed-sign-key": "sign-key",
+ }
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"snap prepare-image --preseed --preseed-sign-key=sign-key --validation=ignore model.assert {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_apparmor_dir(part_info):
+ apparmor_features_dir = "/sys/kernel/security/somewhere"
+ properties = UcPreparePluginProperties.unmarshal(
+ {
+ "uc-prepare-model-assert": "model.assert",
+ "uc-prepare-preseed": True,
+ "uc-prepare-apparmor-features-dir": apparmor_features_dir,
+ }
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"snap prepare-image --preseed --apparmor-features-dir={apparmor_features_dir} --validation=ignore model.assert {part_info.part_install_dir}"
+ )
+
+
+def test_get_build_commands_with_sysfs_overlay(part_info):
+ sysfs_overlay = "./sysfs-overlay"
+ properties = UcPreparePluginProperties.unmarshal(
+ {
+ "uc-prepare-model-assert": "model.assert",
+ "uc-prepare-preseed": True,
+ "uc-prepare-sysfs-overlay": sysfs_overlay,
+ }
+ )
+ plugin = UcPreparePlugin(properties=properties, part_info=part_info)
+
+ assert (
+ plugin.get_build_commands()[0]
+ == f"snap prepare-image --preseed --sysfs-overlay={sysfs_overlay} --validation=ignore model.assert {part_info.part_install_dir}"
+ )