Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions custom_components/hacs/validate/core_domain_override.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from ..enums import HacsCategory
from ..utils.json import json_loads
from .base import ActionValidationBase, ValidationException

if TYPE_CHECKING:
from ..repositories.base import HacsRepository
from ..repositories.integration import HacsIntegrationRepository

# The `next` site is generated from the upcoming Home Assistant release, so domains
# landing in the next release are caught before they ship, unlike `www`.
CORE_INTEGRATIONS_URL = "https://next.home-assistant.io/integrations.json"


async def async_setup_validator(repository: HacsRepository) -> Validator:
"""Set up this validator."""
return Validator(repository=repository)


class Validator(ActionValidationBase):
"""Validate the repository."""

repository: HacsIntegrationRepository
more_info = "https://hacs.xyz/docs/publish/include#check-core-domain-override"
categories = (HacsCategory.INTEGRATION,)

async def async_validate(self) -> None:
"""Validate the repository."""
if not (domain := self.repository.data.domain):
# A missing or invalid manifest is reported by the integration_manifest check.
return

result = await self.hacs.async_download_file(CORE_INTEGRATIONS_URL, handle_rate_limit=True)
if result is None:
raise ValidationException("Could not fetch the core integrations list")

try:
core_domains = json_loads(result)
except Exception as err:
raise ValidationException("Could not parse the core integrations list") from err

if not isinstance(core_domains, dict):
raise ValidationException("Core integrations list has an unexpected format")

