Skip to content

Commit 864e440

Browse files
authored
Make issue creation check architecture instead of uname (home-assistant#146537)
1 parent 2f6fcb5 commit 864e440

File tree

6 files changed

+201
-98
lines changed

6 files changed

+201
-98
lines changed

homeassistant/components/hassio/__init__.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import logging
1010
import os
1111
import re
12+
import struct
1213
from typing import Any, NamedTuple
1314

15+
import aiofiles
1416
from aiohasupervisor import SupervisorError
1517
import voluptuous as vol
1618

@@ -56,7 +58,6 @@
5658
from homeassistant.helpers.service_info.hassio import (
5759
HassioServiceInfo as _HassioServiceInfo,
5860
)
59-
from homeassistant.helpers.system_info import async_get_system_info
6061
from homeassistant.helpers.typing import ConfigType
6162
from homeassistant.loader import bind_hass
6263
from homeassistant.util.async_ import create_eager_task
@@ -233,6 +234,17 @@ def valid_addon(value: Any) -> str:
233234
)
234235

235236

237+
def _is_32_bit() -> bool:
238+
size = struct.calcsize("P")
239+
return size * 8 == 32
240+
241+
242+
async def _get_arch() -> str:
243+
async with aiofiles.open("/etc/apk/arch") as arch_file:
244+
raw_arch = await arch_file.read()
245+
return {"x86": "i386"}.get(raw_arch, raw_arch)
246+
247+
236248
class APIEndpointSettings(NamedTuple):
237249
"""Settings for API endpoint."""
238250

@@ -554,28 +566,27 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
554566
await coordinator.async_config_entry_first_refresh()
555567
hass.data[ADDONS_COORDINATOR] = coordinator
556568

557-
system_info = await async_get_system_info(hass)
569+
arch = await _get_arch()
558570

559571
def deprecated_setup_issue() -> None:
560572
os_info = get_os_info(hass)
561573
info = get_info(hass)
562574
if os_info is None or info is None:
563575
return
564576
is_haos = info.get("hassos") is not None
565-
arch = system_info["arch"]
566577
board = os_info.get("board")
567-
supported_board = board in {"rpi3", "rpi4", "tinker", "odroid-xu4", "rpi2"}
568-
if is_haos and arch == "armv7" and supported_board:
578+
unsupported_board = board in {"tinker", "odroid-xu4", "rpi2"}
579+
unsupported_os_on_board = board in {"rpi3", "rpi4"}
580+
if is_haos and (unsupported_board or unsupported_os_on_board):
569581
issue_id = "deprecated_os_"
570-
if board in {"rpi3", "rpi4"}:
582+
if unsupported_os_on_board:
571583
issue_id += "aarch64"
572-
elif board in {"tinker", "odroid-xu4", "rpi2"}:
584+
elif unsupported_board:
573585
issue_id += "armv7"
574586
ir.async_create_issue(
575587
hass,
576588
"homeassistant",
577589
issue_id,
578-
breaks_in_ha_version="2025.12.0",
579590
learn_more_url=DEPRECATION_URL,
580591
is_fixable=False,
581592
severity=IssueSeverity.WARNING,
@@ -584,9 +595,10 @@ def deprecated_setup_issue() -> None:
584595
"installation_guide": "https://www.home-assistant.io/installation/",
585596
},
586597
)
587-
deprecated_architecture = False
588-
if arch in {"i386", "armhf"} or (arch == "armv7" and not supported_board):
589-
deprecated_architecture = True
598+
bit32 = _is_32_bit()
599+
deprecated_architecture = bit32 and not (
600+
unsupported_board or unsupported_os_on_board
601+
)
590602
if not is_haos or deprecated_architecture:
591603
issue_id = "deprecated"
592604
if not is_haos:
@@ -597,7 +609,6 @@ def deprecated_setup_issue() -> None:
597609
hass,
598610
"homeassistant",
599611
issue_id,
600-
breaks_in_ha_version="2025.12.0",
601612
learn_more_url=DEPRECATION_URL,
602613
is_fixable=False,
603614
severity=IssueSeverity.WARNING,

