mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-20 23:56:30 +00:00
Addon repositories (#2071)
* stash * Add test * Use executor * Make remove a coroutine * Change logging and return
This commit is contained in:
parent
9274a0fa17
commit
1af90721cc
@ -142,6 +142,10 @@ class APISupervisor(CoreSysAttributes):
|
|||||||
if ATTR_ADDONS_REPOSITORIES in body:
|
if ATTR_ADDONS_REPOSITORIES in body:
|
||||||
new = set(body[ATTR_ADDONS_REPOSITORIES])
|
new = set(body[ATTR_ADDONS_REPOSITORIES])
|
||||||
await asyncio.shield(self.sys_store.update_repositories(new))
|
await asyncio.shield(self.sys_store.update_repositories(new))
|
||||||
|
if sorted(body[ATTR_ADDONS_REPOSITORIES]) != sorted(
|
||||||
|
self.sys_config.addons_repositories
|
||||||
|
):
|
||||||
|
raise APIError("Not a valid add-on repository")
|
||||||
|
|
||||||
self.sys_updater.save_data()
|
self.sys_updater.save_data()
|
||||||
self.sys_config.save_data()
|
self.sys_config.save_data()
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
"""Add-on Store handler."""
|
"""Add-on Store handler."""
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from supervisor.store.validate import SCHEMA_REPOSITORY_CONFIG
|
||||||
|
from supervisor.utils.json import read_json_file
|
||||||
|
|
||||||
from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL
|
from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL
|
||||||
from ..coresys import CoreSys, CoreSysAttributes
|
from ..coresys import CoreSys, CoreSysAttributes
|
||||||
|
from ..exceptions import JsonFileError
|
||||||
from .addon import AddonStore
|
from .addon import AddonStore
|
||||||
from .data import StoreData
|
from .data import StoreData
|
||||||
from .repository import Repository
|
from .repository import Repository
|
||||||
@ -60,19 +67,31 @@ class StoreManager(CoreSysAttributes):
|
|||||||
if not await repository.load():
|
if not await repository.load():
|
||||||
_LOGGER.error("Can't load from repository %s", url)
|
_LOGGER.error("Can't load from repository %s", url)
|
||||||
return
|
return
|
||||||
self.repositories[url] = repository
|
|
||||||
|
|
||||||
# don't add built-in repository to config
|
# don't add built-in repository to config
|
||||||
if url not in BUILTIN_REPOSITORIES:
|
if url not in BUILTIN_REPOSITORIES:
|
||||||
|
# Verify that it is a add-on repository
|
||||||
|
repository_file = Path(repository.git.path, "repository.json")
|
||||||
|
try:
|
||||||
|
await self.sys_run_in_executor(
|
||||||
|
SCHEMA_REPOSITORY_CONFIG, read_json_file(repository_file)
|
||||||
|
)
|
||||||
|
except (JsonFileError, vol.Invalid) as err:
|
||||||
|
_LOGGER.error("%s is not a valid add-on repository. %s", url, err)
|
||||||
|
await repository.remove()
|
||||||
|
return
|
||||||
|
|
||||||
self.sys_config.add_addon_repository(url)
|
self.sys_config.add_addon_repository(url)
|
||||||
|
|
||||||
|
self.repositories[url] = repository
|
||||||
|
|
||||||
tasks = [_add_repository(url) for url in new_rep - old_rep]
|
tasks = [_add_repository(url) for url in new_rep - old_rep]
|
||||||
if tasks:
|
if tasks:
|
||||||
await asyncio.wait(tasks)
|
await asyncio.wait(tasks)
|
||||||
|
|
||||||
# del new repository
|
# del new repository
|
||||||
for url in old_rep - new_rep - BUILTIN_REPOSITORIES:
|
for url in old_rep - new_rep - BUILTIN_REPOSITORIES:
|
||||||
self.repositories.pop(url).remove()
|
await self.repositories.pop(url).remove()
|
||||||
self.sys_config.drop_addon_repository(url)
|
self.sys_config.drop_addon_repository(url)
|
||||||
|
|
||||||
# update data
|
# update data
|
||||||
|
@ -94,7 +94,7 @@ class GitRepo(CoreSysAttributes):
|
|||||||
async def pull(self):
|
async def pull(self):
|
||||||
"""Pull Git add-on repo."""
|
"""Pull Git add-on repo."""
|
||||||
if self.lock.locked():
|
if self.lock.locked():
|
||||||
_LOGGER.warning("It is already a task in progress")
|
_LOGGER.warning("There is already a task in progress")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async with self.lock:
|
async with self.lock:
|
||||||
@ -128,8 +128,12 @@ class GitRepo(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _remove(self):
|
async def _remove(self):
|
||||||
"""Remove a repository."""
|
"""Remove a repository."""
|
||||||
|
if self.lock.locked():
|
||||||
|
_LOGGER.warning("There is already a task in progress")
|
||||||
|
return
|
||||||
|
|
||||||
if not self.path.is_dir():
|
if not self.path.is_dir():
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -137,7 +141,9 @@ class GitRepo(CoreSysAttributes):
|
|||||||
"""Log error."""
|
"""Log error."""
|
||||||
_LOGGER.warning("Can't remove %s", path)
|
_LOGGER.warning("Can't remove %s", path)
|
||||||
|
|
||||||
shutil.rmtree(self.path, onerror=log_err)
|
await self.sys_run_in_executor(
|
||||||
|
ft.partial(shutil.rmtree, self.path, onerror=log_err)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GitRepoHassIO(GitRepo):
|
class GitRepoHassIO(GitRepo):
|
||||||
@ -157,7 +163,7 @@ class GitRepoCustom(GitRepo):
|
|||||||
|
|
||||||
super().__init__(coresys, path, url)
|
super().__init__(coresys, path, url)
|
||||||
|
|
||||||
def remove(self):
|
async def remove(self):
|
||||||
"""Remove a custom repository."""
|
"""Remove a custom repository."""
|
||||||
_LOGGER.info("Remove custom add-on repository %s", self.url)
|
_LOGGER.info("Remove custom add-on repository %s", self.url)
|
||||||
self._remove()
|
await self._remove()
|
||||||
|
@ -67,9 +67,9 @@ class Repository(CoreSysAttributes):
|
|||||||
return await self.git.pull()
|
return await self.git.pull()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def remove(self):
|
async def remove(self):
|
||||||
"""Remove add-on repository."""
|
"""Remove add-on repository."""
|
||||||
if self.slug in (REPOSITORY_CORE, REPOSITORY_LOCAL):
|
if self.slug in (REPOSITORY_CORE, REPOSITORY_LOCAL):
|
||||||
raise APIError("Can't remove built-in repositories!")
|
raise APIError("Can't remove built-in repositories!")
|
||||||
|
|
||||||
self.git.remove()
|
await self.git.remove()
|
||||||
|
@ -141,3 +141,12 @@ async def network_interface(dbus):
|
|||||||
await interface.connect(dbus, "/org/freedesktop/NetworkManager/ActiveConnection/1")
|
await interface.connect(dbus, "/org/freedesktop/NetworkManager/ActiveConnection/1")
|
||||||
await interface.connection.update_information()
|
await interface.connection.update_information()
|
||||||
yield interface
|
yield interface
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def store_manager(coresys: CoreSys):
|
||||||
|
"""Fixture for the store manager."""
|
||||||
|
sm_obj = coresys.store
|
||||||
|
sm_obj.repositories = set(coresys.config.addons_repositories)
|
||||||
|
with patch("supervisor.store.data.StoreData.update", return_value=MagicMock()):
|
||||||
|
yield sm_obj
|
||||||
|
29
tests/store/test_custom_repository.py
Normal file
29
tests/store/test_custom_repository.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
"""Test add custom repository."""
|
||||||
|
import json
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_add_valid_repository(coresys, store_manager):
|
||||||
|
"""Test add custom repository."""
|
||||||
|
current = coresys.config.addons_repositories
|
||||||
|
with patch("supervisor.store.repository.Repository.load", return_value=True), patch(
|
||||||
|
"pathlib.Path.read_text",
|
||||||
|
return_value=json.dumps({"name": "Awesome repository"}),
|
||||||
|
):
|
||||||
|
await store_manager.update_repositories(current + ["http://example.com"])
|
||||||
|
assert "http://example.com" in coresys.config.addons_repositories
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_add_invalid_repository(coresys, store_manager):
|
||||||
|
"""Test add custom repository."""
|
||||||
|
current = coresys.config.addons_repositories
|
||||||
|
with patch("supervisor.store.repository.Repository.load", return_value=True), patch(
|
||||||
|
"pathlib.Path.read_text",
|
||||||
|
return_value="",
|
||||||
|
):
|
||||||
|
await store_manager.update_repositories(current + ["http://example.com"])
|
||||||
|
assert "http://example.com" not in coresys.config.addons_repositories
|
Loading…
x
Reference in New Issue
Block a user