1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Reject manifest dependencies on core integrations in hassfest (#169425)

This commit is contained in:
epenet
2026-04-29 09:52:46 +02:00
committed by GitHub
parent a7baedc22b
commit 8091f511b8
3 changed files with 65 additions and 4 deletions
@@ -3,7 +3,6 @@
"name": "Recovery Mode",
"codeowners": ["@home-assistant/core"],
"config_flow": false,
"dependencies": ["persistent_notification"],
"documentation": "https://www.home-assistant.io/integrations/recovery_mode",
"integration_type": "system",
"quality_scale": "internal"
+12 -2
View File
@@ -13,6 +13,10 @@ from homeassistant.requirements import DISCOVERY_INTEGRATIONS
from . import ast_parse_module
from .model import Config, Integration
# Duplicated from homeassistant.bootstrap to avoid importing bootstrap (and its
# eager component pre-imports) into hassfest. Kept in sync via test_dependencies.
CORE_INTEGRATIONS = {"homeassistant", "persistent_notification"}
class ImportCollector(ast.NodeVisitor):
"""Collect all integrations referenced."""
@@ -86,6 +90,7 @@ class ImportCollector(ast.NodeVisitor):
ALLOWED_USED_COMPONENTS = {
*CORE_INTEGRATIONS,
*{platform.value for platform in Platform},
# Internal integrations
"alert",
@@ -95,7 +100,6 @@ ALLOWED_USED_COMPONENTS = {
"device_automation",
"frontend",
"group",
"homeassistant",
"input_boolean",
"input_button",
"input_datetime",
@@ -106,7 +110,6 @@ ALLOWED_USED_COMPONENTS = {
"media_source",
"onboarding",
"panel_custom",
"persistent_notification",
"person",
"script",
"shopping_list",
@@ -332,6 +335,13 @@ def _validate_dependencies(
"dependencies", f"Dependency {dep} does not exist"
)
if dep in CORE_INTEGRATIONS:
integration.add_error(
"dependencies",
f"Dependency {dep} is a core integration and is "
"unconditionally loaded",
)
def validate(
integrations: dict[str, Integration],
+53 -1
View File
@@ -4,7 +4,14 @@ import ast
import pytest
from script.hassfest.dependencies import ImportCollector
from script.hassfest.dependencies import (
CORE_INTEGRATIONS,
ImportCollector,
_validate_dependencies,
)
from script.hassfest.model import Config
from . import get_integration
@pytest.fixture
@@ -90,3 +97,48 @@ import homeassistant.components.renamed_absolute as hue
"child_import_field",
"renamed_absolute",
}
def test_dependency_on_core_integration_rejected(config: Config) -> None:
"""Test that depending on a core integration is rejected."""
consumer = get_integration("consumer", config)
consumer.manifest["dependencies"] = ["persistent_notification"]
integrations = {
"consumer": consumer,
"persistent_notification": get_integration("persistent_notification", config),
}
_validate_dependencies(integrations)
assert len(consumer.errors) == 1
assert (
"Dependency persistent_notification is a core integration"
in consumer.errors[0].error
)
def test_dependency_on_non_core_integration_allowed(config: Config) -> None:
"""Test that depending on a non-core integration is not rejected."""
consumer = get_integration("consumer", config)
consumer.manifest["dependencies"] = ["other"]
integrations = {
"consumer": consumer,
"other": get_integration("other", config),
}
_validate_dependencies(integrations)
assert consumer.errors == []
def test_core_integrations_in_sync_with_bootstrap() -> None:
"""Test the duplicated CORE_INTEGRATIONS stays aligned with bootstrap."""
# Imported here so the rest of the hassfest tests are not slowed down
# by bootstrap's eager component pre-imports.
from homeassistant.bootstrap import ( # noqa: PLC0415
CORE_INTEGRATIONS as bootstrap_core_integrations,
)
assert bootstrap_core_integrations == CORE_INTEGRATIONS