homeassistant/components/homeassistant/__init__.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
from collections.abc import Callable, Coroutine
55
import itertools as it
66
import logging
7+
import struct
78
from typing import Any
89

10+
import aiofiles
911
import voluptuous as vol
1012

1113
from homeassistant import config as conf_util, core_config
@@ -94,6 +96,17 @@
9496
)
9597

9698

99+
def _is_32_bit() -> bool:
100+
size = struct.calcsize("P")
101+
return size * 8 == 32
102+
103+
104+
async def _get_arch() -> str:
105+
async with aiofiles.open("/etc/apk/arch") as arch_file:
106+
raw_arch = (await arch_file.read()).strip()
107+
return {"x86": "i386", "x86_64": "amd64"}.get(raw_arch, raw_arch)
108+
109+
97110
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: # noqa: C901
98111
"""Set up general services related to Home Assistant."""
99112

@@ -403,23 +416,21 @@ async def async_handle_reload_all(call: ServiceCall) -> None:
403416
installation_type = info["installation_type"][15:]
404417
if installation_type in {"Core", "Container"}:
405418
deprecated_method = installation_type == "Core"
419+
bit32 = _is_32_bit()
406420
arch = info["arch"]
407-
if arch == "armv7" and installation_type == "Container":
421+
if bit32 and installation_type == "Container":
422+
arch = await _get_arch()
408423
ir.async_create_issue(
409424
hass,
410425
DOMAIN,
411-
"deprecated_container_armv7",
412-
breaks_in_ha_version="2025.12.0",
426+
"deprecated_container",
413427
learn_more_url=DEPRECATION_URL,
414428
is_fixable=False,
415429
severity=IssueSeverity.WARNING,
416-
translation_key="deprecated_container_armv7",
430+
translation_key="deprecated_container",
431+
translation_placeholders={"arch": arch},
417432
)
418-
deprecated_architecture = False
419-
if arch in {"i386", "armhf"} or (
420-
arch == "armv7" and installation_type != "Container"
421-
):
422-
deprecated_architecture = True
433+
deprecated_architecture = bit32 and installation_type != "Container"
423434
if deprecated_method or deprecated_architecture:
424435
issue_id = "deprecated"
425436
if deprecated_method:
@@ -430,7 +441,6 @@ async def async_handle_reload_all(call: ServiceCall) -> None:
430441
hass,
431442
DOMAIN,
432443
issue_id,
433-
breaks_in_ha_version="2025.12.0",
434444
learn_more_url=DEPRECATION_URL,
435445
is_fixable=False,
436446
severity=IssueSeverity.WARNING,

homeassistant/components/homeassistant/strings.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@
107107
"title": "Deprecation notice: 32-bit architecture",
108108
"description": "This system uses 32-bit hardware (`{arch}`), which has been deprecated and will no longer receive updates after the release of Home Assistant 2025.12. As your hardware is no longer capable of running newer versions of Home Assistant, you will need to migrate to new hardware."
109109
},
110-
"deprecated_container_armv7": {
110+
"deprecated_container": {
111111
"title": "[%key:component::homeassistant::issues::deprecated_architecture::title%]",
112-
"description": "This system is running on a 32-bit operating system (`armv7`), which has been deprecated and will no longer receive updates after the release of Home Assistant 2025.12. Check if your system is capable of running a 64-bit operating system. If not, you will need to migrate to new hardware."
112+
"description": "This system is running on a 32-bit operating system (`{arch}`), which has been deprecated and will no longer receive updates after the release of Home Assistant 2025.12. Check if your system is capable of running a 64-bit operating system. If not, you will need to migrate to new hardware."
113113
},
114114
"deprecated_os_aarch64": {
115115
"title": "[%key:component::homeassistant::issues::deprecated_architecture::title%]",

tests/components/hassio/conftest.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,16 @@ async def mock_addon_stats(addon: str) -> AddonsStats:
260260
},
261261
},
262262
)
263+
264+
265+
@pytest.fixture
266+
def arch() -> str:
267+
"""Arch found in apk file."""
268+
return "amd64"
269+
270+
271+
@pytest.fixture(autouse=True)
272+
def mock_arch_file(arch: str) -> Generator[None]:
273+
"""Mock arch file."""
274+
with patch("homeassistant.components.hassio._get_arch", return_value=arch):
275+
yield

