Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fff1b2692 | |||
| c8356c7603 | |||
| 0c49a50fc9 | |||
| fa48841645 |
@@ -11,6 +11,13 @@ Sections:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 0.7.1 – 2026-01-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- GitHub version provider now reliably fetches the latest 20 releases/tags using authenticated API requests.
|
||||||
|
- Repositories that were previously fetched in a degraded state (only `latest` and `branch`) are now automatically refreshed on repository view.
|
||||||
|
- Cached version lists with incomplete data are no longer reused and are re-fetched from the provider.
|
||||||
|
|
||||||
## [0.7.0] - 2026-01-20
|
## [0.7.0] - 2026-01-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -808,13 +808,21 @@ class BCSCore:
|
|||||||
_LOGGER.debug("BCS ensure_repo_details failed for %s", repo_id, exc_info=True)
|
_LOGGER.debug("BCS ensure_repo_details failed for %s", repo_id, exc_info=True)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
async def list_repo_versions(self, repo_id: str, *, limit: int = 20) -> list[dict[str, Any]]:
|
async def list_repo_versions(
|
||||||
|
self,
|
||||||
|
repo_id: str,
|
||||||
|
*,
|
||||||
|
limit: int = 20,
|
||||||
|
force_refresh: bool = False,
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
repo = self.get_repo(repo_id)
|
repo = self.get_repo(repo_id)
|
||||||
if not repo:
|
if not repo:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# Prefer cached version lists to avoid hammering provider APIs (notably GitHub unauthenticated
|
# Prefer cached version lists to avoid hammering provider APIs (notably GitHub unauthenticated
|
||||||
# rate limits). We refresh on-demand when the user opens the selector.
|
# rate limits). However, if the cached list is clearly a degraded fallback (e.g. only
|
||||||
|
# "Latest" + "Branch"), we treat it as stale and retry immediately when the user requests
|
||||||
|
# versions again.
|
||||||
cached = None
|
cached = None
|
||||||
cached_ts = 0
|
cached_ts = 0
|
||||||
async with self._repo_cache_lock:
|
async with self._repo_cache_lock:
|
||||||
@@ -823,8 +831,17 @@ class BCSCore:
|
|||||||
cached_ts = int(cached.get("versions_ts", 0) or 0)
|
cached_ts = int(cached.get("versions_ts", 0) or 0)
|
||||||
|
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
if isinstance(cached, dict) and cached.get("versions") and (now - cached_ts) < VERSIONS_CACHE_TTL_SECONDS:
|
cached_versions = list(cached.get("versions") or []) if isinstance(cached, dict) else []
|
||||||
return list(cached.get("versions") or [])
|
cache_fresh = (now - cached_ts) < VERSIONS_CACHE_TTL_SECONDS
|
||||||
|
|
||||||
|
# Cache hit if it's fresh and not degraded, unless the caller explicitly wants a refresh.
|
||||||
|
if (
|
||||||
|
not force_refresh
|
||||||
|
and cached_versions
|
||||||
|
and cache_fresh
|
||||||
|
and len(cached_versions) > 2
|
||||||
|
):
|
||||||
|
return cached_versions
|
||||||
|
|
||||||
try:
|
try:
|
||||||
versions = await fetch_repo_versions(
|
versions = await fetch_repo_versions(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "bahmcloud_store",
|
"domain": "bahmcloud_store",
|
||||||
"name": "Bahmcloud Store",
|
"name": "Bahmcloud Store",
|
||||||
"version": "0.7.0",
|
"version": "0.7.1",
|
||||||
"documentation": "https://git.bahmcloud.de/bahmcloud/bahmcloud_store",
|
"documentation": "https://git.bahmcloud.de/bahmcloud/bahmcloud_store",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"platforms": ["update"],
|
"platforms": ["update"],
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ async def fetch_repo_versions(
|
|||||||
- source: release|tag|branch
|
- source: release|tag|branch
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- Uses public endpoints (no tokens) for public repositories.
|
- Uses provider APIs; for GitHub we include the configured token (if any) to avoid unauthenticated rate limits.
|
||||||
- We prefer releases first (if available), then tags.
|
- We prefer releases first (if available), then tags.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -575,11 +575,13 @@ async def fetch_repo_versions(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if prov == "github":
|
if prov == "github":
|
||||||
# Releases
|
# Releases (prefer these over tags)
|
||||||
gh_headers = {"Accept": "application/vnd.github+json", "User-Agent": UA}
|
# Use the configured GitHub token (if any) to avoid unauthenticated rate limits.
|
||||||
|
gh_headers = _github_headers(github_token)
|
||||||
|
per_page = max(1, min(int(limit), 100))
|
||||||
data, _ = await _safe_json(
|
data, _ = await _safe_json(
|
||||||
session,
|
session,
|
||||||
f"https://api.github.com/repos/{owner}/{repo}/releases?per_page={int(limit)}",
|
f"https://api.github.com/repos/{owner}/{repo}/releases?per_page={per_page}",
|
||||||
headers=gh_headers,
|
headers=gh_headers,
|
||||||
)
|
)
|
||||||
if isinstance(data, list):
|
if isinstance(data, list):
|
||||||
@@ -597,7 +599,7 @@ async def fetch_repo_versions(
|
|||||||
# Tags
|
# Tags
|
||||||
data, _ = await _safe_json(
|
data, _ = await _safe_json(
|
||||||
session,
|
session,
|
||||||
f"https://api.github.com/repos/{owner}/{repo}/tags?per_page={int(limit)}",
|
f"https://api.github.com/repos/{owner}/{repo}/tags?per_page={per_page}",
|
||||||
headers=gh_headers,
|
headers=gh_headers,
|
||||||
)
|
)
|
||||||
if isinstance(data, list):
|
if isinstance(data, list):
|
||||||
|
|||||||
Reference in New Issue
Block a user