82 lines
3.0 KiB
Python
82 lines
3.0 KiB
Python
"""Source registry and dispatch."""
|
|||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
|
||
|
|
from ..models import Item, VersionCheck
|
||
|
|
from .base import Context, error
|
||
|
|
from .blackmagic import BlackmagicSource
|
||
|
|
from .generic import HtmlSource, JsonSource, ManualSource, RssSource
|
||
|
|
from .github_releases import GithubSource
|
||
|
|
from .websearch import WebSearchSource
|
||
|
|
|
||
|
|
log = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
_SOURCES = {
|
||
|
|
s.kind: s()
|
||
|
|
for s in (
|
||
|
|
BlackmagicSource,
|
||
|
|
GithubSource,
|
||
|
|
HtmlSource,
|
||
|
|
JsonSource,
|
||
|
|
RssSource,
|
||
|
|
WebSearchSource,
|
||
|
|
ManualSource,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
KINDS = sorted(_SOURCES)
|
||
|
|
|
||
|
|
|
||
|
|
def check_item(item: Item, ctx: Context) -> VersionCheck:
|
||
|
|
"""Resolve the latest available version for one item. Never raises --
|
||
|
|
an adapter blowing up must not abort a 90-item scan."""
|
||
|
|
# An unconfirmed placeholder must not be searched on. A query built from
|
||
|
|
# a guessed vendor returns a real, correct-looking version for the WRONG
|
||
|
|
# product, and the team then acts on it. Silence is the safe answer until
|
||
|
|
# somebody names the actual model.
|
||
|
|
if not item.is_confirmed:
|
||
|
|
return VersionCheck(
|
||
|
|
item_id=item.id,
|
||
|
|
status="unconfigured",
|
||
|
|
detail=("Make/model not confirmed, so no version lookup is "
|
||
|
|
"attempted -- a guessed vendor would return the wrong "
|
||
|
|
"product's version. Set status: confirmed once you have "
|
||
|
|
"filled in the real make and model."),
|
||
|
|
)
|
||
|
|
|
||
|
|
kind = item.source_kind
|
||
|
|
|
||
|
|
# Nothing to decide, so nothing worth looking up.
|
||
|
|
if item.update_class == "do_not_touch":
|
||
|
|
return VersionCheck(
|
||
|
|
item_id=item.id,
|
||
|
|
status="unconfigured",
|
||
|
|
detail=("Marked do_not_touch, so no version is looked up -- this "
|
||
|
|
"is a licensed contractor's to update, never ours. "
|
||
|
|
"Tracked for visibility only."),
|
||
|
|
)
|
||
|
|
|
||
|
|
# Vendor-pushed items update on someone else's schedule. A paid lookup
|
||
|
|
# buys a version number nobody can act on, so skip it -- but keep free
|
||
|
|
# sources (GitHub, the Blackmagic feed), which cost nothing and do at
|
||
|
|
# least tell us a change landed.
|
||
|
|
if item.update_class == "vendor_pushed" and kind == "websearch":
|
||
|
|
return VersionCheck(
|
||
|
|
item_id=item.id,
|
||
|
|
status="unconfigured",
|
||
|
|
detail=("Marked vendor_pushed, so the paid version lookup is "
|
||
|
|
"skipped -- it updates on the supplier's schedule and "
|
||
|
|
"there is no install decision to inform. Watch for "
|
||
|
|
"breaking-change notices instead."),
|
||
|
|
)
|
||
|
|
source = _SOURCES.get(kind)
|
||
|
|
if source is None:
|
||
|
|
return error(item, f"unknown source kind {kind!r}; known: {', '.join(KINDS)}")
|
||
|
|
try:
|
||
|
|
return source.check(item, ctx)
|
||
|
|
except Exception as exc: # noqa: BLE001 - deliberate isolation boundary
|
||
|
|
log.exception("source %s failed for %s", kind, item.id)
|
||
|
|
return error(item, f"{type(exc).__name__}: {exc}")
|