feat: UI polish - dark default, version drift vs default release, humanized enums, failed-jobs layout, processing button hierarchy, PT-BR titles/CTAs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,11 +94,53 @@ def compute_agent_health(snapshot, now: datetime | None = None, *, supplemental=
|
||||
return "ONLINE", "recent heartbeat without critical technical failure"
|
||||
|
||||
|
||||
def compute_version_drift(agent_version: str | None) -> str:
|
||||
_VERSION_RE = re.compile(r"^v?(\d+(?:\.\d+)*)(?:[-_.]?(rc|beta|alpha|b|a)[-_.]?(\d*))?$", re.IGNORECASE)
|
||||
_PRE_RANK = {"alpha": -3, "a": -3, "beta": -2, "b": -2, "rc": -1}
|
||||
|
||||
|
||||
def parse_version(value: str | None):
|
||||
"""Comparable tuple for '1.1.7', '1.0.0rc1', ... (a pre-release sorts below its final); None if unparsable."""
|
||||
match = _VERSION_RE.match((value or "").strip())
|
||||
if not match:
|
||||
return None
|
||||
parts = [int(x) for x in match.group(1).split(".")]
|
||||
while len(parts) < 4:
|
||||
parts.append(0)
|
||||
pre = (_PRE_RANK[match.group(2).lower()], int(match.group(3) or 0)) if match.group(2) else (0, 0)
|
||||
return (*parts, *pre)
|
||||
|
||||
|
||||
def reference_agent_version(session) -> str:
|
||||
"""The version an Agent should be running: the highest PUBLISHED Windows Agent release (REVOKED/DRAFT are
|
||||
never a target). Falls back to the configured server version when no release is published."""
|
||||
from sqlalchemy import select
|
||||
|
||||
from .models import AgentRelease
|
||||
|
||||
best, best_key = None, None
|
||||
for version in session.scalars(select(AgentRelease.version).where(AgentRelease.status == "PUBLISHED", AgentRelease.platform == "windows")):
|
||||
key = parse_version(version)
|
||||
if key is not None and (best_key is None or key > best_key):
|
||||
best, best_key = version, key
|
||||
return best or settings.agent_server_version
|
||||
|
||||
|
||||
def compute_version_drift(agent_version: str | None, reference: str | None = None) -> str:
|
||||
"""CURRENT when the Agent is at (or ahead of) the reference release, OUTDATED when behind it."""
|
||||
if not agent_version:
|
||||
return "UNKNOWN"
|
||||
return "CURRENT" if agent_version == settings.agent_server_version else "OUTDATED"
|
||||
reference = reference or settings.agent_server_version
|
||||
have, want = parse_version(agent_version), parse_version(reference)
|
||||
if have is None or want is None:
|
||||
return "CURRENT" if agent_version == reference else "OUTDATED"
|
||||
return "CURRENT" if have >= want else "OUTDATED"
|
||||
|
||||
|
||||
def machine_fingerprint(parts: list[str]) -> str:
|
||||
return hashlib.sha256("|".join(parts).encode()).hexdigest()
|
||||
def outdated_versions(session, reference: str) -> list[str]:
|
||||
"""Distinct Agent versions in use that are behind the reference (used to keep SQL filters exact)."""
|
||||
from sqlalchemy import select
|
||||
|
||||
from .models import Agent
|
||||
|
||||
used = [v for v in session.scalars(select(Agent.agent_version).where(Agent.agent_version.is_not(None)).distinct())]
|
||||
return [v for v in used if compute_version_drift(v, reference) == "OUTDATED"]
|
||||
|
||||
Reference in New Issue
Block a user