diff --git a/supervisor/addons/build.py b/supervisor/addons/build.py index c7806e876..2acdbef1b 100644 --- a/supervisor/addons/build.py +++ b/supervisor/addons/build.py @@ -1,6 +1,7 @@ """Supervisor add-on build environment.""" from __future__ import annotations +from functools import cached_property from pathlib import Path from typing import TYPE_CHECKING @@ -16,7 +17,7 @@ from ..const import ( ) from ..coresys import CoreSys, CoreSysAttributes from ..docker.interface import MAP_ARCH -from ..exceptions import ConfigurationFileError +from ..exceptions import ConfigurationFileError, HassioArchNotFound from ..utils.common import FileConfiguration, find_one_filetype from .validate import SCHEMA_BUILD_CONFIG @@ -45,6 +46,11 @@ class AddonBuild(FileConfiguration, CoreSysAttributes): """Ignore save function.""" raise RuntimeError() + @cached_property + def arch(self) -> str: + """Return arch of the add-on.""" + return self.sys_arch.match(self.addon.arch) + @property def base_image(self) -> str: """Return base image for this add-on.""" @@ -55,8 +61,18 @@ class AddonBuild(FileConfiguration, CoreSysAttributes): return self._data[ATTR_BUILD_FROM] # Evaluate correct base image - arch = self.sys_arch.match(list(self._data[ATTR_BUILD_FROM].keys())) - return self._data[ATTR_BUILD_FROM][arch] + if self.arch not in self._data[ATTR_BUILD_FROM]: + raise HassioArchNotFound( + f"Add-on {self.addon.slug} is not supported on {self.arch}" + ) + return self._data[ATTR_BUILD_FROM][self.arch] + + @property + def dockerfile(self) -> Path: + """Return Dockerfile path.""" + if self.addon.path_location.joinpath(f"Dockerfile.{self.arch}").exists(): + return self.addon.path_location.joinpath(f"Dockerfile.{self.arch}") + return self.addon.path_location.joinpath("Dockerfile") @property def squash(self) -> bool: @@ -76,25 +92,29 @@ class AddonBuild(FileConfiguration, CoreSysAttributes): @property def is_valid(self) -> bool: """Return true if the build env is valid.""" - return all( - [ - self.addon.path_location.is_dir(), - Path(self.addon.path_location, "Dockerfile").is_file(), - ] - ) + try: + return all( + [ + self.addon.path_location.is_dir(), + self.dockerfile.is_file(), + ] + ) + except HassioArchNotFound: + return False def get_docker_args(self, version: AwesomeVersion): """Create a dict with Docker build arguments.""" args = { "path": str(self.addon.path_location), "tag": f"{self.addon.image}:{version!s}", + "dockerfile": str(self.dockerfile), "pull": True, "forcerm": not self.sys_dev, "squash": self.squash, - "platform": MAP_ARCH[self.sys_arch.match(self.addon.arch)], + "platform": MAP_ARCH[self.arch], "labels": { "io.hass.version": version, - "io.hass.arch": self.sys_arch.default, + "io.hass.arch": self.arch, "io.hass.type": META_ADDON, "io.hass.name": self._fix_label("name"), "io.hass.description": self._fix_label("description"), diff --git a/tests/addons/test_build.py b/tests/addons/test_build.py index 7e909dc4b..6366cd07f 100644 --- a/tests/addons/test_build.py +++ b/tests/addons/test_build.py @@ -19,3 +19,57 @@ async def test_platform_set(coresys: CoreSys, install_addon_ssh: Addon): args = build.get_docker_args(AwesomeVersion("latest")) assert args["platform"] == "linux/amd64" + + +async def test_dockerfile_evaluation(coresys: CoreSys, install_addon_ssh: Addon): + """Test platform set in docker args.""" + build = AddonBuild(coresys, install_addon_ssh) + with patch.object( + type(coresys.arch), "supported", new=PropertyMock(return_value=["amd64"]) + ), patch.object( + type(coresys.arch), "default", new=PropertyMock(return_value="amd64") + ): + args = build.get_docker_args(AwesomeVersion("latest")) + + assert args["dockerfile"].endswith("fixtures/addons/local/ssh/Dockerfile") + assert str(build.dockerfile).endswith("fixtures/addons/local/ssh/Dockerfile") + assert build.arch == "amd64" + + +async def test_dockerfile_evaluation_arch(coresys: CoreSys, install_addon_ssh: Addon): + """Test platform set in docker args.""" + build = AddonBuild(coresys, install_addon_ssh) + with patch.object( + type(coresys.arch), "supported", new=PropertyMock(return_value=["aarch64"]) + ), patch.object( + type(coresys.arch), "default", new=PropertyMock(return_value="aarch64") + ): + args = build.get_docker_args(AwesomeVersion("latest")) + + assert args["dockerfile"].endswith("fixtures/addons/local/ssh/Dockerfile.aarch64") + assert str(build.dockerfile).endswith( + "fixtures/addons/local/ssh/Dockerfile.aarch64" + ) + assert build.arch == "aarch64" + + +async def test_build_valid(coresys: CoreSys, install_addon_ssh: Addon): + """Test platform set in docker args.""" + build = AddonBuild(coresys, install_addon_ssh) + with patch.object( + type(coresys.arch), "supported", new=PropertyMock(return_value=["aarch64"]) + ), patch.object( + type(coresys.arch), "default", new=PropertyMock(return_value="aarch64") + ): + assert build.is_valid + + +async def test_build_invalid(coresys: CoreSys, install_addon_ssh: Addon): + """Test platform set in docker args.""" + build = AddonBuild(coresys, install_addon_ssh) + with patch.object( + type(coresys.arch), "supported", new=PropertyMock(return_value=["amd64"]) + ), patch.object( + type(coresys.arch), "default", new=PropertyMock(return_value="amd64") + ): + assert not build.is_valid diff --git a/tests/fixtures/addons/local/ssh/Dockerfile.aarch64 b/tests/fixtures/addons/local/ssh/Dockerfile.aarch64 new file mode 100644 index 000000000..e69de29bb