if domain in core_domains:
raise ValidationException(
f"The integration overrides the core integration domain '{domain}'"
)
Comment thread
Copilot marked this conversation as resolved.
6 changes: 5 additions & 1 deletion tests/action/test_hacs_action_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ async def test_hacs_action_integration(
"https://brands.home-assistant.io/domains.json",
response=MockedResponse(status=200, content={"custom": ["example"]}),
)
response_mocker.add(
"https://next.home-assistant.io/integrations.json",
response=MockedResponse(status=200, content=json.dumps({}), keep=True),
)
response_mocker.add(
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json",
response=MockedResponse(
Expand All @@ -110,7 +114,7 @@ async def test_hacs_action_integration(
await preflight()

assert (
"All (9) checks passed" if test_case["succeed"] else "1/9 checks failed") in caplog.text
"All (10) checks passed" if test_case["succeed"] else "1/10 checks failed") in caplog.text

splitlines = [f"<{line.rsplit(' <')[1]}" for line in caplog.text.split(
"\n") if " <" in line]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
<Integration hacs-test-org/integration-basic> Getting manifest.json for version=main
<Validation archived> completed
<Validation brands> completed
<Validation core_domain_override> completed
<Validation description> completed
<Validation hacsjson> completed
<Validation information> completed
<Validation integration_manifest> failed: invalid url for dictionary value @ data['documentation']. Got None (More info: https://hacs.xyz/docs/publish/include#check-manifest )
<Validation issues> completed
<Validation license> completed
<Validation topics> completed
<Integration hacs-test-org/integration-basic> 1/9 checks failed
<Integration hacs-test-org/integration-basic> 1/10 checks failed
<Integration hacs-test-org/integration-basic> Validation completed
::group::data
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
<Integration hacs-test-org/integration-basic> Getting manifest.json for version=main
<Validation archived> completed
<Validation brands> completed
<Validation core_domain_override> completed
<Validation description> completed
<Validation hacsjson> completed
<Validation information> completed
<Validation integration_manifest> failed: invalid url for dictionary value @ data['issue_tracker']. Got None (More info: https://hacs.xyz/docs/publish/include#check-manifest )
<Validation issues> completed
<Validation license> completed
<Validation topics> completed
<Integration hacs-test-org/integration-basic> 1/9 checks failed
<Integration hacs-test-org/integration-basic> 1/10 checks failed
<Integration hacs-test-org/integration-basic> Validation completed
::group::data
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
<Integration hacs-test-org/integration-basic> Getting manifest.json for version=main
<Validation archived> completed
<Validation brands> completed
<Validation core_domain_override> completed
<Validation description> completed
<Validation hacsjson> completed
<Validation information> completed
<Validation integration_manifest> completed
<Validation issues> completed
<Validation license> completed
<Validation topics> completed
<Integration hacs-test-org/integration-basic> All (9) checks passed
<Integration hacs-test-org/integration-basic> All (10) checks passed
<Integration hacs-test-org/integration-basic> Validation completed
::group::data
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
<Integration hacs-test-org/integration-basic> Getting manifest.json for version=main
<Validation archived> completed
<Validation brands> completed
<Validation core_domain_override> completed
<Validation description> completed
<Validation hacsjson> completed
<Validation information> completed
<Validation integration_manifest> completed
<Validation issues> completed
<Validation license> completed
<Validation topics> completed
<Integration hacs-test-org/integration-basic> All (9) checks passed
<Integration hacs-test-org/integration-basic> All (10) checks passed
<Integration hacs-test-org/integration-basic> Validation completed
::group::data
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
<Integration hacs-test-org/integration-basic> Getting manifest.json for version=main
<Validation archived> completed
<Validation brands> completed
<Validation core_domain_override> completed
<Validation description> completed
<Validation hacsjson> completed
<Validation information> completed
<Validation integration_manifest> completed
<Validation issues> completed
<Validation license> completed
<Validation topics> completed
<Integration hacs-test-org/integration-basic> All (9) checks passed
<Integration hacs-test-org/integration-basic> All (10) checks passed
<Integration hacs-test-org/integration-basic> Validation completed
::group::data
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1,
"https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1,
"https://brands.home-assistant.io/domains.json": 1,
"https://next.home-assistant.io/integrations.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/hacs.json": 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1,
"https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1,
"https://brands.home-assistant.io/domains.json": 1,
"https://next.home-assistant.io/integrations.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/hacs.json": 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1,
"https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1,
"https://brands.home-assistant.io/domains.json": 1,
"https://next.home-assistant.io/integrations.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/hacs.json": 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1,
"https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1,
"https://brands.home-assistant.io/domains.json": 1,
"https://next.home-assistant.io/integrations.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/hacs.json": 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"https://api.github.com/repos/hacs-test-org/integration-basic/git/trees/main": 1,
"https://api.github.com/repos/hacs-test-org/integration-basic/releases": 1,
"https://brands.home-assistant.io/domains.json": 1,
"https://next.home-assistant.io/integrations.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/custom_components/example/manifest.json": 1,
"https://raw.githubusercontent.com/hacs-test-org/integration-basic/main/hacs.json": 1
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"https://api.github.com/repos/hacs/integration/contents/hacs.json": 1,
"https://api.github.com/repos/hacs/integration/git/trees/main": 1,
"https://api.github.com/repos/hacs/integration/releases": 1,
"https://brands.home-assistant.io/domains.json": 1
"https://brands.home-assistant.io/domains.json": 1,
"https://next.home-assistant.io/integrations.json": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tests/validate/test_core_domain_override_check.py::test_core_integrations_fetch_failure": {
"https://api.github.com/repos/hacs/integration": 1,
"https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1,
"https://api.github.com/repos/hacs/integration/contents/hacs.json": 1,
"https://api.github.com/repos/hacs/integration/git/trees/main": 1,
"https://api.github.com/repos/hacs/integration/releases": 1,
"https://next.home-assistant.io/integrations.json": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tests/validate/test_core_domain_override_check.py::test_core_integrations_invalid_json": {
"https://api.github.com/repos/hacs/integration": 1,
"https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1,
"https://api.github.com/repos/hacs/integration/contents/hacs.json": 1,
"https://api.github.com/repos/hacs/integration/git/trees/main": 1,
"https://api.github.com/repos/hacs/integration/releases": 1,
"https://next.home-assistant.io/integrations.json": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tests/validate/test_core_domain_override_check.py::test_domain_not_in_core": {
"https://api.github.com/repos/hacs/integration": 1,
"https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1,
"https://api.github.com/repos/hacs/integration/contents/hacs.json": 1,
"https://api.github.com/repos/hacs/integration/git/trees/main": 1,
"https://api.github.com/repos/hacs/integration/releases": 1,
"https://next.home-assistant.io/integrations.json": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tests/validate/test_core_domain_override_check.py::test_domain_overrides_core_domain": {
"https://api.github.com/repos/hacs/integration": 1,
"https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1,
"https://api.github.com/repos/hacs/integration/contents/hacs.json": 1,
"https://api.github.com/repos/hacs/integration/git/trees/main": 1,
"https://api.github.com/repos/hacs/integration/releases": 1,
"https://next.home-assistant.io/integrations.json": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"tests/validate/test_core_domain_override_check.py::test_no_domain_skips_fetch": {
"https://api.github.com/repos/hacs/integration": 1,
"https://api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json": 1,
"https://api.github.com/repos/hacs/integration/contents/hacs.json": 1,
"https://api.github.com/repos/hacs/integration/git/trees/main": 1,
"https://api.github.com/repos/hacs/integration/releases": 1
}
}
56 changes: 56 additions & 0 deletions tests/validate/test_core_domain_override_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import json

from custom_components.hacs.validate.core_domain_override import (
CORE_INTEGRATIONS_URL,
Validator,
)

from tests.common import MockedResponse, ResponseMocker


async def test_domain_not_in_core(repository, response_mocker: ResponseMocker):
response_mocker.add(
CORE_INTEGRATIONS_URL,
MockedResponse(content=json.dumps({"hue": {}})),
)
repository.data.domain = "test"
check = Validator(repository)
await check.execute_validation()
assert not check.failed


async def test_domain_overrides_core_domain(repository, response_mocker: ResponseMocker):
response_mocker.add(
CORE_INTEGRATIONS_URL,
MockedResponse(content=json.dumps({"test": {}})),
)
repository.data.domain = "test"
check = Validator(repository)
await check.execute_validation()
assert check.failed


async def test_core_integrations_fetch_failure(repository, response_mocker: ResponseMocker):
response_mocker.add(CORE_INTEGRATIONS_URL, MockedResponse(status=500))
repository.data.domain = "test"
check = Validator(repository)
await check.execute_validation()
assert check.failed


async def test_core_integrations_invalid_json(repository, response_mocker: ResponseMocker):
response_mocker.add(CORE_INTEGRATIONS_URL, MockedResponse(content="not json"))
repository.data.domain = "test"
check = Validator(repository)
await check.execute_validation()
assert check.failed


async def test_no_domain_skips_fetch(repository, response_mocker: ResponseMocker):
# Without a domain there is nothing to compare, the missing manifest is
# reported by the integration_manifest check instead.
response_mocker.add(CORE_INTEGRATIONS_URL, MockedResponse(status=500))
repository.data.domain = None
check = Validator(repository)
await check.execute_validation()
assert not check.failed
Loading