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
70 changes: 69 additions & 1 deletion custom_components/hacs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,59 @@ def set_repository_id(self, repository: HacsRepository, repo_id: str):
repository.data.id = repo_id
self.register(repository)

def reconcile_repository_id(
self,
repository: HacsRepository,
repo_id: str,
matching_repositories: list[HacsRepository],
) -> HacsRepository:
"""Reconcile a repository with its current ID."""
registered_repository = self._repositories_by_id.get(repo_id)
if (
registered_repository
and registered_repository.data.full_name_lower != repository.data.full_name_lower
):
raise ValueError(
f"The repo id {repo_id} is already set to "
f"{registered_repository.data.full_name_lower}"
)

was_default = any(
self.is_default(str(candidate.data.id)) for candidate in matching_repositories
)

installed_repositories = [
candidate for candidate in matching_repositories if candidate.data.installed
]
if registered_repository and registered_repository.data.installed:
repository = registered_repository
elif installed_repositories:
repository = min(
installed_repositories,
key=lambda candidate: int(candidate.data.id),
)
elif registered_repository:
repository = registered_repository
else:
repository = min(
matching_repositories,
key=lambda candidate: int(candidate.data.id),
)

for candidate in matching_repositories:
if candidate is not repository:
self.unregister(candidate)

if str(repository.data.id) != repo_id:
self.unregister(repository)
repository.data.id = repo_id
self.register(repository)

self._repositories_by_full_name[repository.data.full_name_lower] = repository
if was_default:
self.mark_default(repository)
return repository

def is_default(self, repository_id: str | None = None) -> bool:
"""Check if a repository is default."""
if not repository_id:
Expand Down Expand Up @@ -857,6 +910,12 @@ async def async_get_category_repositories_experimental(self, category: str) -> N
self.log.error("Could not update %s - %s", category, exception)
return

repositories_by_full_name: dict[str, list[HacsRepository]] = {}
for repository in self.repositories.list_all:
repositories_by_full_name.setdefault(repository.data.full_name_lower, []).append(
repository
)

await self.data.register_unknown_repositories(category_data, category)

for repo_id, repo_data in category_data.items():
Expand All @@ -868,7 +927,16 @@ async def async_get_category_repositories_experimental(self, category: str) -> N
if repo_name in self.common.archived_repositories:
continue
if repository := self.repositories.get_by_full_name(repo_name):
self.repositories.set_repository_id(repository, repo_id)
matching_repositories = repositories_by_full_name.get(
repository.data.full_name_lower,
[repository],
)
if str(repository.data.id) != repo_id or len(matching_repositories) > 1:
repository = self.repositories.reconcile_repository_id(
repository,
repo_id,
matching_repositories,
)
self.repositories.mark_default(repository)
if repository.data.last_fetched is None or (
repository.data.last_fetched.timestamp() < repo_data["last_fetched"]
Expand Down
27 changes: 16 additions & 11 deletions custom_components/hacs/utils/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,32 +237,37 @@ async def register_unknown_repositories(
):
"""Registry any unknown repositories."""
for repo_idx, (entry, repo_data) in enumerate(repositories.items()):
# async_register_repository is awaited in a loop
# since its unlikely to ever suspend at startup
if repo_idx % 100 == 0:
# yield to avoid blocking the event loop
await asyncio.sleep(0)

repository_category = repo_data.get("category", category)
if (
entry == "0"
or repo_data.get("category", category) is None
or repository_category is None
or self.hacs.repositories.is_registered(repository_id=entry)
):
continue

if category is not None and (repository_full_name := repo_data.get("full_name")):
if renamed := self.hacs.common.renamed_repositories.get(repository_full_name):
repository_full_name = renamed
if self.hacs.repositories.get_by_full_name(repository_full_name):
continue

await self.hacs.async_register_repository(
repository_full_name=repo_data["full_name"],
category=repo_data.get("category", category),
category=repository_category,
check=False,
repository_id=entry,
)
if repo_idx % 100 == 0:
# yield to avoid blocking the event loop
await asyncio.sleep(0)

@callback
def async_restore_repository(self, entry: str, repository_data: dict[str, Any]):
"""Restore repository."""
repository: HacsRepository | None = None
if full_name := repository_data.get("full_name"):
repository: HacsRepository | None = self.hacs.repositories.get_by_id(entry)
if not repository and (full_name := repository_data.get("full_name")):
repository = self.hacs.repositories.get_by_full_name(full_name)
if not repository:
repository = self.hacs.repositories.get_by_id(entry)
if not repository:
return

Expand Down
Loading