-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
234 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1 @@ | ||
from __future__ import annotations | ||
|
||
import asyncio | ||
import glob | ||
import importlib | ||
from os.path import dirname, join, sep | ||
from typing import TYPE_CHECKING | ||
|
||
from homeassistant.core import HomeAssistant | ||
|
||
from ..share import SHARE | ||
|
||
if TYPE_CHECKING: | ||
from ..base import HacsBase | ||
from ..repositories.base import HacsRepository | ||
|
||
|
||
def _initialize_rules(): | ||
rules = glob.glob(join(dirname(__file__), "**/*.py")) | ||
for rule in rules: | ||
rule = rule.replace(sep, "/") | ||
rule = rule.split("custom_components/hacs")[-1] | ||
rule = f"custom_components/hacs{rule}".replace("/", ".")[:-3] | ||
importlib.import_module(rule) | ||
|
||
|
||
async def async_initialize_rules(hass: HomeAssistant) -> None: | ||
await hass.async_add_executor_job(_initialize_rules) | ||
|
||
|
||
async def async_run_repository_checks(hacs: HacsBase, repository: HacsRepository): | ||
if not SHARE["rules"]: | ||
await async_initialize_rules(hacs.hass) | ||
if not hacs.system.running: | ||
return | ||
checks = [] | ||
for check in SHARE["rules"].get("common", []): | ||
checks.append(check(repository)) | ||
for check in SHARE["rules"].get(repository.data.category, []): | ||
checks.append(check(repository)) | ||
|
||
await asyncio.gather( | ||
*[ | ||
check._async_run_check() | ||
for check in checks or [] | ||
if hacs.system.action or not check.action_only | ||
] | ||
) | ||
|
||
total = len([x for x in checks if hacs.system.action or not x.action_only]) | ||
failed = len([x for x in checks if x.failed]) | ||
|
||
if failed != 0: | ||
repository.logger.error("%s %s/%s checks failed", repository, failed, total) | ||
if hacs.system.action: | ||
exit(1) | ||
else: | ||
repository.logger.debug("%s All (%s) checks passed", repository, total) | ||
"""Initialize validation.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,58 @@ | ||
"""Base class for validation.""" | ||
from __future__ import annotations | ||
|
||
from time import monotonic | ||
from typing import TYPE_CHECKING | ||
|
||
from ..share import SHARE | ||
from ..exceptions import HacsException | ||
|
||
if TYPE_CHECKING: | ||
from ..repositories.base import HacsRepository | ||
|
||
|
||
class ValidationException(Exception): | ||
pass | ||
class ValidationException(HacsException): | ||
"""Raise when there is a validation issue.""" | ||
|
||
|
||
class ValidationBase: | ||
action_only = False | ||
"""Base class for validation.""" | ||
|
||
action_only: bool = False | ||
category: str = "common" | ||
|
||
def __init__(self, repository: HacsRepository) -> None: | ||
self.hacs = repository.hacs | ||
self.repository = repository | ||
self.failed = False | ||
self.logger = repository.logger | ||
|
||
def __init_subclass__(cls, category="common", **kwargs) -> None: | ||
"""Initialize a subclass, register if possible.""" | ||
super().__init_subclass__(**kwargs) | ||
if SHARE["rules"].get(category) is None: | ||
SHARE["rules"][category] = [] | ||
if cls not in SHARE["rules"][category]: | ||
SHARE["rules"][category].append(cls) | ||
|
||
async def _async_run_check(self): | ||
"""DO NOT OVERRIDE THIS IN SUBCLASSES!""" | ||
if self.hacs.system.action: | ||
self.logger.info(f"Running check '{self.__class__.__name__}'") | ||
|
||
@property | ||
def slug(self) -> str: | ||
"""Return the check slug.""" | ||
return self.__class__.__module__.rsplit(".", maxsplit=1)[-1] | ||
|
||
async def execute_validation(self, *_, **__) -> None: | ||
"""Execute the task defined in subclass.""" | ||
self.hacs.log.debug("Validation<%s> Starting validation", self.slug) | ||
|
||
start_time = monotonic() | ||
self.failed = False | ||
|
||
try: | ||
await self.hacs.hass.async_add_executor_job(self.check) | ||
await self.async_check() | ||
if task := getattr(self, "validate", None): | ||
await self.hacs.hass.async_add_executor_job(task) | ||
elif task := getattr(self, "async_validate", None): | ||
await task() # pylint: disable=not-callable | ||
except ValidationException as exception: | ||
self.failed = True | ||
self.logger.error(exception) | ||
self.hacs.log.error("Validation<%s> failed: %s", self.slug, exception) | ||
|
||
def check(self): | ||
pass | ||
|
||
async def async_check(self): | ||
pass | ||
else: | ||
self.hacs.log.debug( | ||
"Validation<%s> took %.3f seconds to complete", self.slug, monotonic() - start_time | ||
) | ||
|
||
|
||
class ActionValidationBase(ValidationBase): | ||
"""Base class for action validation.""" | ||
|
||
action_only = True |
Empty file.
This file was deleted.
Oops, something went wrong.
7 changes: 0 additions & 7 deletions
7
custom_components/hacs/validate/common/repository_description.py
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
from __future__ import annotations | ||
|
||
from ..enums import RepositoryFile | ||
from ..repositories.base import HacsRepository | ||
from .base import ActionValidationBase, ValidationException | ||
|
||
|
||
async def async_setup_validator(repository: HacsRepository) -> Validator: | ||
"""Set up this validator.""" | ||
return Validator(repository=repository) | ||
|
||
|
||
class Validator(ActionValidationBase): | ||
def validate(self): | ||
if RepositoryFile.HACS_JSON not in [x.filename for x in self.repository.tree]: | ||
raise ValidationException(f"The repository has no '{RepositoryFile.HACS_JSON}' file") |
Empty file.
10 changes: 0 additions & 10 deletions
10
custom_components/hacs/validate/integration/integration_manifest.py
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
from __future__ import annotations | ||
|
||
from ..enums import RepositoryFile | ||
from ..repositories.base import HacsRepository | ||
from .base import ActionValidationBase, ValidationException | ||
|
||
|
||
async def async_setup_validator(repository: HacsRepository) -> Validator: | ||
"""Set up this validator.""" | ||
return Validator(repository=repository) | ||
|
||
|
||
class Validator(ActionValidationBase): | ||
category = "integration" | ||
|
||
def validate(self): | ||
if RepositoryFile.MAINIFEST_JSON not in [x.filename for x in self.repository.tree]: | ||
raise ValidationException( | ||
f"The repository has no '{RepositoryFile.MAINIFEST_JSON}' file" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
"""Hacs validation manager.""" | ||
from __future__ import annotations | ||
|
||
import asyncio | ||
from importlib import import_module | ||
from pathlib import Path | ||
from typing import TYPE_CHECKING | ||
|
||
from homeassistant.core import HomeAssistant | ||
|
||
from custom_components.hacs.repositories.base import HacsRepository | ||
|
||
from .base import ValidationBase | ||
|
||
if TYPE_CHECKING: | ||
from ..base import HacsBase | ||
|
||
|
||
class ValidationManager: | ||
"""Hacs validation manager.""" | ||
|
||
def __init__(self, hacs: HacsBase, hass: HomeAssistant) -> None: | ||
"""Initialize the setup manager class.""" | ||
self.hacs = hacs | ||
self.hass = hass | ||
self._validatiors: dict[str, ValidationBase] = {} | ||
|
||
@property | ||
def validatiors(self) -> dict[str, ValidationBase]: | ||
"""Return all list of all tasks.""" | ||
return list(self._validatiors.values()) | ||
|
||
async def async_load(self, repository: HacsRepository) -> None: | ||
"""Load all tasks.""" | ||
self._validatiors = {} | ||
validator_files = Path(__file__).parent | ||
validator_modules = ( | ||
module.stem | ||
for module in validator_files.glob("*.py") | ||
if module.name not in ("base.py", "__init__.py", "manager.py") | ||
) | ||
|
||
async def _load_module(module: str): | ||
task_module = import_module(f"{__package__}.{module}") | ||
if task := await task_module.async_setup_validator(repository=repository): | ||
self._validatiors[task.slug] = task | ||
|
||
await asyncio.gather(*[_load_module(task) for task in validator_modules]) | ||
self.hacs.log.info("Loaded %s validators", len(self.validatiors)) | ||
|
||
async def async_run_repository_checks(self, repository: HacsRepository) -> None: | ||
"""Run all validators for a repository.""" | ||
if not self.hacs.system.running: | ||
return | ||
|
||
await self.async_load(repository) | ||
|
||
await asyncio.gather( | ||
*[ | ||
validator.execute_validation() | ||
for validator in self.validatiors or [] | ||
if (self.hacs.system.action or not validator.action_only) | ||
and ( | ||
validator.category == "common" or validator.category == repository.data.category | ||
) | ||
] | ||
) | ||
|
||
total = len([x for x in self.validatiors if self.hacs.system.action or not x.action_only]) | ||
failed = len([x for x in self.validatiors if x.failed]) | ||
|
||
if failed != 0: | ||
repository.logger.error("%s %s/%s checks failed", repository, failed, total) | ||
if self.hacs.system.action: | ||
exit(1) | ||
else: | ||
repository.logger.debug("%s All (%s) checks passed", repository, total) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from __future__ import annotations | ||
|
||
from ..repositories.base import HacsRepository | ||
from .base import ActionValidationBase, ValidationException | ||
|
||
|
||
async def async_setup_validator(repository: HacsRepository) -> Validator: | ||
"""Set up this validator.""" | ||
return Validator(repository=repository) | ||
|
||
|
||
class Validator(ActionValidationBase): | ||
def validate(self): | ||
if not self.repository.data.description: | ||
raise ValidationException("The repository has no description") |
14 changes: 11 additions & 3 deletions
14
...ate/common/repository_information_file.py → ...s/validate/repository_information_file.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.