12
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user