Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions snapcraft/parts/setup_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
from .desktop_file import DesktopFile


def _uses_legacy_system_metadata(project: models.Project) -> bool:
"""Return whether gadget/kernel metadata should follow the core22 path."""
return project.base == "core22" or (
project.base is None and project.build_base == "core22"
)
Comment on lines +37 to +39

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should simplify it:

Suggested change
return project.base == "core22" or (
project.base is None and project.build_base == "core22"
)
return project.get_effective_base() == "core22"



def setup_assets(
project: models.Project,
*,
Expand Down Expand Up @@ -67,16 +74,16 @@ def setup_assets(
)
setup_hooks(component.hooks, prime_dirs[component_name])

if project.type == const.ProjectType.GADGET:
gadget_yaml = project_dir / "gadget.yaml"
if not gadget_yaml.exists():
raise errors.SnapcraftError("gadget.yaml is required for gadget snaps")
_copy_file(gadget_yaml, meta_dir / "gadget.yaml")

if project.type == const.ProjectType.KERNEL:
kernel_yaml = project_dir / "kernel.yaml"
if kernel_yaml.exists():
_copy_file(kernel_yaml, meta_dir / "kernel.yaml")
if _uses_legacy_system_metadata(project):
if project.type == const.ProjectType.GADGET:
gadget_yaml = project_dir / "gadget.yaml"
if not gadget_yaml.exists():
raise errors.SnapcraftError("gadget.yaml is required for gadget snaps")
_copy_file(gadget_yaml, meta_dir / "gadget.yaml")
elif project.type == const.ProjectType.KERNEL:
kernel_yaml = project_dir / "kernel.yaml"
if kernel_yaml.exists():
_copy_file(kernel_yaml, meta_dir / "kernel.yaml")

icon_path = _finalize_icon(
project.icon, assets_dir=assets_dir, gui_dir=gui_dir, prime_dir=prime_dir
Expand Down
68 changes: 67 additions & 1 deletion snapcraft/services/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from craft_cli import emit
from typing_extensions import override

from snapcraft import errors, linters, models, pack
from snapcraft import const, errors, linters, models, pack
from snapcraft.errors import SnapcraftPrecreationEscapesPrimeError
from snapcraft.linters import LinterStatus
from snapcraft.meta import component_yaml, snap_yaml
Expand Down Expand Up @@ -68,6 +68,40 @@ def _get_component_yaml(self, partition: str | None = None) -> str:
component_name = partition.removeprefix("component/")
return component_yaml.get_str(self._project, component_name)

@package_file("meta/gadget.yaml", partition_re="default")
def _get_gadget_yaml(
self, partition: str | None = None # noqa: ARG002
) -> str | Literal[False] | None:
"""Generate mediated gadget metadata for core24+ gadget snaps.

Returns ``False`` (leave existing file untouched) when this project
should not be mediated (core22 snaps use the legacy copy path in
``setup_assets``) or when the project is not a gadget snap.
"""
if self._project.type != const.ProjectType.GADGET:
return False

return self._read_project_metadata_file(
"gadget.yaml",
required=True,
error_message="gadget.yaml is required for gadget snaps",
)
Comment thread
cmatsuoka marked this conversation as resolved.

@package_file("meta/kernel.yaml", partition_re="default")
def _get_kernel_yaml(
self, partition: str | None = None # noqa: ARG002
) -> str | Literal[False] | None:
"""Generate mediated kernel metadata for core24+ kernel snaps.

Returns ``False`` (leave existing file untouched) when this project
should not be mediated (core22 snaps use the legacy copy path in
``setup_assets``) or when the project is not a kernel snap.
"""
if self._project.type != const.ProjectType.KERNEL:
return False

return self._read_project_metadata_file("kernel.yaml")
Comment thread
cmatsuoka marked this conversation as resolved.

@override
def setup(self) -> None:
"""Application-specific service setup."""
Expand Down Expand Up @@ -433,6 +467,37 @@ def _get_assets_dir(self) -> pathlib.Path:
# This is for backwards compatibility with setup_assets(...)
return project_dir / "snap"

def _read_project_metadata_file(
self,
filename: str,
*,
required: bool = False,
error_message: str | None = None,
) -> str | None:
"""Read a top-level project metadata file if it exists."""
metadata_path = self._services.lifecycle.project_info.project_dir / filename
if metadata_path.exists():
return metadata_path.read_text(encoding="utf-8")

if required:
raise errors.SnapcraftError(error_message or f"{filename} is required")

return None

def _write_system_metadata(self, path: pathlib.Path) -> None:
"""Materialize mediated gadget/kernel metadata files for core24+ snaps."""
meta_dir = path / "meta"

if self._project.type == const.ProjectType.GADGET:
contents = self._get_gadget_yaml()
if isinstance(contents, str):
(meta_dir / "gadget.yaml").write_text(contents, encoding="utf-8")

if self._project.type == const.ProjectType.KERNEL:
contents = self._get_kernel_yaml()
if isinstance(contents, str):
(meta_dir / "kernel.yaml").write_text(contents, encoding="utf-8")

@override
def write_metadata(self, path: pathlib.Path) -> None:
"""Write the project metadata to metadata.yaml in the given directory.
Expand Down Expand Up @@ -466,6 +531,7 @@ def write_metadata(self, path: pathlib.Path) -> None:
prime_dirs=lifecycle_service.prime_dirs,
meta_directory_handler=meta_directory_handler,
)
self._write_system_metadata(path)

for component in self._project.get_component_names():
component_yaml.write(
Expand Down
52 changes: 50 additions & 2 deletions tests/unit/parts/test_setup_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,30 @@ def test_gadget(yaml_data, gadget_yaml_file, new_dir):
assert gadget_path.is_file()


def test_gadget_core24_not_copied(yaml_data, gadget_yaml_file, new_dir):
project = models.Project.unmarshal(
yaml_data(
{
"type": "gadget",
"base": "core24",
"build-base": "core24",
"version": "1.0",
"summary": "summary",
"description": "description",
}
)
)

setup_assets(
project,
assets_dir=Path("snap"),
project_dir=Path.cwd(),
prime_dirs={None: Path("prime")},
)

assert not Path("prime/meta/gadget.yaml").exists()


def test_gadget_missing(yaml_data, new_dir):
project = models.Project.unmarshal(
yaml_data(
Expand Down Expand Up @@ -144,7 +168,7 @@ def test_kernel(yaml_data, kernel_yaml_file, new_dir):
"summary": "summary",
"description": "description",
"parts": {},
"build-base": "devel",
"build-base": "core22",
}
)

Expand All @@ -160,6 +184,30 @@ def test_kernel(yaml_data, kernel_yaml_file, new_dir):
assert kernel_path.is_file()


def test_kernel_core24_not_copied(yaml_data, kernel_yaml_file, new_dir):
project = models.Project.unmarshal(
{
"name": "custom-kernel",
"type": "kernel",
"confinement": "strict",
"version": "1.0",
"summary": "summary",
"description": "description",
"parts": {},
"build-base": "core24",
}
)

setup_assets(
project,
assets_dir=Path("snap"),
project_dir=Path.cwd(),
prime_dirs={None: Path("prime")},
)

assert not Path("prime/meta/kernel.yaml").exists()


def test_kernel_missing(yaml_data, new_dir):
project = models.Project.unmarshal(
{
Expand All @@ -170,7 +218,7 @@ def test_kernel_missing(yaml_data, new_dir):
"summary": "summary",
"description": "description",
"parts": {},
"build-base": "devel",
"build-base": "core22",
}
)

Expand Down
Loading
Loading