Initial release: full Arma 3 mod management toolchain
Pipeline: parse HTML presets, compare modlists, download from Caddy file server, create junctions/symlinks to Arma 3 Server directory. Includes update/sync flows, missing-mod reporting, OS compat layer, shared config, dep checker, comprehensive test suite (71 tests). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
108
arma_modlist_tools/compat.py
Normal file
108
arma_modlist_tools/compat.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
arma_modlist_tools.compat
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
OS detection and cross-platform utilities shared by all CLI scripts.
|
||||
|
||||
Supported platforms:
|
||||
- Windows / Windows Server (sys.platform == "win32")
|
||||
- Ubuntu / Ubuntu Server (sys.platform == "linux")
|
||||
|
||||
Typical usage::
|
||||
|
||||
from arma_modlist_tools.compat import is_windows, get_os_label, fix_console_encoding
|
||||
|
||||
fix_console_encoding() # call once at script start on Windows
|
||||
print(get_os_label()) # "Windows Server", "Ubuntu", etc.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import platform
|
||||
import sys
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Platform detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def is_windows() -> bool:
|
||||
"""Return ``True`` on Windows and Windows Server."""
|
||||
return sys.platform == "win32"
|
||||
|
||||
|
||||
def is_linux() -> bool:
|
||||
"""Return ``True`` on Linux (Ubuntu, Ubuntu Server, and other distros)."""
|
||||
return sys.platform == "linux"
|
||||
|
||||
|
||||
def get_os_label() -> str:
|
||||
"""
|
||||
Return a human-readable OS label.
|
||||
|
||||
Possible values: ``"Windows"``, ``"Windows Server"``, ``"Ubuntu"``,
|
||||
``"Ubuntu Server"``, ``"Linux"``, ``"Unknown"``.
|
||||
"""
|
||||
if is_windows():
|
||||
ver = platform.version()
|
||||
# Windows Server versions contain "Server" in the version string
|
||||
# e.g. "10.0.17763 ... Windows Server 2019 ..."
|
||||
if "Server" in platform.version() or "Server" in platform.uname().version:
|
||||
return "Windows Server"
|
||||
return "Windows"
|
||||
|
||||
if is_linux():
|
||||
# Read /etc/os-release for distro name
|
||||
os_release = _read_os_release()
|
||||
name = os_release.get("NAME", "").lower()
|
||||
|
||||
if "ubuntu" in name:
|
||||
# Distinguish desktop vs server: server images have no display server
|
||||
if _is_headless():
|
||||
return "Ubuntu Server"
|
||||
return "Ubuntu"
|
||||
|
||||
return "Linux"
|
||||
|
||||
return "Unknown"
|
||||
|
||||
|
||||
def _read_os_release() -> dict[str, str]:
|
||||
"""Parse /etc/os-release into a dict (Linux only)."""
|
||||
result: dict[str, str] = {}
|
||||
try:
|
||||
with open("/etc/os-release", encoding="utf-8") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if "=" in line and not line.startswith("#"):
|
||||
k, _, v = line.partition("=")
|
||||
result[k] = v.strip('"')
|
||||
except OSError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
def _is_headless() -> bool:
|
||||
"""Return True if no graphical display server is detected (headless/server)."""
|
||||
import os
|
||||
# Check for common display environment variables
|
||||
return not (os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Console encoding
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def fix_console_encoding() -> None:
|
||||
"""
|
||||
Force UTF-8 output on Windows terminals that default to cp1252.
|
||||
|
||||
Call once at the top of any CLI script that uses Unicode characters
|
||||
(checkmarks, arrows, etc.). No-op on Linux.
|
||||
"""
|
||||
if not is_windows():
|
||||
return
|
||||
if sys.stdout.encoding and sys.stdout.encoding.lower() == "utf-8":
|
||||
return
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
||||
Reference in New Issue
Block a user