Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove share #2382

Merged
merged 3 commits into from
Dec 26, 2021
Merged
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
3 changes: 3 additions & 0 deletions custom_components/hacs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from homeassistant.loader import async_get_integration
import voluptuous as vol

from custom_components.hacs.validate.manager import ValidationManager

from .base import HacsBase
from .const import DOMAIN, PLATFORMS, STARTUP
from .enums import ConfigurationType, HacsDisabledReason, HacsStage, LovelaceMode
Expand Down Expand Up @@ -82,6 +84,7 @@ async def async_initialize_integration(
hacs.system.running = True
hacs.session = async_create_clientsession(hass)
hacs.tasks = HacsTaskManager(hacs=hacs, hass=hass)
hacs.validation = ValidationManager(hacs=hacs, hass=hass)

hacs.core.lovelace_mode = LovelaceMode.YAML
try:
Expand Down
2 changes: 2 additions & 0 deletions custom_components/hacs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
from .repositories.base import HacsRepository
from .tasks.manager import HacsTaskManager
from .utils.data import HacsData
from .validate.manager import ValidationManager


@dataclass
Expand Down Expand Up @@ -332,6 +333,7 @@ class HacsBase:
status = HacsStatus()
system = HacsSystem()
tasks: HacsTaskManager | None = None
validation: ValidationManager | None = None
version: str | None = None

@property
Expand Down
3 changes: 1 addition & 2 deletions custom_components/hacs/repositories/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
version_left_higher_then_right,
version_to_download,
)
from ..validate import async_run_repository_checks

if TYPE_CHECKING:
from ..base import HacsBase
Expand Down Expand Up @@ -706,7 +705,7 @@ async def async_registration(self, ref=None) -> None:

async def async_post_registration(self):
"""Run post registration steps."""
await async_run_repository_checks(self.hacs, self)
await self.hacs.validation.async_run_repository_checks(self)

async def async_pre_install(self) -> None:
"""Run pre install steps."""
Expand Down
6 changes: 0 additions & 6 deletions custom_components/hacs/share.py

This file was deleted.

2 changes: 0 additions & 2 deletions custom_components/hacs/tasks/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
class HacsTask(LogMixin):
"""Hacs task base."""

hass: HomeAssistant

events: list[str] | None = None
schedule: timedelta | None = None
stages: list[HacsStage] | None = None
Expand Down
59 changes: 1 addition & 58 deletions custom_components/hacs/validate/__init__.py
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."""
59 changes: 33 additions & 26 deletions custom_components/hacs/validate/base.py
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.
8 changes: 0 additions & 8 deletions custom_components/hacs/validate/common/hacs_manifest.py

This file was deleted.

This file was deleted.

7 changes: 0 additions & 7 deletions custom_components/hacs/validate/common/repository_topics.py

This file was deleted.

16 changes: 16 additions & 0 deletions custom_components/hacs/validate/hacs_manifest.py
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.

This file was deleted.

20 changes: 20 additions & 0 deletions custom_components/hacs/validate/integration_manifest.py
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"
)
77 changes: 77 additions & 0 deletions custom_components/hacs/validate/manager.py
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)
15 changes: 15 additions & 0 deletions custom_components/hacs/validate/repository_description.py
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")
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
from ..base import ActionValidationBase, ValidationException
from __future__ import annotations

from ..repositories.base import HacsRepository
from .base import ActionValidationBase, ValidationException

class RepositoryInformationFile(ActionValidationBase):
async def async_check(self):

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


class Validator(ActionValidationBase):
async def async_validate(self):
filenames = [x.filename.lower() for x in self.repository.tree]
if self.repository.data.render_readme and "readme" in filenames:
pass
Expand Down
Loading