diff --git a/supervisor/addons/addon.py b/supervisor/addons/addon.py index ab4f61646..1a56911fe 100644 --- a/supervisor/addons/addon.py +++ b/supervisor/addons/addon.py @@ -89,7 +89,7 @@ from ..hardware.data import Device from ..homeassistant.const import WSEvent from ..jobs.const import JobConcurrency, JobThrottle from ..jobs.decorator import Job -from ..resolution.const import ContextType, IssueType, UnhealthyReason +from ..resolution.const import ContextType, IssueType, SuggestionType, UnhealthyReason from ..resolution.data import Issue from ..store.addon import AddonStore from ..utils import check_port @@ -239,6 +239,19 @@ class Addon(AddonModel): await self._check_ingress_port() + if (self.has_deprecated_arch and not self.has_supported_arch) or ( + self.has_deprecated_machine and not self.has_supported_machine + ): + self.sys_resolution.create_issue( + IssueType.DEPRECATED_ARCH_ADDON, + ContextType.ADDON, + reference=self.slug, + suggestions=[SuggestionType.EXECUTE_REMOVE], + ) + with suppress(DockerError): + await self.instance.attach(version=self.version) + return + default_image = self._image(self.data) try: await self.instance.attach(version=self.version) diff --git a/supervisor/addons/model.py b/supervisor/addons/model.py index 4ac0a1030..0419d95c8 100644 --- a/supervisor/addons/model.py +++ b/supervisor/addons/model.py @@ -12,6 +12,7 @@ from typing import Any from awesomeversion import AwesomeVersion, AwesomeVersionException from ..const import ( + ARCH_DEPRECATED, ATTR_ADVANCED, ATTR_APPARMOR, ATTR_ARCH, @@ -78,6 +79,7 @@ from ..const import ( ATTR_VIDEO, ATTR_WATCHDOG, ATTR_WEBUI, + MACHINE_DEPRECATED, SECURITY_DEFAULT, SECURITY_DISABLE, SECURITY_PROFILE, @@ -94,6 +96,7 @@ from ..exceptions import ( AddonNotSupportedError, AddonNotSupportedHomeAssistantVersionError, AddonNotSupportedMachineTypeError, + HassioArchNotFound, ) from ..jobs.const import JOB_GROUP_ADDON from ..jobs.job_group import JobGroup @@ -542,6 +545,35 @@ class AddonModel(JobGroup, ABC): """Return list of supported arch.""" return self.data[ATTR_ARCH] + @property + def has_deprecated_arch(self) -> bool: + """Return True if add-on includes deprecated architectures.""" + return any(arch in ARCH_DEPRECATED for arch in self.supported_arch) + + @property + def has_supported_arch(self) -> bool: + """Return True if add-on supports any architecture on this system.""" + return self.sys_arch.is_supported(self.supported_arch) + + @property + def has_deprecated_machine(self) -> bool: + """Return True if add-on includes deprecated machine entries.""" + return any( + machine.lstrip("!") in MACHINE_DEPRECATED + for machine in self.supported_machine + ) + + @property + def has_supported_machine(self) -> bool: + """Return True if add-on supports this machine.""" + if not (machine_types := self.supported_machine): + return True + + return ( + f"!{self.sys_machine}" not in machine_types + and self.sys_machine in machine_types + ) + @property def supported_machine(self) -> list[str]: """Return list of supported machine.""" @@ -718,7 +750,10 @@ class AddonModel(JobGroup, ABC): """Generate image name from data.""" # Repository with Dockerhub images if ATTR_IMAGE in config: - arch = self.sys_arch.match(config[ATTR_ARCH]) + try: + arch = self.sys_arch.match(config[ATTR_ARCH]) + except HassioArchNotFound: + arch = self.sys_arch.default return config[ATTR_IMAGE].format(arch=arch) # local build diff --git a/supervisor/addons/validate.py b/supervisor/addons/validate.py index e51754c31..c56df63f4 100644 --- a/supervisor/addons/validate.py +++ b/supervisor/addons/validate.py @@ -9,7 +9,8 @@ import uuid import voluptuous as vol from ..const import ( - ARCH_ALL, + ARCH_ALL_COMPAT, + ARCH_DEPRECATED, ATTR_ACCESS_TOKEN, ATTR_ADVANCED, ATTR_APPARMOR, @@ -97,6 +98,7 @@ from ..const import ( ATTR_VIDEO, ATTR_WATCHDOG, ATTR_WEBUI, + MACHINE_DEPRECATED, ROLE_ALL, ROLE_DEFAULT, AddonBoot, @@ -156,6 +158,8 @@ SCHEMA_ELEMENT = vol.Schema( RE_MACHINE = re.compile( r"^!?(?:" r"|intel-nuc" + r"|khadas-vim3" + r"|generic-aarch64" r"|generic-x86-64" r"|odroid-c2" r"|odroid-c4" @@ -207,6 +211,26 @@ def _warn_addon_config(config: dict[str, Any]): name, ) + if deprecated_arches := [ + arch for arch in config.get(ATTR_ARCH, []) if arch in ARCH_DEPRECATED + ]: + _LOGGER.warning( + "Add-on config 'arch' uses deprecated values %s. Please report this to the maintainer of %s", + deprecated_arches, + name, + ) + + if deprecated_machines := [ + machine + for machine in config.get(ATTR_MACHINE, []) + if machine.lstrip("!") in MACHINE_DEPRECATED + ]: + _LOGGER.warning( + "Add-on config 'machine' uses deprecated values %s. Please report this to the maintainer of %s", + deprecated_machines, + name, + ) + if ATTR_CODENOTARY in config: _LOGGER.warning( "Add-on '%s' uses deprecated 'codenotary' field in config. This field is no longer used and will be ignored. Please report this to the maintainer.", @@ -349,7 +373,7 @@ _SCHEMA_ADDON_CONFIG = vol.Schema( vol.Required(ATTR_VERSION): version_tag, vol.Required(ATTR_SLUG): vol.Match(RE_SLUG_FIELD), vol.Required(ATTR_DESCRIPTON): str, - vol.Required(ATTR_ARCH): [vol.In(ARCH_ALL)], + vol.Required(ATTR_ARCH): [vol.In(ARCH_ALL_COMPAT)], vol.Optional(ATTR_MACHINE): vol.All([vol.Match(RE_MACHINE)], vol.Unique()), vol.Optional(ATTR_URL): vol.Url(), vol.Optional(ATTR_STARTUP, default=AddonStartup.APPLICATION): vol.Coerce( @@ -462,7 +486,7 @@ SCHEMA_BUILD_CONFIG = vol.Schema( { vol.Optional(ATTR_BUILD_FROM, default=dict): vol.Any( vol.Match(RE_DOCKER_IMAGE_BUILD), - vol.Schema({vol.In(ARCH_ALL): vol.Match(RE_DOCKER_IMAGE_BUILD)}), + vol.Schema({vol.In(ARCH_ALL_COMPAT): vol.Match(RE_DOCKER_IMAGE_BUILD)}), ), vol.Optional(ATTR_SQUASH, default=False): vol.Boolean(), vol.Optional(ATTR_ARGS, default=dict): vol.Schema({str: str}), diff --git a/supervisor/arch.py b/supervisor/arch.py index 882d06ba9..d48ef145b 100644 --- a/supervisor/arch.py +++ b/supervisor/arch.py @@ -14,11 +14,8 @@ _LOGGER: logging.Logger = logging.getLogger(__name__) ARCH_JSON: Path = Path(__file__).parent.joinpath("data/arch.json") MAP_CPU: dict[str, CpuArch] = { - "armv7": CpuArch.ARMV7, - "armv6": CpuArch.ARMHF, "armv8": CpuArch.AARCH64, "aarch64": CpuArch.AARCH64, - "i686": CpuArch.I386, "x86_64": CpuArch.AMD64, } diff --git a/supervisor/const.py b/supervisor/const.py index 7bd8a3c5b..ac9e5ef09 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -388,7 +388,20 @@ ARCH_AARCH64 = "aarch64" ARCH_AMD64 = "amd64" ARCH_I386 = "i386" -ARCH_ALL = [ARCH_ARMHF, ARCH_ARMV7, ARCH_AARCH64, ARCH_AMD64, ARCH_I386] +ARCH_ALL = [ARCH_AARCH64, ARCH_AMD64] +ARCH_DEPRECATED = [ARCH_ARMHF, ARCH_ARMV7, ARCH_I386] +ARCH_ALL_COMPAT = ARCH_ALL + ARCH_DEPRECATED + +MACHINE_DEPRECATED = [ + "odroid-xu", + "qemuarm", + "qemux86", + "raspberrypi", + "raspberrypi2", + "raspberrypi3", + "raspberrypi4", + "tinker", +] REPOSITORY_CORE = "core" REPOSITORY_LOCAL = "local" @@ -530,10 +543,7 @@ class BusEvent(StrEnum): class CpuArch(StrEnum): """Supported CPU architectures.""" - ARMV7 = "armv7" - ARMHF = "armhf" AARCH64 = "aarch64" - I386 = "i386" AMD64 = "amd64" diff --git a/supervisor/data/arch.json b/supervisor/data/arch.json index 0093cc510..8565aaf36 100644 --- a/supervisor/data/arch.json +++ b/supervisor/data/arch.json @@ -1,25 +1,17 @@ { - "raspberrypi": ["armhf"], - "raspberrypi2": ["armv7", "armhf"], - "raspberrypi3": ["armv7", "armhf"], - "raspberrypi3-64": ["aarch64", "armv7", "armhf"], - "raspberrypi4": ["armv7", "armhf"], - "raspberrypi4-64": ["aarch64", "armv7", "armhf"], - "raspberrypi5-64": ["aarch64", "armv7", "armhf"], - "yellow": ["aarch64", "armv7", "armhf"], - "green": ["aarch64", "armv7", "armhf"], - "tinker": ["armv7", "armhf"], - "odroid-c2": ["aarch64", "armv7", "armhf"], - "odroid-c4": ["aarch64", "armv7", "armhf"], - "odroid-m1": ["aarch64", "armv7", "armhf"], - "odroid-n2": ["aarch64", "armv7", "armhf"], - "odroid-xu": ["armv7", "armhf"], - "khadas-vim3": ["aarch64", "armv7", "armhf"], + "raspberrypi3-64": ["aarch64"], + "raspberrypi4-64": ["aarch64"], + "raspberrypi5-64": ["aarch64"], + "yellow": ["aarch64"], + "green": ["aarch64"], + "odroid-c2": ["aarch64"], + "odroid-c4": ["aarch64"], + "odroid-m1": ["aarch64"], + "odroid-n2": ["aarch64"], + "khadas-vim3": ["aarch64"], "generic-aarch64": ["aarch64"], - "qemux86": ["i386"], - "qemux86-64": ["amd64", "i386"], - "qemuarm": ["armhf"], + "qemux86-64": ["amd64"], "qemuarm-64": ["aarch64"], - "intel-nuc": ["amd64", "i386"], - "generic-x86-64": ["amd64", "i386"] + "intel-nuc": ["amd64"], + "generic-x86-64": ["amd64"] } diff --git a/supervisor/docker/interface.py b/supervisor/docker/interface.py index ce5e4d664..623895a22 100644 --- a/supervisor/docker/interface.py +++ b/supervisor/docker/interface.py @@ -50,10 +50,7 @@ from .stats import DockerStats _LOGGER: logging.Logger = logging.getLogger(__name__) MAP_ARCH: dict[CpuArch, str] = { - CpuArch.ARMV7: "linux/arm/v7", - CpuArch.ARMHF: "linux/arm/v6", CpuArch.AARCH64: "linux/arm64", - CpuArch.I386: "linux/386", CpuArch.AMD64: "linux/amd64", } diff --git a/supervisor/resolution/checks/deprecated_arch_addon.py b/supervisor/resolution/checks/deprecated_arch_addon.py new file mode 100644 index 000000000..d0f4c3010 --- /dev/null +++ b/supervisor/resolution/checks/deprecated_arch_addon.py @@ -0,0 +1,61 @@ +"""Helpers to check for add-ons using deprecated compatibility entries.""" + +from ...const import AddonStage, CoreState +from ...coresys import CoreSys +from ..const import ContextType, IssueType, SuggestionType +from .base import CheckBase + + +def setup(coresys: CoreSys) -> CheckBase: + """Check setup function.""" + return CheckDeprecatedArchAddon(coresys) + + +class CheckDeprecatedArchAddon(CheckBase): + """CheckDeprecatedArchAddon class for check.""" + + async def run_check(self) -> None: + """Run check if not affected by issue.""" + for addon in self.sys_addons.installed: + if addon.stage == AddonStage.DEPRECATED: + continue + + if (addon.has_deprecated_arch and not addon.has_supported_arch) or ( + addon.has_deprecated_machine and not addon.has_supported_machine + ): + self.sys_resolution.create_issue( + IssueType.DEPRECATED_ARCH_ADDON, + ContextType.ADDON, + reference=addon.slug, + suggestions=[SuggestionType.EXECUTE_REMOVE], + ) + + async def approve_check(self, reference: str | None = None) -> bool: + """Approve check if it is affected by issue.""" + if not reference: + return False + + addon = self.sys_addons.get_local_only(reference) + return ( + addon is not None + and addon.stage != AddonStage.DEPRECATED + and ( + (addon.has_deprecated_arch and not addon.has_supported_arch) + or (addon.has_deprecated_machine and not addon.has_supported_machine) + ) + ) + + @property + def issue(self) -> IssueType: + """Return a IssueType enum.""" + return IssueType.DEPRECATED_ARCH_ADDON + + @property + def context(self) -> ContextType: + """Return a ContextType enum.""" + return ContextType.ADDON + + @property + def states(self) -> list[CoreState]: + """Return a list of valid states when this check can run.""" + return [CoreState.SETUP, CoreState.RUNNING] diff --git a/supervisor/resolution/const.py b/supervisor/resolution/const.py index 34be58c2d..6142743ba 100644 --- a/supervisor/resolution/const.py +++ b/supervisor/resolution/const.py @@ -81,6 +81,7 @@ class IssueType(StrEnum): CORRUPT_REPOSITORY = "corrupt_repository" CORRUPT_FILESYSTEM = "corrupt_filesystem" DEPRECATED_ADDON = "deprecated_addon" + DEPRECATED_ARCH_ADDON = "deprecated_arch_addon" DETACHED_ADDON_MISSING = "detached_addon_missing" DETACHED_ADDON_REMOVED = "detached_addon_removed" DEVICE_ACCESS_MISSING = "device_access_missing" diff --git a/supervisor/resolution/evaluations/system_architecture.py b/supervisor/resolution/evaluations/system_architecture.py index 7a034e536..644fb8507 100644 --- a/supervisor/resolution/evaluations/system_architecture.py +++ b/supervisor/resolution/evaluations/system_architecture.py @@ -31,7 +31,7 @@ class EvaluateSystemArchitecture(EvaluateBase): async def evaluate(self): """Run evaluation.""" - return self.sys_host.info.sys_arch.supervisor in { + return self.sys_supervisor.arch in { "i386", "armhf", "armv7", diff --git a/supervisor/resolution/fixups/addon_execute_remove.py b/supervisor/resolution/fixups/addon_execute_remove.py index b3398fe93..ae77806c3 100644 --- a/supervisor/resolution/fixups/addon_execute_remove.py +++ b/supervisor/resolution/fixups/addon_execute_remove.py @@ -48,7 +48,7 @@ class FixupAddonExecuteRemove(FixupBase): @property def issues(self) -> list[IssueType]: """Return a IssueType enum list.""" - return [IssueType.DETACHED_ADDON_REMOVED] + return [IssueType.DETACHED_ADDON_REMOVED, IssueType.DEPRECATED_ARCH_ADDON] @property def auto(self) -> bool: diff --git a/tests/addons/test_config.py b/tests/addons/test_config.py index 2fa4645c6..d7ac00a77 100644 --- a/tests/addons/test_config.py +++ b/tests/addons/test_config.py @@ -187,6 +187,42 @@ def test_valid_basic_build(): vd.SCHEMA_BUILD_CONFIG(config) +def test_valid_legacy_arch_values_for_migration(): + """Validate legacy arch values are accepted for migration compatibility.""" + config = load_json_fixture("basic-addon-config.json") + config["arch"] = ["armv7", "amd64"] + + assert vd.SCHEMA_ADDON_CONFIG(config) + + +def test_valid_legacy_build_from_keys_for_migration(): + """Validate legacy build_from keys are accepted for migration compatibility.""" + config = load_json_fixture("basic-build-config.json") + config["build_from"]["i386"] = "mycustom/legacy-base:latest" + + assert vd.SCHEMA_BUILD_CONFIG(config) + + +def test_warn_legacy_arch_values(caplog: pytest.LogCaptureFixture): + """Warn when deprecated architecture values are present.""" + config = load_json_fixture("basic-addon-config.json") + config["arch"] = ["armv7", "amd64"] + + vd.SCHEMA_ADDON_CONFIG(config) + + assert "Add-on config 'arch' uses deprecated values" in caplog.text + + +def test_warn_legacy_machine_values(caplog: pytest.LogCaptureFixture): + """Warn when deprecated machine values are present.""" + config = load_json_fixture("basic-addon-config.json") + config["machine"] = ["qemux86"] + + vd.SCHEMA_ADDON_CONFIG(config) + + assert "Add-on config 'machine' uses deprecated values" in caplog.text + + async def test_valid_manifest_build(): """Validate build config with manifest build from.""" config = load_json_fixture("build-config-manifest.json") @@ -200,56 +236,66 @@ def test_valid_machine(): config["machine"] = [ "intel-nuc", + "khadas-vim3", + "generic-aarch64", "odroid-c2", "odroid-n2", "odroid-xu", - "qemuarm-64", "qemuarm", - "qemux86-64", + "qemuarm-64", "qemux86", + "qemux86-64", "raspberrypi", "raspberrypi2", - "raspberrypi3-64", "raspberrypi3", - "raspberrypi4-64", + "raspberrypi3-64", "raspberrypi4", + "raspberrypi4-64", "raspberrypi5-64", "tinker", + "yellow", + "green", + "generic-x86-64", ] assert vd.SCHEMA_ADDON_CONFIG(config) config["machine"] = [ "!intel-nuc", + "!khadas-vim3", + "!generic-aarch64", "!odroid-c2", "!odroid-n2", "!odroid-xu", - "!qemuarm-64", "!qemuarm", - "!qemux86-64", + "!qemuarm-64", "!qemux86", + "!qemux86-64", "!raspberrypi", "!raspberrypi2", - "!raspberrypi3-64", "!raspberrypi3", - "!raspberrypi4-64", + "!raspberrypi3-64", "!raspberrypi4", + "!raspberrypi4-64", "!raspberrypi5-64", "!tinker", + "!yellow", + "!green", + "!generic-x86-64", ] assert vd.SCHEMA_ADDON_CONFIG(config) config["machine"] = [ "odroid-n2", - "!odroid-xu", + "odroid-xu", + "qemuarm", "qemuarm-64", - "!qemuarm", - "qemux86-64", "qemux86", + "qemux86-64", "raspberrypi", - "raspberrypi4-64", "raspberrypi4", + "raspberrypi4-64", "raspberrypi5-64", "!tinker", ] @@ -263,10 +309,9 @@ def test_invalid_machine(): config["machine"] = [ "intel-nuc", - "raspberrypi3", "raspberrypi4-64", - "raspberrypi4", - "tinkerxy", + "raspberrypi7-64", + "generic-armv7", ] with pytest.raises(vol.Invalid): diff --git a/tests/api/test_store.py b/tests/api/test_store.py index beadaf1fd..bf084cbaa 100644 --- a/tests/api/test_store.py +++ b/tests/api/test_store.py @@ -587,12 +587,12 @@ async def test_api_store_addons_addon_availability_success( @pytest.mark.parametrize( ("supported_architectures", "api_action", "api_method", "installed"), [ - (["i386"], "availability", "get", False), - (["i386", "aarch64"], "availability", "get", False), - (["i386"], "install", "post", False), - (["i386", "aarch64"], "install", "post", False), - (["i386"], "update", "post", True), - (["i386", "aarch64"], "update", "post", True), + (["aarch64"], "availability", "get", False), + (["aarch64", "fooarch"], "availability", "get", False), + (["aarch64"], "install", "post", False), + (["aarch64", "fooarch"], "install", "post", False), + (["aarch64"], "update", "post", True), + (["aarch64", "fooarch"], "update", "post", True), ], ) async def test_api_store_addons_addon_availability_arch_not_supported( diff --git a/tests/docker/test_interface.py b/tests/docker/test_interface.py index a0353ba91..b355abea4 100644 --- a/tests/docker/test_interface.py +++ b/tests/docker/test_interface.py @@ -34,10 +34,7 @@ from tests.common import AsyncIterator, load_json_fixture @pytest.mark.parametrize( "cpu_arch, platform", [ - (CpuArch.ARMV7, "linux/arm/v7"), - (CpuArch.ARMHF, "linux/arm/v6"), (CpuArch.AARCH64, "linux/arm64"), - (CpuArch.I386, "linux/386"), (CpuArch.AMD64, "linux/amd64"), ], ) @@ -63,14 +60,14 @@ async def test_docker_image_default_platform( coresys.docker.images.inspect.return_value = {"Id": "test:1.2.3"} with ( patch.object( - type(coresys.supervisor), "arch", PropertyMock(return_value="i386") + type(coresys.supervisor), "arch", PropertyMock(return_value="amd64") ), ): await test_docker_interface.install(AwesomeVersion("1.2.3"), "test") coresys.docker.images.pull.assert_called_once_with( "test", tag="1.2.3", - platform="linux/386", + platform="linux/amd64", auth=None, stream=True, timeout=None, @@ -349,14 +346,14 @@ async def test_install_fires_progress_events( with ( patch.object( - type(coresys.supervisor), "arch", PropertyMock(return_value="i386") + type(coresys.supervisor), "arch", PropertyMock(return_value="amd64") ), ): await test_docker_interface.install(AwesomeVersion("1.2.3"), "test") coresys.docker.images.pull.assert_called_once_with( "test", tag="1.2.3", - platform="linux/386", + platform="linux/amd64", auth=None, stream=True, timeout=None, @@ -567,7 +564,7 @@ async def test_install_progress_handles_download_restart( with ( patch.object( - type(coresys.supervisor), "arch", PropertyMock(return_value="i386") + type(coresys.supervisor), "arch", PropertyMock(return_value="amd64") ), ): # Schedule job so we can listen for the end. Then we can assert against the WS mock @@ -805,7 +802,7 @@ async def test_install_progress_containerd_snapshot( async def mock_install(self) -> None: """Mock install.""" await super().install( - AwesomeVersion("1.2.3"), image="test", arch=CpuArch.I386 + AwesomeVersion("1.2.3"), image="test", arch=CpuArch.AMD64 ) # Fixture emulates log as received when using containerd snapshotter @@ -814,12 +811,12 @@ async def test_install_progress_containerd_snapshot( coresys.docker.images.pull.return_value = AsyncIterator(logs) test_docker_interface = TestDockerInterface(coresys) - with patch.object(Supervisor, "arch", PropertyMock(return_value="i386")): + with patch.object(Supervisor, "arch", PropertyMock(return_value="amd64")): await test_docker_interface.mock_install() coresys.docker.images.pull.assert_called_once_with( "test", tag="1.2.3", - platform="linux/386", + platform="linux/amd64", auth=None, stream=True, timeout=None, diff --git a/tests/fixtures/addon-config-add-image.json b/tests/fixtures/addon-config-add-image.json index a731433ec..79dc06be4 100644 --- a/tests/fixtures/addon-config-add-image.json +++ b/tests/fixtures/addon-config-add-image.json @@ -4,7 +4,7 @@ "slug": "ssh", "description": "Allow logging in remotely to Home Assistant using SSH", "url": "https://github.com/home-assistant/hassio-addons/tree/master/ssh", - "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], + "arch": ["aarch64", "amd64"], "init": false, "advanced": true, "host_dbus": true, diff --git a/tests/fixtures/addon-config-remove-image.json b/tests/fixtures/addon-config-remove-image.json index e05111ea8..99d06aaa4 100644 --- a/tests/fixtures/addon-config-remove-image.json +++ b/tests/fixtures/addon-config-remove-image.json @@ -4,7 +4,7 @@ "slug": "ssh", "description": "Allow logging in remotely to Home Assistant using SSH", "url": "https://github.com/home-assistant/hassio-addons/tree/master/ssh", - "arch": ["armhf", "armv7", "aarch64", "amd64", "i386"], + "arch": ["aarch64", "amd64"], "init": false, "advanced": true, "host_dbus": true, diff --git a/tests/fixtures/addons/core/samba/build.yaml b/tests/fixtures/addons/core/samba/build.yaml index 84dc2cbcb..db9fac1b2 100644 --- a/tests/fixtures/addons/core/samba/build.yaml +++ b/tests/fixtures/addons/core/samba/build.yaml @@ -1,6 +1,3 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.13" amd64: "ghcr.io/home-assistant/amd64-base:3.13" - armhf: "ghcr.io/home-assistant/armhf-base:3.13" - armv7: "ghcr.io/home-assistant/armv7-base:3.13" - i386: "ghcr.io/home-assistant/i386-base:3.13" diff --git a/tests/fixtures/addons/core/samba/config.yaml b/tests/fixtures/addons/core/samba/config.yaml index 51de0465a..d99e935f4 100644 --- a/tests/fixtures/addons/core/samba/config.yaml +++ b/tests/fixtures/addons/core/samba/config.yaml @@ -4,11 +4,8 @@ slug: samba description: Expose Home Assistant folders with SMB/CIFS url: "https://github.com/home-assistant/hassio-addons/tree/master/samba" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 startup: services init: false hassio_api: true diff --git a/tests/fixtures/addons/local/example/build.yaml b/tests/fixtures/addons/local/example/build.yaml index cdca3163d..8964c2eed 100644 --- a/tests/fixtures/addons/local/example/build.yaml +++ b/tests/fixtures/addons/local/example/build.yaml @@ -2,9 +2,6 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.15" amd64: "ghcr.io/home-assistant/amd64-base:3.15" - armhf: "ghcr.io/home-assistant/armhf-base:3.15" - armv7: "ghcr.io/home-assistant/armv7-base:3.15" - i386: "ghcr.io/home-assistant/i386-base:3.15" labels: org.opencontainers.image.title: "Home Assistant Add-on: Example add-on" org.opencontainers.image.description: "Example add-on to use as a blueprint for new add-ons." diff --git a/tests/fixtures/addons/local/example/config.yaml b/tests/fixtures/addons/local/example/config.yaml index ab94374ae..3f710b281 100644 --- a/tests/fixtures/addons/local/example/config.yaml +++ b/tests/fixtures/addons/local/example/config.yaml @@ -5,11 +5,8 @@ slug: example description: Example add-on url: "https://github.com/home-assistant/addons-example/tree/main/example" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 init: false map: - share:rw diff --git a/tests/fixtures/addons/local/example_image/build.yaml b/tests/fixtures/addons/local/example_image/build.yaml index cdca3163d..8964c2eed 100644 --- a/tests/fixtures/addons/local/example_image/build.yaml +++ b/tests/fixtures/addons/local/example_image/build.yaml @@ -2,9 +2,6 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.15" amd64: "ghcr.io/home-assistant/amd64-base:3.15" - armhf: "ghcr.io/home-assistant/armhf-base:3.15" - armv7: "ghcr.io/home-assistant/armv7-base:3.15" - i386: "ghcr.io/home-assistant/i386-base:3.15" labels: org.opencontainers.image.title: "Home Assistant Add-on: Example add-on" org.opencontainers.image.description: "Example add-on to use as a blueprint for new add-ons." diff --git a/tests/fixtures/addons/local/example_image/config.yaml b/tests/fixtures/addons/local/example_image/config.yaml index 0cee3435f..02878e4cd 100644 --- a/tests/fixtures/addons/local/example_image/config.yaml +++ b/tests/fixtures/addons/local/example_image/config.yaml @@ -5,11 +5,8 @@ slug: example_image description: Example add-on url: "https://github.com/home-assistant/addons-example/tree/main/example" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 init: false map: - share:rw diff --git a/tests/fixtures/addons/local/ssh/build.yaml b/tests/fixtures/addons/local/ssh/build.yaml index 973b3e320..b83c45868 100644 --- a/tests/fixtures/addons/local/ssh/build.yaml +++ b/tests/fixtures/addons/local/ssh/build.yaml @@ -1,9 +1,6 @@ build_from: aarch64: "ghcr.io/home-assistant/aarch64-base:3.14" amd64: "ghcr.io/home-assistant/amd64-base:3.14" - armhf: "ghcr.io/home-assistant/armhf-base:3.14" - armv7: "ghcr.io/home-assistant/armv7-base:3.14" - i386: "ghcr.io/home-assistant/i386-base:3.14" labels: org.opencontainers.image.title: "Home Assistant Add-on: Test add-on" org.opencontainers.image.description: "Test add-on PyTest." diff --git a/tests/fixtures/addons/local/ssh/config.yaml b/tests/fixtures/addons/local/ssh/config.yaml index 1d001ebd2..cec2040ec 100644 --- a/tests/fixtures/addons/local/ssh/config.yaml +++ b/tests/fixtures/addons/local/ssh/config.yaml @@ -4,11 +4,8 @@ slug: ssh description: Allow logging in remotely to Home Assistant using SSH url: "https://github.com/home-assistant/hassio-addons/tree/master/ssh" arch: - - armhf - - armv7 - aarch64 - amd64 - - i386 init: false advanced: true host_dbus: true diff --git a/tests/fixtures/basic-build-config.json b/tests/fixtures/basic-build-config.json index 392664f9b..b5fc8bedc 100644 --- a/tests/fixtures/basic-build-config.json +++ b/tests/fixtures/basic-build-config.json @@ -1,6 +1,5 @@ { "build_from": { - "armhf": "mycustom/base-image:latest", "aarch64": "mycustom/base-image", "amd64": "ghcr.io/home-assistant/amd64-base-ubuntu:18.04" }, diff --git a/tests/fixtures/version_stable.json b/tests/fixtures/version_stable.json index 460f21261..9eb27d2c8 100644 --- a/tests/fixtures/version_stable.json +++ b/tests/fixtures/version_stable.json @@ -28,21 +28,16 @@ }, "hassos": { "ova": "13.2", - "rpi2": "13.2", - "rpi3": "13.2", "rpi3-64": "13.2", - "rpi4": "13.2", "rpi4-64": "13.2", "rpi5-64": "13.2", "yellow": "13.2", "green": "13.2", - "tinker": "13.2", "odroid-c2": "13.2", "odroid-c4": "13.2", "odroid-m1": "13.2", "odroid-m1s": "13.2", "odroid-n2": "13.2", - "odroid-xu4": "13.2", "generic-x86-64": "13.2", "generic-aarch64": "13.2", "khadas-vim3": "13.2" diff --git a/tests/resolution/check/test_check_deprecated_arch_addon.py b/tests/resolution/check/test_check_deprecated_arch_addon.py new file mode 100644 index 000000000..e59f61368 --- /dev/null +++ b/tests/resolution/check/test_check_deprecated_arch_addon.py @@ -0,0 +1,164 @@ +"""Test check for add-ons with deprecated architectures.""" + +from unittest.mock import patch + +from supervisor.addons.addon import Addon +from supervisor.const import AddonStage, CoreState +from supervisor.coresys import CoreSys +from supervisor.resolution.checks.deprecated_arch_addon import CheckDeprecatedArchAddon +from supervisor.resolution.const import ContextType, IssueType, SuggestionType + + +async def test_base(coresys: CoreSys): + """Test check basics.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + assert deprecated_arch_addon.slug == "deprecated_arch_addon" + assert deprecated_arch_addon.enabled + + +async def test_check(coresys: CoreSys, install_addon_ssh: Addon): + """Test check for installed add-ons with deprecated architectures.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + await deprecated_arch_addon() + assert len(coresys.resolution.issues) == 0 + + install_addon_ssh.data["arch"] = ["armv7"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 1 + assert coresys.resolution.issues[0].type is IssueType.DEPRECATED_ARCH_ADDON + assert coresys.resolution.issues[0].context is ContextType.ADDON + assert coresys.resolution.issues[0].reference == install_addon_ssh.slug + assert len(coresys.resolution.suggestions) == 1 + assert coresys.resolution.suggestions[0].type is SuggestionType.EXECUTE_REMOVE + + +async def test_check_ignores_mixed_supported_arch( + coresys: CoreSys, install_addon_ssh: Addon +): + """Test check does not create issue when a supported arch is still present.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["arch"] = ["armv7", "amd64"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 0 + + +async def test_check_deprecated_machine(coresys: CoreSys, install_addon_ssh: Addon): + """Test check for installed add-ons using deprecated machine entries.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["machine"] = ["raspberrypi3"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 1 + assert coresys.resolution.issues[0].type is IssueType.DEPRECATED_ARCH_ADDON + assert coresys.resolution.suggestions[0].type is SuggestionType.EXECUTE_REMOVE + + +async def test_check_ignores_mixed_supported_machine( + coresys: CoreSys, install_addon_ssh: Addon +): + """Test check does not create issue when current machine is still supported.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["machine"] = ["raspberrypi3", install_addon_ssh.sys_machine] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 0 + + +async def test_check_ignores_stage_deprecated( + coresys: CoreSys, install_addon_ssh: Addon +): + """Test check does not create arch repair issue for already deprecated add-ons.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + install_addon_ssh.data["stage"] = AddonStage.DEPRECATED + install_addon_ssh.data["arch"] = ["armv7"] + + await deprecated_arch_addon() + + assert len(coresys.resolution.issues) == 0 + + +async def test_approve(coresys: CoreSys, install_addon_ssh: Addon): + """Test approve existing deprecated arch addon issues.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + await coresys.core.set_state(CoreState.SETUP) + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + install_addon_ssh.data["arch"] = ["armv7"] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is True + ) + + install_addon_ssh.data["arch"] = ["armv7", "amd64"] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + install_addon_ssh.data["arch"] = ["amd64"] + install_addon_ssh.data["machine"] = ["raspberrypi3"] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is True + ) + + install_addon_ssh.data["machine"] = ["raspberrypi3", install_addon_ssh.sys_machine] + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + install_addon_ssh.data["stage"] = AddonStage.DEPRECATED + + assert ( + await deprecated_arch_addon.approve_check(reference=install_addon_ssh.slug) + is False + ) + + +async def test_did_run(coresys: CoreSys): + """Test that the check ran as expected.""" + deprecated_arch_addon = CheckDeprecatedArchAddon(coresys) + should_run = deprecated_arch_addon.states + should_not_run = [state for state in CoreState if state not in should_run] + assert should_run == [CoreState.SETUP, CoreState.RUNNING] + assert len(should_not_run) != 0 + + with patch.object( + CheckDeprecatedArchAddon, "run_check", return_value=None + ) as check: + for state in should_run: + await coresys.core.set_state(state) + await deprecated_arch_addon() + check.assert_called_once() + check.reset_mock() + + for state in should_not_run: + await coresys.core.set_state(state) + await deprecated_arch_addon() + check.assert_not_called() + check.reset_mock() diff --git a/tests/resolution/fixup/test_addon_execute_remove.py b/tests/resolution/fixup/test_addon_execute_remove.py index 56a6fc2fe..869c2a1fa 100644 --- a/tests/resolution/fixup/test_addon_execute_remove.py +++ b/tests/resolution/fixup/test_addon_execute_remove.py @@ -37,3 +37,31 @@ async def test_fixup(coresys: CoreSys, install_addon_ssh: Addon): assert len(coresys.resolution.suggestions) == 0 assert len(coresys.resolution.issues) == 0 + + +async def test_fixup_deprecated_arch_addon(coresys: CoreSys, install_addon_ssh: Addon): + """Test fixup for deprecated arch add-on issue.""" + addon_execute_remove = FixupAddonExecuteRemove(coresys) + + coresys.resolution.add_suggestion( + Suggestion( + SuggestionType.EXECUTE_REMOVE, + ContextType.ADDON, + reference=install_addon_ssh.slug, + ) + ) + coresys.resolution.add_issue( + Issue( + IssueType.DEPRECATED_ARCH_ADDON, + ContextType.ADDON, + reference=install_addon_ssh.slug, + ) + ) + + with patch.object(Addon, "uninstall") as uninstall: + await addon_execute_remove() + + assert uninstall.called + + assert len(coresys.resolution.suggestions) == 0 + assert len(coresys.resolution.issues) == 0 diff --git a/tests/store/test_store_manager.py b/tests/store/test_store_manager.py index 4a328a928..736591862 100644 --- a/tests/store/test_store_manager.py +++ b/tests/store/test_store_manager.py @@ -127,8 +127,8 @@ async def test_reload_fails_if_out_of_date(coresys: CoreSys): "config,log", [ ( - {"arch": ["i386"]}, - "Add-on local_ssh not supported on this platform, supported architectures: i386", + {"arch": ["aarch64"]}, + "Add-on local_ssh not supported on this platform, supported architectures: aarch64", ), ( {"machine": ["odroid-n2"]}, @@ -186,8 +186,8 @@ async def test_update_unavailable_addon( "config,log", [ ( - {"arch": ["i386"]}, - "Add-on local_ssh not supported on this platform, supported architectures: i386", + {"arch": ["aarch64"]}, + "Add-on local_ssh not supported on this platform, supported architectures: aarch64", ), ( {"machine": ["odroid-n2"]}, diff --git a/tests/test_arch.py b/tests/test_arch.py index 6e482a38a..2c0dc33b0 100644 --- a/tests/test_arch.py +++ b/tests/test_arch.py @@ -16,7 +16,7 @@ def mock_detect_cpu_fixture(): async def test_machine_not_exits(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" + """Test fallback when machine is missing.""" sys_machine.return_value = None sys_supervisor.arch = "amd64" await coresys.arch.load() @@ -26,7 +26,7 @@ async def test_machine_not_exits(coresys, sys_machine, sys_supervisor): async def test_machine_not_exits_in_db(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" + """Test fallback when machine is unknown.""" sys_machine.return_value = "jedi-master-knight" sys_supervisor.arch = "amd64" await coresys.arch.load() @@ -36,7 +36,7 @@ async def test_machine_not_exits_in_db(coresys, sys_machine, sys_supervisor): async def test_supervisor_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" + """Test supervisor architecture property.""" sys_machine.return_value = None sys_supervisor.arch = "amd64" assert coresys.arch.supervisor == "amd64" @@ -46,36 +46,6 @@ async def test_supervisor_arch(coresys, sys_machine, sys_supervisor): assert coresys.arch.supervisor == "amd64" -async def test_raspberrypi_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi.""" - sys_machine.return_value = "raspberrypi" - sys_supervisor.arch = "armhf" - await coresys.arch.load() - - assert coresys.arch.default == "armhf" - assert coresys.arch.supported == [CpuArch.ARMHF] - - -async def test_raspberrypi2_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi2.""" - sys_machine.return_value = "raspberrypi2" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_raspberrypi3_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi3.""" - sys_machine.return_value = "raspberrypi3" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] - - async def test_raspberrypi3_64_arch(coresys, sys_machine, sys_supervisor): """Test arch for raspberrypi3_64.""" sys_machine.return_value = "raspberrypi3-64" @@ -83,17 +53,7 @@ async def test_raspberrypi3_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_raspberrypi4_arch(coresys, sys_machine, sys_supervisor): - """Test arch for raspberrypi4.""" - sys_machine.return_value = "raspberrypi4" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_raspberrypi4_64_arch(coresys, sys_machine, sys_supervisor): @@ -103,7 +63,7 @@ async def test_raspberrypi4_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_raspberrypi5_64_arch(coresys, sys_machine, sys_supervisor): @@ -113,7 +73,7 @@ async def test_raspberrypi5_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_yellow_arch(coresys, sys_machine, sys_supervisor): @@ -123,11 +83,11 @@ async def test_yellow_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] assert coresys.arch.is_supported(["aarch64"]) is True - assert coresys.arch.is_supported(["armv7"]) is True - assert coresys.arch.is_supported(["armhf"]) is True - assert coresys.arch.is_supported(["x86_64", "i386"]) is False + assert coresys.arch.is_supported(["fooarch"]) is False + assert coresys.arch.is_supported(["bararch"]) is False + assert coresys.arch.is_supported(["x86_64"]) is False async def test_green_arch(coresys, sys_machine, sys_supervisor): @@ -137,17 +97,7 @@ async def test_green_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_tinker_arch(coresys, sys_machine, sys_supervisor): - """Test arch for tinker.""" - sys_machine.return_value = "tinker" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_c2_arch(coresys, sys_machine, sys_supervisor): @@ -157,7 +107,7 @@ async def test_odroid_c2_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_c4_arch(coresys, sys_machine, sys_supervisor): @@ -167,7 +117,7 @@ async def test_odroid_c4_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_m1_arch(coresys, sys_machine, sys_supervisor): @@ -177,7 +127,7 @@ async def test_odroid_m1_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_odroid_n2_arch(coresys, sys_machine, sys_supervisor): @@ -187,17 +137,7 @@ async def test_odroid_n2_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "aarch64" - assert coresys.arch.supported == [CpuArch.AARCH64, CpuArch.ARMV7, CpuArch.ARMHF] - - -async def test_odroid_xu_arch(coresys, sys_machine, sys_supervisor): - """Test arch for odroid-xu.""" - sys_machine.return_value = "odroid-xu" - sys_supervisor.arch = "armv7" - await coresys.arch.load() - - assert coresys.arch.default == "armv7" - assert coresys.arch.supported == [CpuArch.ARMV7, CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AARCH64] async def test_intel_nuc_arch(coresys, sys_machine, sys_supervisor): @@ -207,17 +147,7 @@ async def test_intel_nuc_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "amd64" - assert coresys.arch.supported == [CpuArch.AMD64, CpuArch.I386] - - -async def test_qemux86_arch(coresys, sys_machine, sys_supervisor): - """Test arch for qemux86.""" - sys_machine.return_value = "qemux86" - sys_supervisor.arch = "i386" - await coresys.arch.load() - - assert coresys.arch.default == "i386" - assert coresys.arch.supported == [CpuArch.I386] + assert coresys.arch.supported == [CpuArch.AMD64] async def test_qemux86_64_arch(coresys, sys_machine, sys_supervisor): @@ -227,17 +157,7 @@ async def test_qemux86_64_arch(coresys, sys_machine, sys_supervisor): await coresys.arch.load() assert coresys.arch.default == "amd64" - assert coresys.arch.supported == [CpuArch.AMD64, CpuArch.I386] - - -async def test_qemuarm_arch(coresys, sys_machine, sys_supervisor): - """Test arch for qemuarm.""" - sys_machine.return_value = "qemuarm" - sys_supervisor.arch = "armhf" - await coresys.arch.load() - - assert coresys.arch.default == "armhf" - assert coresys.arch.supported == [CpuArch.ARMHF] + assert coresys.arch.supported == [CpuArch.AMD64] async def test_qemuarm_64_arch(coresys, sys_machine, sys_supervisor): @@ -248,16 +168,3 @@ async def test_qemuarm_64_arch(coresys, sys_machine, sys_supervisor): assert coresys.arch.default == "aarch64" assert coresys.arch.supported == [CpuArch.AARCH64] - - -async def test_qemuarm_arch_native_armv7( - coresys, sys_machine, mock_detect_cpu, sys_supervisor -): - """Test arch for qemuarm.""" - sys_machine.return_value = "qemuarm" - sys_supervisor.arch = "armhf" - mock_detect_cpu.return_value = "armv7l" - await coresys.arch.load() - - assert coresys.arch.default == "armhf" - assert coresys.arch.supported == [CpuArch.ARMHF, CpuArch.ARMV7] diff --git a/tests/test_updater.py b/tests/test_updater.py index 6fcd5e5d2..37d7dbb9e 100644 --- a/tests/test_updater.py +++ b/tests/test_updater.py @@ -84,7 +84,7 @@ async def test_os_update_path( supervisor_internet: AsyncMock, ): """Test OS upgrade path across major versions.""" - coresys.os._board = "rpi4" # pylint: disable=protected-access + coresys.os._board = "rpi4-64" # pylint: disable=protected-access coresys.os._version = AwesomeVersion(version) # pylint: disable=protected-access await coresys.updater.fetch_data() @@ -147,7 +147,7 @@ async def test_load_calls_reload_when_os_board_without_version( ) -> None: """Test load calls reload when OS board exists but no version_hassos_unrestricted.""" # Set up OS board but no version data - coresys.os._board = "rpi4" # pylint: disable=protected-access + coresys.os._board = "rpi4-64" # pylint: disable=protected-access coresys.security.force = True # Mock reload to verify it gets called @@ -162,7 +162,7 @@ async def test_load_skips_reload_when_os_board_with_version( ) -> None: """Test load skips reload when OS board exists and version_hassos_unrestricted is set.""" # Set up OS board and version data - coresys.os._board = "rpi4" # pylint: disable=protected-access + coresys.os._board = "rpi4-64" # pylint: disable=protected-access coresys.security.force = True # Pre-populate version_hassos_unrestricted by setting it directly on the data dict