diff options
| author | tranch <tranch.xiao@gmail.com> | 2026-05-29 16:36:08 +0800 |
|---|---|---|
| committer | tranch <tranch.xiao@gmail.com> | 2026-05-29 16:36:08 +0800 |
| commit | e0dc8000344879f6ffc275301d338c55d375295b (patch) | |
| tree | 370cfc874c6ce627e0838033f806a28c81c542a3 | |
| parent | 0c01c3ac51eade1f9cde484ebec157c6602e7258 (diff) | |
| download | accelerator-installer-main.tar.gz accelerator-installer-main.zip | |
Use semantic version ordering when selecting the latest store version so multi-digit patch versions are not sorted behind older releases.
| -rw-r--r-- | decky_client.py | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/decky_client.py b/decky_client.py index a770797..e31661b 100644 --- a/decky_client.py +++ b/decky_client.py @@ -3,10 +3,11 @@ import asyncio import base64 import json import os +import re import struct import sys import urllib.request -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple # Decky Loader Message Types CALL = 0 @@ -28,12 +29,50 @@ STORE_TYPE_NAMES = { 2: "custom" } +SEMVER_RE = re.compile( + r"^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$" +) + def log(*args: Any) -> None: """Print formatted logs to stderr.""" print("[DeckyInstaller]", *args, file=sys.stderr, flush=True) +def _prerelease_sort_key(prerelease: Optional[str]) -> Tuple[Tuple[int, int, str], ...]: + if not prerelease: + return () + + parts = [] + for part in prerelease.split("."): + if part.isdigit(): + parts.append((0, int(part), "")) + else: + parts.append((1, 0, part)) + return tuple(parts) + + +def version_sort_key(version: Dict[str, Any]) -> Tuple[Any, ...]: + """Sort store versions by semantic version, falling back to creation time.""" + name = str(version.get("name") or "") + match = SEMVER_RE.match(name) + if match: + major, minor, patch, prerelease = match.groups() + is_stable = 1 if prerelease is None else 0 + return ( + 1, + int(major), + int(minor or 0), + int(patch or 0), + is_stable, + _prerelease_sort_key(prerelease), + str(version.get("created") or ""), + name, + ) + + return (0, str(version.get("created") or ""), name) + + class DeckyClient: """ A robust client for Decky Loader using asyncio streams. @@ -221,7 +260,7 @@ async def run_installer(target_id: int, store_url: str) -> None: if not versions: raise RuntimeError("store entry missing versions") - latest = sorted(versions, key=lambda v: (v.get("name") or ""))[-1] + latest = max(versions, key=version_sort_key) version_name = latest.get("name") or "dev" artifact_url = latest.get("artifact") or "" hash_ = latest.get("hash") or "" |