tests/components/hassio/test_init.py

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,11 @@ def test_deprecated_constants(
11561156
("rpi2", "deprecated_os_armv7"),
11571157
],
11581158
)
1159-
async def test_deprecated_installation_issue_aarch64(
1159+
@pytest.mark.parametrize(
1160+
"arch",
1161+
["armv7"],
1162+
)
1163+
async def test_deprecated_installation_issue_os_armv7(
11601164
hass: HomeAssistant,
11611165
issue_registry: ir.IssueRegistry,
11621166
freezer: FrozenDateTimeFactory,
@@ -1167,18 +1171,15 @@ async def test_deprecated_installation_issue_aarch64(
11671171
with (
11681172
patch.dict(os.environ, MOCK_ENVIRON),
11691173
patch(
1170-
"homeassistant.components.hassio.async_get_system_info",
1174+
"homeassistant.components.homeassistant.async_get_system_info",
11711175
return_value={
11721176
"installation_type": "Home Assistant OS",
11731177
"arch": "armv7",
11741178
},
11751179
),
11761180
patch(
1177-
"homeassistant.components.homeassistant.async_get_system_info",
1178-
return_value={
1179-
"installation_type": "Home Assistant OS",
1180-
"arch": "armv7",
1181-
},
1181+
"homeassistant.components.hassio._is_32_bit",
1182+
return_value=True,
11821183
),
11831184
patch(
11841185
"homeassistant.components.hassio.get_os_info", return_value={"board": board}
@@ -1228,7 +1229,7 @@ async def test_deprecated_installation_issue_aarch64(
12281229
"armv7",
12291230
],
12301231
)
1231-
async def test_deprecated_installation_issue_32bit_method(
1232+
async def test_deprecated_installation_issue_32bit_os(
12321233
hass: HomeAssistant,
12331234
issue_registry: ir.IssueRegistry,
12341235
freezer: FrozenDateTimeFactory,
@@ -1238,18 +1239,15 @@ async def test_deprecated_installation_issue_32bit_method(
12381239
with (
12391240
patch.dict(os.environ, MOCK_ENVIRON),
12401241
patch(
1241-
"homeassistant.components.hassio.async_get_system_info",
1242+
"homeassistant.components.homeassistant.async_get_system_info",
12421243
return_value={
12431244
"installation_type": "Home Assistant OS",
12441245
"arch": arch,
12451246
},
12461247
),
12471248
patch(
1248-
"homeassistant.components.homeassistant.async_get_system_info",
1249-
return_value={
1250-
"installation_type": "Home Assistant OS",
1251-
"arch": arch,
1252-
},
1249+
"homeassistant.components.hassio._is_32_bit",
1250+
return_value=True,
12531251
),
12541252
patch(
12551253
"homeassistant.components.hassio.get_os_info",
@@ -1308,22 +1306,90 @@ async def test_deprecated_installation_issue_32bit_supervised(
13081306
with (
13091307
patch.dict(os.environ, MOCK_ENVIRON),
13101308
patch(
1311-
"homeassistant.components.hassio.async_get_system_info",
1309+
"homeassistant.components.homeassistant.async_get_system_info",
13121310
return_value={
13131311
"installation_type": "Home Assistant Supervised",
13141312
"arch": arch,
13151313
},
13161314
),
1315+
patch(
1316+
"homeassistant.components.hassio._is_32_bit",
1317+
return_value=True,
1318+
),
1319+
patch(
1320+
"homeassistant.components.hassio.get_os_info",
1321+
return_value={"board": "rpi3-64"},
1322+
),
1323+
patch(
1324+
"homeassistant.components.hassio.get_info", return_value={"hassos": None}
1325+
),
1326+
patch("homeassistant.components.hardware.async_setup", return_value=True),
1327+
):
1328+
assert await async_setup_component(hass, "homeassistant", {})
1329+
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
1330+
config_entry.add_to_hass(hass)
1331+
assert await hass.config_entries.async_setup(config_entry.entry_id)
1332+
await hass.async_block_till_done()
1333+
freezer.tick(REQUEST_REFRESH_DELAY)
1334+
async_fire_time_changed(hass)
1335+
await hass.async_block_till_done()
1336+
await hass.services.async_call(
1337+
"homeassistant",
1338+
"update_entity",
1339+
{
1340+
"entity_id": [
1341+
"update.home_assistant_core_update",
1342+
"update.home_assistant_supervisor_update",
1343+
]
1344+
},
1345+
blocking=True,
1346+
)
1347+
freezer.tick(HASSIO_UPDATE_INTERVAL)
1348+
async_fire_time_changed(hass)
1349+
await hass.async_block_till_done()
1350+
1351+
assert len(issue_registry.issues) == 1
1352+
issue = issue_registry.async_get_issue(
1353+
"homeassistant", "deprecated_method_architecture"
1354+
)
1355+
assert issue.domain == "homeassistant"
1356+
assert issue.severity == ir.IssueSeverity.WARNING
1357+
assert issue.translation_placeholders == {
1358+
"installation_type": "Supervised",
1359+
"arch": arch,
1360+
}
1361+
1362+
1363+
@pytest.mark.parametrize(
1364+
"arch",
1365+
[
1366+
"amd64",
1367+
"aarch64",
1368+
],
1369+
)
1370+
async def test_deprecated_installation_issue_64bit_supervised(
1371+
hass: HomeAssistant,
1372+
issue_registry: ir.IssueRegistry,
1373+
freezer: FrozenDateTimeFactory,
1374+
arch: str,
1375+
) -> None:
1376+
"""Test deprecated architecture issue."""
1377+
with (
1378+
patch.dict(os.environ, MOCK_ENVIRON),
13171379
patch(
13181380
"homeassistant.components.homeassistant.async_get_system_info",
13191381
return_value={
13201382
"installation_type": "Home Assistant Supervised",
13211383
"arch": arch,
13221384
},
13231385
),
1386+
patch(
1387+
"homeassistant.components.hassio._is_32_bit",
1388+
return_value=False,
1389+
),
13241390
patch(
13251391
"homeassistant.components.hassio.get_os_info",
1326-
return_value={"board": "rpi3-64"},
1392+
return_value={"board": "generic-x86-64"},
13271393
),
13281394
patch(
13291395
"homeassistant.components.hassio.get_info", return_value={"hassos": None}
@@ -1354,9 +1420,7 @@ async def test_deprecated_installation_issue_32bit_supervised(
13541420
await hass.async_block_till_done()
13551421

13561422
assert len(issue_registry.issues) == 1
1357-
issue = issue_registry.async_get_issue(
1358-
"homeassistant", "deprecated_method_architecture"
1359-
)
1423+
issue = issue_registry.async_get_issue("homeassistant", "deprecated_method")
13601424
assert issue.domain == "homeassistant"
13611425
assert issue.severity == ir.IssueSeverity.WARNING
13621426
assert issue.translation_placeholders == {
@@ -1382,18 +1446,15 @@ async def test_deprecated_installation_issue_supported_board(
13821446
with (
13831447
patch.dict(os.environ, MOCK_ENVIRON),
13841448
patch(
1385-
"homeassistant.components.hassio.async_get_system_info",
1449+
"homeassistant.components.homeassistant.async_get_system_info",
13861450
return_value={
13871451
"installation_type": "Home Assistant OS",
13881452
"arch": "aarch64",
13891453
},
13901454
),
13911455
patch(
1392-
"homeassistant.components.homeassistant.async_get_system_info",
1393-
return_value={
1394-
"installation_type": "Home Assistant OS",
1395-
"arch": "aarch64",
1396-
},
1456+
"homeassistant.components.hassio._is_32_bit",
1457+
return_value=False,
13971458
),
13981459
patch(
13991460
"homeassistant.components.hassio.get_os_info", return_value={"board": board}

0 commit comments

Comments
 (0)