mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-11 19:26:28 +00:00
Refactor addon git repo (#5987)
* Refactor Repository into setup with inheritance * Remove subclasses of GitRepo
This commit is contained in:
parent
3e20a0937d
commit
abc44946bb
@ -42,13 +42,8 @@ class FixupStoreExecuteReset(FixupBase):
|
|||||||
_LOGGER.warning("Can't find store %s for fixup", reference)
|
_LOGGER.warning("Can't find store %s for fixup", reference)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Local add-ons are not a git repo, can't remove and re-pull
|
|
||||||
try:
|
try:
|
||||||
if repository.git:
|
await repository.reset()
|
||||||
await repository.git.reset()
|
|
||||||
|
|
||||||
# Load data again
|
|
||||||
await repository.load()
|
|
||||||
except StoreError:
|
except StoreError:
|
||||||
raise ResolutionFixupError() from None
|
raise ResolutionFixupError() from None
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class StoreManager(CoreSysAttributes, FileConfiguration):
|
|||||||
if url == URL_HASSIO_ADDONS:
|
if url == URL_HASSIO_ADDONS:
|
||||||
url = StoreType.CORE
|
url = StoreType.CORE
|
||||||
|
|
||||||
repository = Repository(self.coresys, url)
|
repository = Repository.create(self.coresys, url)
|
||||||
|
|
||||||
if repository.slug in self.repositories:
|
if repository.slug in self.repositories:
|
||||||
raise StoreError(f"Can't add {url}, already in the store", _LOGGER.error)
|
raise StoreError(f"Can't add {url}, already in the store", _LOGGER.error)
|
||||||
@ -183,7 +183,7 @@ class StoreManager(CoreSysAttributes, FileConfiguration):
|
|||||||
raise err
|
raise err
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if not await self.sys_run_in_executor(repository.validate):
|
if not await repository.validate():
|
||||||
if add_with_errors:
|
if add_with_errors:
|
||||||
_LOGGER.error("%s is not a valid add-on repository", url)
|
_LOGGER.error("%s is not a valid add-on repository", url)
|
||||||
self.sys_resolution.create_issue(
|
self.sys_resolution.create_issue(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Init file for Supervisor add-on Git."""
|
"""Init file for Supervisor add-on Git."""
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import errno
|
import errno
|
||||||
import functools as ft
|
import functools as ft
|
||||||
@ -16,17 +15,14 @@ from ..exceptions import StoreGitCloneError, StoreGitError, StoreJobError
|
|||||||
from ..jobs.decorator import Job, JobCondition
|
from ..jobs.decorator import Job, JobCondition
|
||||||
from ..resolution.const import ContextType, IssueType, SuggestionType, UnhealthyReason
|
from ..resolution.const import ContextType, IssueType, SuggestionType, UnhealthyReason
|
||||||
from ..utils import remove_folder
|
from ..utils import remove_folder
|
||||||
from .utils import get_hash_from_repository
|
from .validate import RE_REPOSITORY
|
||||||
from .validate import RE_REPOSITORY, BuiltinRepository
|
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class GitRepo(CoreSysAttributes, ABC):
|
class GitRepo(CoreSysAttributes):
|
||||||
"""Manage Add-on Git repository."""
|
"""Manage Add-on Git repository."""
|
||||||
|
|
||||||
builtin: bool
|
|
||||||
|
|
||||||
def __init__(self, coresys: CoreSys, path: Path, url: str):
|
def __init__(self, coresys: CoreSys, path: Path, url: str):
|
||||||
"""Initialize Git base wrapper."""
|
"""Initialize Git base wrapper."""
|
||||||
self.coresys: CoreSys = coresys
|
self.coresys: CoreSys = coresys
|
||||||
@ -239,38 +235,8 @@ class GitRepo(CoreSysAttributes, ABC):
|
|||||||
)
|
)
|
||||||
raise StoreGitError() from err
|
raise StoreGitError() from err
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
async def remove(self) -> None:
|
async def remove(self) -> None:
|
||||||
"""Remove a repository."""
|
"""Remove a repository."""
|
||||||
|
|
||||||
|
|
||||||
class GitRepoBuiltin(GitRepo):
|
|
||||||
"""Built-in add-ons repository."""
|
|
||||||
|
|
||||||
builtin: bool = True
|
|
||||||
|
|
||||||
def __init__(self, coresys: CoreSys, repository: BuiltinRepository):
|
|
||||||
"""Initialize Git Supervisor add-on repository."""
|
|
||||||
super().__init__(coresys, repository.get_path(coresys), repository.url)
|
|
||||||
|
|
||||||
async def remove(self) -> None:
|
|
||||||
"""Raise. Cannot remove built-in repositories."""
|
|
||||||
raise RuntimeError("Cannot remove built-in repositories!")
|
|
||||||
|
|
||||||
|
|
||||||
class GitRepoCustom(GitRepo):
|
|
||||||
"""Custom add-ons repository."""
|
|
||||||
|
|
||||||
builtin: bool = False
|
|
||||||
|
|
||||||
def __init__(self, coresys, url):
|
|
||||||
"""Initialize custom Git Supervisor addo-n repository."""
|
|
||||||
path = Path(coresys.config.path_addons_git, get_hash_from_repository(url))
|
|
||||||
|
|
||||||
super().__init__(coresys, path, url)
|
|
||||||
|
|
||||||
async def remove(self) -> None:
|
|
||||||
"""Remove a custom repository."""
|
|
||||||
if self.lock.locked():
|
if self.lock.locked():
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Cannot remove add-on repository %s, there is already a task in progress",
|
"Cannot remove add-on repository %s, there is already a task in progress",
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
"""Represent a Supervisor repository."""
|
"""Represent a Supervisor repository."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -12,7 +15,7 @@ from ..coresys import CoreSys, CoreSysAttributes
|
|||||||
from ..exceptions import ConfigurationFileError, StoreError
|
from ..exceptions import ConfigurationFileError, StoreError
|
||||||
from ..utils.common import read_json_or_yaml_file
|
from ..utils.common import read_json_or_yaml_file
|
||||||
from .const import StoreType
|
from .const import StoreType
|
||||||
from .git import GitRepo, GitRepoBuiltin, GitRepoCustom
|
from .git import GitRepo
|
||||||
from .utils import get_hash_from_repository
|
from .utils import get_hash_from_repository
|
||||||
from .validate import SCHEMA_REPOSITORY_CONFIG, BuiltinRepository
|
from .validate import SCHEMA_REPOSITORY_CONFIG, BuiltinRepository
|
||||||
|
|
||||||
@ -20,28 +23,24 @@ _LOGGER: logging.Logger = logging.getLogger(__name__)
|
|||||||
UNKNOWN = "unknown"
|
UNKNOWN = "unknown"
|
||||||
|
|
||||||
|
|
||||||
class Repository(CoreSysAttributes):
|
class Repository(CoreSysAttributes, ABC):
|
||||||
"""Add-on store repository in Supervisor."""
|
"""Add-on store repository in Supervisor."""
|
||||||
|
|
||||||
def __init__(self, coresys: CoreSys, repository: str):
|
def __init__(self, coresys: CoreSys, repository: str):
|
||||||
"""Initialize add-on store repository object."""
|
"""Initialize add-on store repository object."""
|
||||||
|
self._slug: str
|
||||||
|
self._type: StoreType
|
||||||
self.coresys: CoreSys = coresys
|
self.coresys: CoreSys = coresys
|
||||||
self.git: GitRepo | None = None
|
|
||||||
|
|
||||||
self.source: str = repository
|
self.source: str = repository
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def create(coresys: CoreSys, repository: str) -> Repository:
|
||||||
|
"""Create a repository instance."""
|
||||||
if repository == StoreType.LOCAL:
|
if repository == StoreType.LOCAL:
|
||||||
self._slug = repository
|
return RepositoryLocal(coresys)
|
||||||
self._type = StoreType.LOCAL
|
if repository in BuiltinRepository:
|
||||||
self._latest_mtime: float | None = None
|
return RepositoryGitBuiltin(coresys, BuiltinRepository(repository))
|
||||||
elif repository in BuiltinRepository:
|
return RepositoryCustom(coresys, repository)
|
||||||
builtin = BuiltinRepository(repository)
|
|
||||||
self.git = GitRepoBuiltin(coresys, builtin)
|
|
||||||
self._slug = builtin.id
|
|
||||||
self._type = builtin.type
|
|
||||||
else:
|
|
||||||
self.git = GitRepoCustom(coresys, repository)
|
|
||||||
self._slug = get_hash_from_repository(repository)
|
|
||||||
self._type = StoreType.GIT
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Return internal representation."""
|
"""Return internal representation."""
|
||||||
@ -77,17 +76,75 @@ class Repository(CoreSysAttributes):
|
|||||||
"""Return url of repository."""
|
"""Return url of repository."""
|
||||||
return self.data.get(ATTR_MAINTAINER, UNKNOWN)
|
return self.data.get(ATTR_MAINTAINER, UNKNOWN)
|
||||||
|
|
||||||
def validate(self) -> bool:
|
@abstractmethod
|
||||||
"""Check if store is valid.
|
async def validate(self) -> bool:
|
||||||
|
"""Check if store is valid."""
|
||||||
|
|
||||||
Must be run in executor.
|
@abstractmethod
|
||||||
|
async def load(self) -> None:
|
||||||
|
"""Load addon repository."""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def update(self) -> bool:
|
||||||
|
"""Update add-on repository.
|
||||||
|
|
||||||
|
Returns True if the repository was updated.
|
||||||
"""
|
"""
|
||||||
if not self.git or self.type == StoreType.CORE:
|
|
||||||
|
@abstractmethod
|
||||||
|
async def remove(self) -> None:
|
||||||
|
"""Remove add-on repository."""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
async def reset(self) -> None:
|
||||||
|
"""Reset add-on repository to fix corruption issue with files."""
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryBuiltin(Repository, ABC):
|
||||||
|
"""A built-in add-on repository."""
|
||||||
|
|
||||||
|
def __init__(self, coresys: CoreSys, builtin: BuiltinRepository) -> None:
|
||||||
|
"""Initialize object."""
|
||||||
|
super().__init__(coresys, builtin.value)
|
||||||
|
self._builtin = builtin
|
||||||
|
self._slug = builtin.id
|
||||||
|
self._type = builtin.type
|
||||||
|
|
||||||
|
async def validate(self) -> bool:
|
||||||
|
"""Assume built-in repositories are always valid."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
async def remove(self) -> None:
|
||||||
|
"""Raise. Not supported for built-in repositories."""
|
||||||
|
raise StoreError("Can't remove built-in repositories!", _LOGGER.error)
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryGit(Repository, ABC):
|
||||||
|
"""A git based add-on repository."""
|
||||||
|
|
||||||
|
_git: GitRepo
|
||||||
|
|
||||||
|
async def load(self) -> None:
|
||||||
|
"""Load addon repository."""
|
||||||
|
await self._git.load()
|
||||||
|
|
||||||
|
async def update(self) -> bool:
|
||||||
|
"""Update add-on repository.
|
||||||
|
|
||||||
|
Returns True if the repository was updated.
|
||||||
|
"""
|
||||||
|
if not await self.validate():
|
||||||
|
return False
|
||||||
|
|
||||||
|
return await self._git.pull()
|
||||||
|
|
||||||
|
async def validate(self) -> bool:
|
||||||
|
"""Check if store is valid."""
|
||||||
|
|
||||||
|
def validate_file() -> bool:
|
||||||
# If exists?
|
# If exists?
|
||||||
for filetype in FILE_SUFFIX_CONFIGURATION:
|
for filetype in FILE_SUFFIX_CONFIGURATION:
|
||||||
repository_file = Path(self.git.path / f"repository{filetype}")
|
repository_file = Path(self._git.path / f"repository{filetype}")
|
||||||
if repository_file.exists():
|
if repository_file.exists():
|
||||||
break
|
break
|
||||||
|
|
||||||
@ -103,26 +160,33 @@ class Repository(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
return await self.sys_run_in_executor(validate_file)
|
||||||
|
|
||||||
|
async def reset(self) -> None:
|
||||||
|
"""Reset add-on repository to fix corruption issue with files."""
|
||||||
|
await self._git.reset()
|
||||||
|
await self.load()
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryLocal(RepositoryBuiltin):
|
||||||
|
"""A local add-on repository."""
|
||||||
|
|
||||||
|
def __init__(self, coresys: CoreSys) -> None:
|
||||||
|
"""Initialize object."""
|
||||||
|
super().__init__(coresys, BuiltinRepository.LOCAL)
|
||||||
|
self._latest_mtime: float | None = None
|
||||||
|
|
||||||
async def load(self) -> None:
|
async def load(self) -> None:
|
||||||
"""Load addon repository."""
|
"""Load addon repository."""
|
||||||
if not self.git:
|
|
||||||
self._latest_mtime, _ = await self.sys_run_in_executor(
|
self._latest_mtime, _ = await self.sys_run_in_executor(
|
||||||
get_latest_mtime, self.sys_config.path_addons_local
|
get_latest_mtime, self.sys_config.path_addons_local
|
||||||
)
|
)
|
||||||
return
|
|
||||||
await self.git.load()
|
|
||||||
|
|
||||||
async def update(self) -> bool:
|
async def update(self) -> bool:
|
||||||
"""Update add-on repository.
|
"""Update add-on repository.
|
||||||
|
|
||||||
Returns True if the repository was updated.
|
Returns True if the repository was updated.
|
||||||
"""
|
"""
|
||||||
if not await self.sys_run_in_executor(self.validate):
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self.git:
|
|
||||||
return await self.git.pull()
|
|
||||||
|
|
||||||
# Check local modifications
|
# Check local modifications
|
||||||
latest_mtime, modified_path = await self.sys_run_in_executor(
|
latest_mtime, modified_path = await self.sys_run_in_executor(
|
||||||
get_latest_mtime, self.sys_config.path_addons_local
|
get_latest_mtime, self.sys_config.path_addons_local
|
||||||
@ -138,9 +202,32 @@ class Repository(CoreSysAttributes):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def reset(self) -> None:
|
||||||
|
"""Raise. Not supported for local repository."""
|
||||||
|
raise StoreError(
|
||||||
|
"Can't reset local repository as it is not git based!", _LOGGER.error
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryGitBuiltin(RepositoryBuiltin, RepositoryGit):
|
||||||
|
"""A built-in add-on repository based on git."""
|
||||||
|
|
||||||
|
def __init__(self, coresys: CoreSys, builtin: BuiltinRepository) -> None:
|
||||||
|
"""Initialize object."""
|
||||||
|
super().__init__(coresys, builtin)
|
||||||
|
self._git = GitRepo(coresys, builtin.get_path(coresys), builtin.url)
|
||||||
|
|
||||||
|
|
||||||
|
class RepositoryCustom(RepositoryGit):
|
||||||
|
"""A custom add-on repository."""
|
||||||
|
|
||||||
|
def __init__(self, coresys: CoreSys, url: str) -> None:
|
||||||
|
"""Initialize object."""
|
||||||
|
super().__init__(coresys, url)
|
||||||
|
self._slug = get_hash_from_repository(url)
|
||||||
|
self._type = StoreType.GIT
|
||||||
|
self._git = GitRepo(coresys, coresys.config.path_addons_git / self._slug, url)
|
||||||
|
|
||||||
async def remove(self) -> None:
|
async def remove(self) -> None:
|
||||||
"""Remove add-on repository."""
|
"""Remove add-on repository."""
|
||||||
if not self.git or self.git.builtin:
|
await self._git.remove()
|
||||||
raise StoreError("Can't remove built-in repositories!", _LOGGER.error)
|
|
||||||
|
|
||||||
await self.git.remove()
|
|
||||||
|
@ -820,7 +820,7 @@ async def test_paths_cache(coresys: CoreSys, install_addon_ssh: Addon):
|
|||||||
|
|
||||||
with (
|
with (
|
||||||
patch("supervisor.addons.addon.Path.exists", return_value=True),
|
patch("supervisor.addons.addon.Path.exists", return_value=True),
|
||||||
patch("supervisor.store.repository.Repository.update", return_value=True),
|
patch("supervisor.store.repository.RepositoryLocal.update", return_value=True),
|
||||||
):
|
):
|
||||||
await coresys.store.reload(coresys.store.get("local"))
|
await coresys.store.reload(coresys.store.get("local"))
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ from supervisor.plugins.dns import PluginDns
|
|||||||
from supervisor.resolution.const import ContextType, IssueType, SuggestionType
|
from supervisor.resolution.const import ContextType, IssueType, SuggestionType
|
||||||
from supervisor.resolution.data import Issue, Suggestion
|
from supervisor.resolution.data import Issue, Suggestion
|
||||||
from supervisor.store.addon import AddonStore
|
from supervisor.store.addon import AddonStore
|
||||||
from supervisor.store.repository import Repository
|
from supervisor.store.repository import RepositoryLocal
|
||||||
from supervisor.utils import check_exception_chain
|
from supervisor.utils import check_exception_chain
|
||||||
from supervisor.utils.common import write_json_file
|
from supervisor.utils.common import write_json_file
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ async def test_store_data_changes_during_update(
|
|||||||
update_task = coresys.create_task(simulate_update())
|
update_task = coresys.create_task(simulate_update())
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
|
|
||||||
with patch.object(Repository, "update", return_value=True):
|
with patch.object(RepositoryLocal, "update", return_value=True):
|
||||||
await coresys.store.reload()
|
await coresys.store.reload()
|
||||||
|
|
||||||
assert "image" not in coresys.store.data.addons["local_ssh"]
|
assert "image" not in coresys.store.data.addons["local_ssh"]
|
||||||
|
@ -97,8 +97,8 @@ async def test_api_store_add_repository(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test POST /store/repositories REST API."""
|
"""Test POST /store/repositories REST API."""
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch("supervisor.store.repository.Repository.validate", return_value=True),
|
patch("supervisor.store.repository.RepositoryGit.validate", return_value=True),
|
||||||
):
|
):
|
||||||
response = await api_client.post(
|
response = await api_client.post(
|
||||||
"/store/repositories", json={"repository": REPO_URL}
|
"/store/repositories", json={"repository": REPO_URL}
|
||||||
|
@ -42,8 +42,8 @@ async def test_api_supervisor_options_add_repository(
|
|||||||
coresys.store.get_from_url(REPO_URL)
|
coresys.store.get_from_url(REPO_URL)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch("supervisor.store.repository.Repository.validate", return_value=True),
|
patch("supervisor.store.repository.RepositoryGit.validate", return_value=True),
|
||||||
):
|
):
|
||||||
response = await api_client.post(
|
response = await api_client.post(
|
||||||
"/supervisor/options", json={"addons_repositories": [REPO_URL]}
|
"/supervisor/options", json={"addons_repositories": [REPO_URL]}
|
||||||
@ -76,9 +76,9 @@ async def test_api_supervisor_options_repositories_skipped_on_error(
|
|||||||
):
|
):
|
||||||
"""Test repositories skipped on error via POST /supervisor/options REST API."""
|
"""Test repositories skipped on error via POST /supervisor/options REST API."""
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", side_effect=git_error),
|
patch("supervisor.store.repository.RepositoryGit.load", side_effect=git_error),
|
||||||
patch("supervisor.store.repository.Repository.validate", return_value=False),
|
patch("supervisor.store.repository.RepositoryGit.validate", return_value=False),
|
||||||
patch("supervisor.store.repository.Repository.remove"),
|
patch("supervisor.store.repository.RepositoryCustom.remove"),
|
||||||
):
|
):
|
||||||
response = await api_client.post(
|
response = await api_client.post(
|
||||||
"/supervisor/options", json={"addons_repositories": [REPO_URL]}
|
"/supervisor/options", json={"addons_repositories": [REPO_URL]}
|
||||||
@ -98,7 +98,7 @@ async def test_api_supervisor_options_repo_error_with_config_change(
|
|||||||
assert not coresys.config.debug
|
assert not coresys.config.debug
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"supervisor.store.repository.Repository.load", side_effect=StoreGitError()
|
"supervisor.store.repository.RepositoryGit.load", side_effect=StoreGitError()
|
||||||
):
|
):
|
||||||
response = await api_client.post(
|
response = await api_client.post(
|
||||||
"/supervisor/options",
|
"/supervisor/options",
|
||||||
|
@ -409,7 +409,7 @@ async def coresys(
|
|||||||
coresys_obj.init_websession = AsyncMock()
|
coresys_obj.init_websession = AsyncMock()
|
||||||
|
|
||||||
# Don't remove files/folders related to addons and stores
|
# Don't remove files/folders related to addons and stores
|
||||||
with patch("supervisor.store.git.GitRepoCustom.remove"):
|
with patch("supervisor.store.git.GitRepo.remove"):
|
||||||
yield coresys_obj
|
yield coresys_obj
|
||||||
|
|
||||||
await coresys_obj.dbus.unload()
|
await coresys_obj.dbus.unload()
|
||||||
@ -611,7 +611,7 @@ async def repository(coresys: CoreSys):
|
|||||||
):
|
):
|
||||||
await coresys.store.load()
|
await coresys.store.load()
|
||||||
|
|
||||||
repository_obj = Repository(
|
repository_obj = Repository.create(
|
||||||
coresys, "https://github.com/awesome-developer/awesome-repo"
|
coresys, "https://github.com/awesome-developer/awesome-repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ async def test_fixup(coresys: CoreSys):
|
|||||||
path = path or obj.path
|
path = path or obj.path
|
||||||
await coresys.run_in_executor((path / ".git").mkdir)
|
await coresys.run_in_executor((path / ".git").mkdir)
|
||||||
|
|
||||||
coresys.store.repositories["94cfad5a"] = Repository(
|
coresys.store.repositories["94cfad5a"] = Repository.create(
|
||||||
coresys, "https://github.com/home-assistant/addons-example"
|
coresys, "https://github.com/home-assistant/addons-example"
|
||||||
)
|
)
|
||||||
with (
|
with (
|
||||||
@ -97,7 +97,7 @@ async def test_fixup_clone_fail(coresys: CoreSys):
|
|||||||
assert test_repo.exists()
|
assert test_repo.exists()
|
||||||
assert corrupt_marker.exists()
|
assert corrupt_marker.exists()
|
||||||
|
|
||||||
coresys.store.repositories["94cfad5a"] = Repository(
|
coresys.store.repositories["94cfad5a"] = Repository.create(
|
||||||
coresys, "https://github.com/home-assistant/addons-example"
|
coresys, "https://github.com/home-assistant/addons-example"
|
||||||
)
|
)
|
||||||
with (
|
with (
|
||||||
@ -129,7 +129,7 @@ async def test_fixup_move_fail(coresys: CoreSys, error_num: int, unhealthy: bool
|
|||||||
|
|
||||||
add_store_reset_suggestion(coresys)
|
add_store_reset_suggestion(coresys)
|
||||||
test_repo.mkdir(parents=True)
|
test_repo.mkdir(parents=True)
|
||||||
coresys.store.repositories["94cfad5a"] = Repository(
|
coresys.store.repositories["94cfad5a"] = Repository.create(
|
||||||
coresys, "https://github.com/home-assistant/addons-example"
|
coresys, "https://github.com/home-assistant/addons-example"
|
||||||
)
|
)
|
||||||
with (
|
with (
|
||||||
|
@ -33,7 +33,7 @@ async def test_add_valid_repository(
|
|||||||
"""Test add custom repository."""
|
"""Test add custom repository."""
|
||||||
current = coresys.store.repository_urls
|
current = coresys.store.repository_urls
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch(
|
patch(
|
||||||
"supervisor.utils.common.read_yaml_file",
|
"supervisor.utils.common.read_yaml_file",
|
||||||
return_value={"name": "Awesome repository"},
|
return_value={"name": "Awesome repository"},
|
||||||
@ -54,7 +54,7 @@ async def test_add_invalid_repository(coresys: CoreSys, store_manager: StoreMana
|
|||||||
"""Test add invalid custom repository."""
|
"""Test add invalid custom repository."""
|
||||||
current = coresys.store.repository_urls
|
current = coresys.store.repository_urls
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch(
|
patch(
|
||||||
"pathlib.Path.read_text",
|
"pathlib.Path.read_text",
|
||||||
return_value="",
|
return_value="",
|
||||||
@ -64,9 +64,7 @@ async def test_add_invalid_repository(coresys: CoreSys, store_manager: StoreMana
|
|||||||
current + ["http://example.com"], add_with_errors=True
|
current + ["http://example.com"], add_with_errors=True
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not await coresys.run_in_executor(
|
assert not await store_manager.get_from_url("http://example.com").validate()
|
||||||
store_manager.get_from_url("http://example.com").validate
|
|
||||||
)
|
|
||||||
|
|
||||||
assert "http://example.com" in coresys.store.repository_urls
|
assert "http://example.com" in coresys.store.repository_urls
|
||||||
assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REMOVE
|
assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REMOVE
|
||||||
@ -79,7 +77,7 @@ async def test_error_on_invalid_repository(
|
|||||||
"""Test invalid repository not added."""
|
"""Test invalid repository not added."""
|
||||||
current = coresys.store.repository_urls
|
current = coresys.store.repository_urls
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch(
|
patch(
|
||||||
"pathlib.Path.read_text",
|
"pathlib.Path.read_text",
|
||||||
return_value="",
|
return_value="",
|
||||||
@ -103,7 +101,7 @@ async def test_add_invalid_repository_file(
|
|||||||
"""Test add invalid custom repository file."""
|
"""Test add invalid custom repository file."""
|
||||||
current = coresys.store.repository_urls
|
current = coresys.store.repository_urls
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch(
|
patch(
|
||||||
"pathlib.Path.read_text",
|
"pathlib.Path.read_text",
|
||||||
return_value=json.dumps({"name": "Awesome repository"}),
|
return_value=json.dumps({"name": "Awesome repository"}),
|
||||||
@ -114,7 +112,7 @@ async def test_add_invalid_repository_file(
|
|||||||
current + ["http://example.com"], add_with_errors=True
|
current + ["http://example.com"], add_with_errors=True
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not store_manager.get_from_url("http://example.com").validate()
|
assert not await store_manager.get_from_url("http://example.com").validate()
|
||||||
|
|
||||||
assert "http://example.com" in coresys.store.repository_urls
|
assert "http://example.com" in coresys.store.repository_urls
|
||||||
assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REMOVE
|
assert coresys.resolution.suggestions[-1].type == SuggestionType.EXECUTE_REMOVE
|
||||||
@ -135,7 +133,7 @@ async def test_add_repository_with_git_error(
|
|||||||
):
|
):
|
||||||
"""Test repo added with issue on git error."""
|
"""Test repo added with issue on git error."""
|
||||||
current = coresys.store.repository_urls
|
current = coresys.store.repository_urls
|
||||||
with patch("supervisor.store.repository.Repository.load", side_effect=git_error):
|
with patch("supervisor.store.repository.RepositoryGit.load", side_effect=git_error):
|
||||||
await store_manager.update_repositories(
|
await store_manager.update_repositories(
|
||||||
current + ["http://example.com"], add_with_errors=True
|
current + ["http://example.com"], add_with_errors=True
|
||||||
)
|
)
|
||||||
@ -163,7 +161,7 @@ async def test_error_on_repository_with_git_error(
|
|||||||
"""Test repo not added on git error."""
|
"""Test repo not added on git error."""
|
||||||
current = coresys.store.repository_urls
|
current = coresys.store.repository_urls
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", side_effect=git_error),
|
patch("supervisor.store.repository.RepositoryGit.load", side_effect=git_error),
|
||||||
pytest.raises(StoreError),
|
pytest.raises(StoreError),
|
||||||
):
|
):
|
||||||
if use_update:
|
if use_update:
|
||||||
@ -182,7 +180,7 @@ async def test_preinstall_valid_repository(
|
|||||||
coresys: CoreSys, store_manager: StoreManager
|
coresys: CoreSys, store_manager: StoreManager
|
||||||
):
|
):
|
||||||
"""Test add core repository valid."""
|
"""Test add core repository valid."""
|
||||||
with patch("supervisor.store.repository.Repository.load", return_value=None):
|
with patch("supervisor.store.repository.RepositoryGit.load", return_value=None):
|
||||||
await store_manager.update_repositories(BUILTIN_REPOSITORIES)
|
await store_manager.update_repositories(BUILTIN_REPOSITORIES)
|
||||||
|
|
||||||
def validate():
|
def validate():
|
||||||
@ -244,8 +242,8 @@ async def test_remove_used_repository(
|
|||||||
|
|
||||||
async def test_update_partial_error(coresys: CoreSys, store_manager: StoreManager):
|
async def test_update_partial_error(coresys: CoreSys, store_manager: StoreManager):
|
||||||
"""Test partial error on update does partial save and errors."""
|
"""Test partial error on update does partial save and errors."""
|
||||||
with patch("supervisor.store.repository.Repository.validate", return_value=True):
|
with patch("supervisor.store.repository.RepositoryGit.validate", return_value=True):
|
||||||
with patch("supervisor.store.repository.Repository.load", return_value=None):
|
with patch("supervisor.store.repository.RepositoryGit.load", return_value=None):
|
||||||
await store_manager.update_repositories([])
|
await store_manager.update_repositories([])
|
||||||
|
|
||||||
store_manager.data.update.assert_called_once()
|
store_manager.data.update.assert_called_once()
|
||||||
@ -256,7 +254,7 @@ async def test_update_partial_error(coresys: CoreSys, store_manager: StoreManage
|
|||||||
|
|
||||||
with (
|
with (
|
||||||
patch(
|
patch(
|
||||||
"supervisor.store.repository.Repository.load",
|
"supervisor.store.repository.RepositoryGit.load",
|
||||||
side_effect=[None, StoreGitError()],
|
side_effect=[None, StoreGitError()],
|
||||||
),
|
),
|
||||||
pytest.raises(StoreError),
|
pytest.raises(StoreError),
|
||||||
@ -275,8 +273,8 @@ async def test_error_adding_duplicate(
|
|||||||
"""Test adding a duplicate repository causes an error."""
|
"""Test adding a duplicate repository causes an error."""
|
||||||
assert repository.source in coresys.store.repository_urls
|
assert repository.source in coresys.store.repository_urls
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.validate", return_value=True),
|
patch("supervisor.store.repository.RepositoryGit.validate", return_value=True),
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
pytest.raises(StoreError),
|
pytest.raises(StoreError),
|
||||||
):
|
):
|
||||||
await store_manager.add_repository(repository.source)
|
await store_manager.add_repository(repository.source)
|
||||||
@ -290,7 +288,7 @@ async def test_add_with_update_repositories(
|
|||||||
assert "http://example.com" not in coresys.store.repository_urls
|
assert "http://example.com" not in coresys.store.repository_urls
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch(
|
patch(
|
||||||
"supervisor.utils.common.read_yaml_file",
|
"supervisor.utils.common.read_yaml_file",
|
||||||
return_value={"name": "Awesome repository"},
|
return_value={"name": "Awesome repository"},
|
||||||
@ -328,7 +326,7 @@ async def test_repositories_loaded_ignore_updates(
|
|||||||
):
|
):
|
||||||
"""Test repositories loaded whether or not supervisor needs an update."""
|
"""Test repositories loaded whether or not supervisor needs an update."""
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
patch.object(
|
patch.object(
|
||||||
type(coresys.supervisor),
|
type(coresys.supervisor),
|
||||||
"need_update",
|
"need_update",
|
||||||
|
@ -15,13 +15,6 @@ from supervisor.store.git import GitRepo
|
|||||||
REPO_URL = "https://github.com/awesome-developer/awesome-repo"
|
REPO_URL = "https://github.com/awesome-developer/awesome-repo"
|
||||||
|
|
||||||
|
|
||||||
class GitRepoTest(GitRepo):
|
|
||||||
"""Implementation of GitRepo for tests that allows direct setting of path."""
|
|
||||||
|
|
||||||
async def remove(self) -> None:
|
|
||||||
"""Not implemented."""
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="clone_from")
|
@pytest.fixture(name="clone_from")
|
||||||
async def fixture_clone_from():
|
async def fixture_clone_from():
|
||||||
"""Mock git clone_from."""
|
"""Mock git clone_from."""
|
||||||
@ -35,7 +28,7 @@ async def test_git_clone(
|
|||||||
):
|
):
|
||||||
"""Test git clone."""
|
"""Test git clone."""
|
||||||
fragment = f"#{branch}" if branch else ""
|
fragment = f"#{branch}" if branch else ""
|
||||||
repo = GitRepoTest(coresys, tmp_path, f"{REPO_URL}{fragment}")
|
repo = GitRepo(coresys, tmp_path, f"{REPO_URL}{fragment}")
|
||||||
|
|
||||||
await repo.clone.__wrapped__(repo)
|
await repo.clone.__wrapped__(repo)
|
||||||
|
|
||||||
@ -63,7 +56,7 @@ async def test_git_clone_error(
|
|||||||
coresys: CoreSys, tmp_path: Path, clone_from: AsyncMock, git_error: Exception
|
coresys: CoreSys, tmp_path: Path, clone_from: AsyncMock, git_error: Exception
|
||||||
):
|
):
|
||||||
"""Test git clone error."""
|
"""Test git clone error."""
|
||||||
repo = GitRepoTest(coresys, tmp_path, REPO_URL)
|
repo = GitRepo(coresys, tmp_path, REPO_URL)
|
||||||
|
|
||||||
clone_from.side_effect = git_error
|
clone_from.side_effect = git_error
|
||||||
with pytest.raises(StoreGitCloneError):
|
with pytest.raises(StoreGitCloneError):
|
||||||
@ -75,7 +68,7 @@ async def test_git_clone_error(
|
|||||||
async def test_git_load(coresys: CoreSys, tmp_path: Path):
|
async def test_git_load(coresys: CoreSys, tmp_path: Path):
|
||||||
"""Test git load."""
|
"""Test git load."""
|
||||||
repo_dir = tmp_path / "repo"
|
repo_dir = tmp_path / "repo"
|
||||||
repo = GitRepoTest(coresys, repo_dir, REPO_URL)
|
repo = GitRepo(coresys, repo_dir, REPO_URL)
|
||||||
repo.clone = AsyncMock()
|
repo.clone = AsyncMock()
|
||||||
|
|
||||||
# Test with non-existing git repo root directory
|
# Test with non-existing git repo root directory
|
||||||
@ -113,7 +106,7 @@ async def test_git_load(coresys: CoreSys, tmp_path: Path):
|
|||||||
async def test_git_load_error(coresys: CoreSys, tmp_path: Path, git_errors: Exception):
|
async def test_git_load_error(coresys: CoreSys, tmp_path: Path, git_errors: Exception):
|
||||||
"""Test git load error."""
|
"""Test git load error."""
|
||||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||||
repo = GitRepoTest(coresys, tmp_path, REPO_URL)
|
repo = GitRepo(coresys, tmp_path, REPO_URL)
|
||||||
|
|
||||||
# Pretend we have a repo
|
# Pretend we have a repo
|
||||||
(tmp_path / ".git").mkdir()
|
(tmp_path / ".git").mkdir()
|
||||||
|
@ -32,7 +32,8 @@ async def test_default_load(coresys: CoreSys):
|
|||||||
refresh_cache_calls.add(obj.slug)
|
refresh_cache_calls.add(obj.slug)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
|
patch("supervisor.store.repository.RepositoryLocal.load", return_value=None),
|
||||||
patch.object(type(coresys.config), "addons_repositories", return_value=[]),
|
patch.object(type(coresys.config), "addons_repositories", return_value=[]),
|
||||||
patch("pathlib.Path.exists", return_value=True),
|
patch("pathlib.Path.exists", return_value=True),
|
||||||
patch.object(AddonStore, "refresh_path_cache", new=mock_refresh_cache),
|
patch.object(AddonStore, "refresh_path_cache", new=mock_refresh_cache),
|
||||||
@ -80,9 +81,13 @@ async def test_load_with_custom_repository(coresys: CoreSys):
|
|||||||
store_manager = await StoreManager(coresys).load_config()
|
store_manager = await StoreManager(coresys).load_config()
|
||||||
|
|
||||||
with (
|
with (
|
||||||
patch("supervisor.store.repository.Repository.load", return_value=None),
|
patch("supervisor.store.repository.RepositoryGit.load", return_value=None),
|
||||||
|
patch("supervisor.store.repository.RepositoryLocal.load", return_value=None),
|
||||||
patch.object(type(coresys.config), "addons_repositories", return_value=[]),
|
patch.object(type(coresys.config), "addons_repositories", return_value=[]),
|
||||||
patch("supervisor.store.repository.Repository.validate", return_value=True),
|
patch("supervisor.store.repository.RepositoryGit.validate", return_value=True),
|
||||||
|
patch(
|
||||||
|
"supervisor.store.repository.RepositoryLocal.validate", return_value=True
|
||||||
|
),
|
||||||
patch("pathlib.Path.exists", return_value=True),
|
patch("pathlib.Path.exists", return_value=True),
|
||||||
patch.object(AddonStore, "refresh_path_cache", new=mock_refresh_cache),
|
patch.object(AddonStore, "refresh_path_cache", new=mock_refresh_cache),
|
||||||
):
|
):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user