Add custom integration block list (#112481)
* Add custom integration block list * Fix typo * Add version condition * Add block reason, simplify blocked versions, add tests * Change logic for OK versions * Add link to custom integration's issue tracker * Add missing file --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
780428fde6
commit
807c3ca76b
3 changed files with 115 additions and 5 deletions
|
@ -82,6 +82,19 @@ BASE_PRELOAD_PLATFORMS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BlockedIntegration:
|
||||||
|
"""Blocked custom integration details."""
|
||||||
|
|
||||||
|
lowest_good_version: AwesomeVersion | None
|
||||||
|
reason: str
|
||||||
|
|
||||||
|
|
||||||
|
BLOCKED_CUSTOM_INTEGRATIONS: dict[str, BlockedIntegration] = {
|
||||||
|
# Added in 2024.3.0 because of https://github.com/home-assistant/core/issues/112464
|
||||||
|
"start_time": BlockedIntegration(None, "breaks Home Assistant")
|
||||||
|
}
|
||||||
|
|
||||||
DATA_COMPONENTS = "components"
|
DATA_COMPONENTS = "components"
|
||||||
DATA_INTEGRATIONS = "integrations"
|
DATA_INTEGRATIONS = "integrations"
|
||||||
DATA_MISSING_PLATFORMS = "missing_platforms"
|
DATA_MISSING_PLATFORMS = "missing_platforms"
|
||||||
|
@ -643,6 +656,7 @@ class Integration:
|
||||||
return integration
|
return integration
|
||||||
|
|
||||||
_LOGGER.warning(CUSTOM_WARNING, integration.domain)
|
_LOGGER.warning(CUSTOM_WARNING, integration.domain)
|
||||||
|
|
||||||
if integration.version is None:
|
if integration.version is None:
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
(
|
(
|
||||||
|
@ -679,6 +693,21 @@ class Integration:
|
||||||
integration.version,
|
integration.version,
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if blocked := BLOCKED_CUSTOM_INTEGRATIONS.get(integration.domain):
|
||||||
|
if _version_blocked(integration.version, blocked):
|
||||||
|
_LOGGER.error(
|
||||||
|
(
|
||||||
|
"Version %s of custom integration '%s' %s and was blocked "
|
||||||
|
"from loading, please %s"
|
||||||
|
),
|
||||||
|
integration.version,
|
||||||
|
integration.domain,
|
||||||
|
blocked.reason,
|
||||||
|
async_suggest_report_issue(None, integration=integration),
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
return integration
|
return integration
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
@ -1210,6 +1239,20 @@ class Integration:
|
||||||
return f"<Integration {self.domain}: {self.pkg_path}>"
|
return f"<Integration {self.domain}: {self.pkg_path}>"
|
||||||
|
|
||||||
|
|
||||||
|
def _version_blocked(
|
||||||
|
integration_version: AwesomeVersion,
|
||||||
|
blocked_integration: BlockedIntegration,
|
||||||
|
) -> bool:
|
||||||
|
"""Return True if the integration version is blocked."""
|
||||||
|
if blocked_integration.lowest_good_version is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if integration_version >= blocked_integration.lowest_good_version:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _resolve_integrations_from_root(
|
def _resolve_integrations_from_root(
|
||||||
hass: HomeAssistant, root_module: ModuleType, domains: Iterable[str]
|
hass: HomeAssistant, root_module: ModuleType, domains: Iterable[str]
|
||||||
) -> dict[str, Integration]:
|
) -> dict[str, Integration]:
|
||||||
|
@ -1565,6 +1608,7 @@ def is_component_module_loaded(hass: HomeAssistant, module: str) -> bool:
|
||||||
def async_get_issue_tracker(
|
def async_get_issue_tracker(
|
||||||
hass: HomeAssistant | None,
|
hass: HomeAssistant | None,
|
||||||
*,
|
*,
|
||||||
|
integration: Integration | None = None,
|
||||||
integration_domain: str | None = None,
|
integration_domain: str | None = None,
|
||||||
module: str | None = None,
|
module: str | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
|
@ -1572,19 +1616,23 @@ def async_get_issue_tracker(
|
||||||
issue_tracker = (
|
issue_tracker = (
|
||||||
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
|
"https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue"
|
||||||
)
|
)
|
||||||
if not integration_domain and not module:
|
if not integration and not integration_domain and not module:
|
||||||
# If we know nothing about the entity, suggest opening an issue on HA core
|
# If we know nothing about the entity, suggest opening an issue on HA core
|
||||||
return issue_tracker
|
return issue_tracker
|
||||||
|
|
||||||
if hass and integration_domain:
|
if not integration and (hass and integration_domain):
|
||||||
with suppress(IntegrationNotLoaded):
|
with suppress(IntegrationNotLoaded):
|
||||||
integration = async_get_loaded_integration(hass, integration_domain)
|
integration = async_get_loaded_integration(hass, integration_domain)
|
||||||
if not integration.is_built_in:
|
|
||||||
return integration.issue_tracker
|
if integration and not integration.is_built_in:
|
||||||
|
return integration.issue_tracker
|
||||||
|
|
||||||
if module and "custom_components" in module:
|
if module and "custom_components" in module:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if integration:
|
||||||
|
integration_domain = integration.domain
|
||||||
|
|
||||||
if integration_domain:
|
if integration_domain:
|
||||||
issue_tracker += f"+label%3A%22integration%3A+{integration_domain}%22"
|
issue_tracker += f"+label%3A%22integration%3A+{integration_domain}%22"
|
||||||
return issue_tracker
|
return issue_tracker
|
||||||
|
@ -1594,15 +1642,21 @@ def async_get_issue_tracker(
|
||||||
def async_suggest_report_issue(
|
def async_suggest_report_issue(
|
||||||
hass: HomeAssistant | None,
|
hass: HomeAssistant | None,
|
||||||
*,
|
*,
|
||||||
|
integration: Integration | None = None,
|
||||||
integration_domain: str | None = None,
|
integration_domain: str | None = None,
|
||||||
module: str | None = None,
|
module: str | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Generate a blurb asking the user to file a bug report."""
|
"""Generate a blurb asking the user to file a bug report."""
|
||||||
issue_tracker = async_get_issue_tracker(
|
issue_tracker = async_get_issue_tracker(
|
||||||
hass, integration_domain=integration_domain, module=module
|
hass,
|
||||||
|
integration=integration,
|
||||||
|
integration_domain=integration_domain,
|
||||||
|
module=module,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not issue_tracker:
|
if not issue_tracker:
|
||||||
|
if integration:
|
||||||
|
integration_domain = integration.domain
|
||||||
if not integration_domain:
|
if not integration_domain:
|
||||||
return "report it to the custom integration author"
|
return "report it to the custom integration author"
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -6,6 +6,7 @@ import threading
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import MagicMock, Mock, patch
|
from unittest.mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
|
from awesomeversion import AwesomeVersion
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant import loader
|
from homeassistant import loader
|
||||||
|
@ -167,6 +168,57 @@ async def test_custom_integration_version_not_valid(
|
||||||
) in caplog.text
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"blocked_versions",
|
||||||
|
[
|
||||||
|
loader.BlockedIntegration(None, "breaks Home Assistant"),
|
||||||
|
loader.BlockedIntegration(AwesomeVersion("2.0.0"), "breaks Home Assistant"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_custom_integration_version_blocked(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
blocked_versions,
|
||||||
|
) -> None:
|
||||||
|
"""Test that we log a warning when custom integrations have a blocked version."""
|
||||||
|
with patch.dict(
|
||||||
|
loader.BLOCKED_CUSTOM_INTEGRATIONS, {"test_blocked_version": blocked_versions}
|
||||||
|
):
|
||||||
|
with pytest.raises(loader.IntegrationNotFound):
|
||||||
|
await loader.async_get_integration(hass, "test_blocked_version")
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Version 1.0.0 of custom integration 'test_blocked_version' breaks"
|
||||||
|
" Home Assistant and was blocked from loading, please report it to the"
|
||||||
|
" author of the 'test_blocked_version' custom integration"
|
||||||
|
) in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"blocked_versions",
|
||||||
|
[
|
||||||
|
loader.BlockedIntegration(AwesomeVersion("0.9.9"), "breaks Home Assistant"),
|
||||||
|
loader.BlockedIntegration(AwesomeVersion("1.0.0"), "breaks Home Assistant"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_custom_integration_version_not_blocked(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
caplog: pytest.LogCaptureFixture,
|
||||||
|
enable_custom_integrations: None,
|
||||||
|
blocked_versions,
|
||||||
|
) -> None:
|
||||||
|
"""Test that we log a warning when custom integrations have a blocked version."""
|
||||||
|
with patch.dict(
|
||||||
|
loader.BLOCKED_CUSTOM_INTEGRATIONS, {"test_blocked_version": blocked_versions}
|
||||||
|
):
|
||||||
|
await loader.async_get_integration(hass, "test_blocked_version")
|
||||||
|
|
||||||
|
assert (
|
||||||
|
"Version 1.0.0 of custom integration 'test_blocked_version'"
|
||||||
|
) not in caplog.text
|
||||||
|
|
||||||
|
|
||||||
async def test_get_integration(hass: HomeAssistant) -> None:
|
async def test_get_integration(hass: HomeAssistant) -> None:
|
||||||
"""Test resolving integration."""
|
"""Test resolving integration."""
|
||||||
with pytest.raises(loader.IntegrationNotLoaded):
|
with pytest.raises(loader.IntegrationNotLoaded):
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"domain": "test_blocked_version",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue