Add channel-aware release tooling and updater recovery
This commit is contained in:
83
ptlinux/system_identity.py
Normal file
83
ptlinux/system_identity.py
Normal file
@@ -0,0 +1,83 @@
|
||||
"""Identify the host OS without losing the launcher's execution runtime."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
|
||||
EXECUTION_OS_RELEASE = Path("/etc/os-release")
|
||||
CONTAINER_HOST_OS_RELEASE = Path("/run/host/os-release")
|
||||
STEAM_RUNTIME_OS_ID = "steamrt"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SystemIdentity:
|
||||
"""OS releases visible to the launcher process and its container host."""
|
||||
|
||||
execution_release: dict[str, str]
|
||||
host_release: dict[str, str] | None
|
||||
|
||||
@property
|
||||
def operating_system_release(self) -> dict[str, str]:
|
||||
return self.host_release or self.execution_release
|
||||
|
||||
@property
|
||||
def running_inside_steam_runtime(self) -> bool:
|
||||
return (
|
||||
self.execution_release.get("ID", "").strip().casefold()
|
||||
== STEAM_RUNTIME_OS_ID
|
||||
)
|
||||
|
||||
|
||||
def detect_system_identity(
|
||||
*,
|
||||
execution_os_release: Path = EXECUTION_OS_RELEASE,
|
||||
host_os_release: Path = CONTAINER_HOST_OS_RELEASE,
|
||||
) -> SystemIdentity:
|
||||
"""Read the runtime OS and the standard container-host identity, if present."""
|
||||
|
||||
execution = read_os_release(execution_os_release)
|
||||
host: dict[str, str] | None = None
|
||||
if execution.get("ID", "").strip().casefold() == STEAM_RUNTIME_OS_ID:
|
||||
candidate = read_os_release(host_os_release)
|
||||
candidate_id = candidate.get("ID", "").strip().casefold()
|
||||
if candidate_id and candidate_id != STEAM_RUNTIME_OS_ID:
|
||||
host = candidate
|
||||
return SystemIdentity(execution_release=execution, host_release=host)
|
||||
|
||||
|
||||
def detect_distribution_id() -> str:
|
||||
"""Return the host distribution ID when a container exposes it."""
|
||||
|
||||
release = detect_system_identity().operating_system_release
|
||||
return release.get("ID", "").strip().casefold()
|
||||
|
||||
|
||||
def is_running_inside_steam_runtime() -> bool:
|
||||
"""Return whether the launcher process itself is inside Steam Runtime."""
|
||||
|
||||
return detect_system_identity().running_inside_steam_runtime
|
||||
|
||||
|
||||
def read_os_release(path: Path) -> dict[str, str]:
|
||||
"""Read bounded freedesktop os-release fields without invoking host tools."""
|
||||
|
||||
try:
|
||||
lines = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
except OSError:
|
||||
return {}
|
||||
result: dict[str, str] = {}
|
||||
for line in lines:
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, raw = line.split("=", 1)
|
||||
if not re.fullmatch(r"[A-Z][A-Z0-9_]*", key):
|
||||
continue
|
||||
value = raw.strip()
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
||||
value = value[1:-1]
|
||||
value = value.replace(r"\n", " ").replace(r"\"", '"').replace(r"\\", "\\")
|
||||
result[key] = value[:255]
|
||||
return result
|
||||
Reference in New Issue
Block a user