diff --git a/custom_components/bahmcloud_store/providers.py b/custom_components/bahmcloud_store/providers.py index 7f95f10..ca2007f 100644 --- a/custom_components/bahmcloud_store/providers.py +++ b/custom_components/bahmcloud_store/providers.py @@ -6,12 +6,11 @@ import xml.etree.ElementTree as ET from dataclasses import dataclass from urllib.parse import quote_plus, urlparse +from packaging.version import InvalidVersion, Version + from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession -# Home Assistant includes "packaging" -from packaging.version import InvalidVersion, Version - _LOGGER = logging.getLogger(__name__) UA = "BahmcloudStore (Home Assistant)" @@ -112,13 +111,9 @@ def _semver_key(tag: str) -> Version | None: return None -def _pick_highest_semver(candidates: list[str]) -> str | None: - """Pick highest semantic version from a list of tag strings. - - If no semver parseable tags exist, return None. - """ +def _pick_highest_semver(tags: list[str]) -> str | None: parsed: list[tuple[Version, str]] = [] - for t in candidates: + for t in tags: if not isinstance(t, str): continue ts = t.strip() @@ -130,7 +125,6 @@ def _pick_highest_semver(candidates: list[str]) -> str | None: if not parsed: return None - parsed.sort(key=lambda x: x[0], reverse=True) return parsed[0][1] @@ -199,17 +193,16 @@ async def _github_latest_version_api(hass: HomeAssistant, owner: str, repo: str) session = async_get_clientsession(hass) headers = {"Accept": "application/vnd.github+json", "User-Agent": UA} - # 1) Preferred: GitHub latest release endpoint (already good) data, status = await _safe_json(session, f"https://api.github.com/repos/{owner}/{repo}/releases/latest", headers=headers) if isinstance(data, dict) and data.get("tag_name"): return str(data["tag_name"]), "release" - # 2) If no releases: fetch multiple tags and select highest semver + # No releases -> pick highest semver from many tags (instead of per_page=1) if status == 404: - tags_data, _ = await _safe_json(session, f"https://api.github.com/repos/{owner}/{repo}/tags?per_page=100", headers=headers) + data, _ = await _safe_json(session, f"https://api.github.com/repos/{owner}/{repo}/tags?per_page=100", headers=headers) tags: list[str] = [] - if isinstance(tags_data, list): - for t in tags_data: + if isinstance(data, list): + for t in data: if isinstance(t, dict) and t.get("name"): tags.append(str(t["name"])) @@ -217,7 +210,7 @@ async def _github_latest_version_api(hass: HomeAssistant, owner: str, repo: str) if best: return best, "tag" - # fallback: keep old behavior (first tag, if any) + # fallback: keep old behavior (first tag) if tags: return tags[0], "tag" @@ -225,7 +218,6 @@ async def _github_latest_version_api(hass: HomeAssistant, owner: str, repo: str) async def _github_latest_version(hass: HomeAssistant, owner: str, repo: str) -> tuple[str | None, str | None]: - # Redirect is very robust if releases exist tag, src = await _github_latest_version_redirect(hass, owner, repo) if tag: return tag, src @@ -240,26 +232,25 @@ async def _github_latest_version(hass: HomeAssistant, owner: str, repo: str) -> async def _gitea_latest_version(hass: HomeAssistant, base: str, owner: str, repo: str) -> tuple[str | None, str | None]: session = async_get_clientsession(hass) - # Fetch multiple releases, pick highest semver by tag_name - rels, _ = await _safe_json(session, f"{base}/api/v1/repos/{owner}/{repo}/releases?limit=50") - release_tags: list[str] = [] - if isinstance(rels, list): - for r in rels: + # releases: fetch multiple, pick highest semver (instead of limit=1) + data, _ = await _safe_json(session, f"{base}/api/v1/repos/{owner}/{repo}/releases?limit=50") + rel_tags: list[str] = [] + if isinstance(data, list): + for r in data: if isinstance(r, dict) and r.get("tag_name"): - release_tags.append(str(r["tag_name"])) + rel_tags.append(str(r["tag_name"])) - best_rel = _pick_highest_semver(release_tags) + best_rel = _pick_highest_semver(rel_tags) if best_rel: return best_rel, "release" - if release_tags: - # fallback: first release tag - return release_tags[0], "release" + if rel_tags: + return rel_tags[0], "release" - # No releases: fetch multiple tags, pick highest semver - tags_data, _ = await _safe_json(session, f"{base}/api/v1/repos/{owner}/{repo}/tags?limit=50") + # tags: fetch multiple, pick highest semver (instead of limit=1) + data, _ = await _safe_json(session, f"{base}/api/v1/repos/{owner}/{repo}/tags?limit=50") tags: list[str] = [] - if isinstance(tags_data, list): - for t in tags_data: + if isinstance(data, list): + for t in data: if isinstance(t, dict) and t.get("name"): tags.append(str(t["name"])) @@ -280,25 +271,25 @@ async def _gitlab_latest_version( project = quote_plus(f"{owner}/{repo}") - # Fetch multiple releases, pick highest semver by tag_name - rels, _ = await _safe_json(session, f"{base}/api/v4/projects/{project}/releases?per_page=50", headers=headers) - release_tags: list[str] = [] - if isinstance(rels, list): - for r in rels: + # releases: fetch multiple, pick highest semver (instead of per_page=1) + data, _ = await _safe_json(session, f"{base}/api/v4/projects/{project}/releases?per_page=50", headers=headers) + rel_tags: list[str] = [] + if isinstance(data, list): + for r in data: if isinstance(r, dict) and r.get("tag_name"): - release_tags.append(str(r["tag_name"])) + rel_tags.append(str(r["tag_name"])) - best_rel = _pick_highest_semver(release_tags) + best_rel = _pick_highest_semver(rel_tags) if best_rel: return best_rel, "release" - if release_tags: - return release_tags[0], "release" + if rel_tags: + return rel_tags[0], "release" - # No releases: fetch multiple tags, pick highest semver - tags_data, _ = await _safe_json(session, f"{base}/api/v4/projects/{project}/repository/tags?per_page=50", headers=headers) + # tags: fetch multiple, pick highest semver (instead of per_page=1) + data, _ = await _safe_json(session, f"{base}/api/v4/projects/{project}/repository/tags?per_page=50", headers=headers) tags: list[str] = [] - if isinstance(tags_data, list): - for t in tags_data: + if isinstance(data, list): + for t in data: if isinstance(t, dict) and t.get("name"): tags.append(str(t["name"])) @@ -308,7 +299,7 @@ async def _gitlab_latest_version( if tags: return tags[0], "tag" - # last fallback: atom (often newest by date, not semver) + # atom fallback atom, status = await _safe_text(session, f"{base}/{owner}/{repo}/-/tags?format=atom", headers=headers) if status == 200 and atom: try: