Fix doubled platformdirs paths; pick newest dotnet runtime
release / gui-build (linux, bash) (push) Successful in 54s
release / gui-build (windows, cmd) (push) Successful in 1m24s
release / gui-build (macos, bash) (push) Successful in 1m47s

appauthor=False/opinion=False avoids mediahive\mediahive\Cache style
paths on Windows. fetch_dotnet now probes all known dotnet locations and
selects the highest Microsoft.NETCore.App major, requiring the version
vpk's TFM targets (10) — PATH on the Windows CI runner resolves to a
runtime-only .NET 8 while scoop holds the SDK 10.
This commit is contained in:
2026-09-23 00:49:38 +00:00
parent e514829737
commit 3142bc2cb2
3 changed files with 40 additions and 24 deletions
+6 -4
View File
@@ -22,13 +22,15 @@ class Config(msgspec.Struct, omit_defaults=True):
def config_dir() -> Path:
# Local (non-roaming) on Windows: config is machine-specific state,
# not something to sync across a domain profile.
return user_config_path("mediahive", roaming=False)
# appauthor=False: avoid the doubled %LOCALAPPDATA%\mediahive\mediahive.
# roaming=False: config is machine-specific state, not something to sync
# across a domain profile.
return user_config_path("mediahive", appauthor=False, roaming=False)
def log_dir() -> Path:
return user_log_path("mediahive", opinion=False)
# opinion=False: no extra Logs/ subdir; mediahive.log sits beside config.
return user_log_path("mediahive", appauthor=False, opinion=False)
def config_path() -> Path:
+3 -1
View File
@@ -24,7 +24,9 @@ _icon_mac = _pkg / "assets" / "mediahive.icns"
# scripts/guibuild.py); fall back to the legacy build/ffmpeg location.
from platformdirs import user_cache_path
_tools_dir = user_cache_path("mediahive-build") / "ffmpeg"
_tools_dir = (
user_cache_path("mediahive-build", appauthor=False, opinion=False) / "ffmpeg"
)
if not _tools_dir.exists():
_tools_dir = Path(SPECPATH).parent / "build" / "ffmpeg"
_tool_names = ["ffmpeg.exe"] if sys.platform == "win32" else ["ffmpeg"]
+31 -19
View File
@@ -47,7 +47,7 @@ _ASSETS_DIR = _REPO_ROOT / "mediahive" / "assets"
def _build_cache_dir() -> Path:
"""Return the persistent cross-build cache dir for downloaded tools (CI wipes build/)."""
return user_cache_path("mediahive-build")
return user_cache_path("mediahive-build", appauthor=False, opinion=False)
_FFMPEG_STAGING = _build_cache_dir() / "ffmpeg"
@@ -202,12 +202,16 @@ def ensure_macos_icon() -> Path:
return icon_icns
_VPK_TFM = "net10.0"
_VPK_REQUIRED_DOTNET_MAJOR = int(re.fullmatch(r"net(\d+)\.0", _VPK_TFM).group(1))
def fetch_vpk() -> Path:
"""Download the Velopack CLI package into the persistent build cache.
Returns the path to vpk.dll, runnable with `dotnet vpk.dll ...`.
"""
vpk_dll = _VPK_STAGING / "tools" / "net10.0" / "any" / "vpk.dll"
vpk_dll = _VPK_STAGING / "tools" / _VPK_TFM / "any" / "vpk.dll"
if vpk_dll.exists():
print(f"vpk already staged at {_VPK_STAGING}, skipping download.")
return vpk_dll
@@ -226,35 +230,35 @@ def fetch_vpk() -> Path:
return vpk_dll
def _dotnet_has_runtime(exe: Path) -> bool:
"""Check that `exe` runs and has Microsoft.NETCore.App >= 8."""
def _dotnet_runtime_major(exe: Path) -> int | None:
"""Return the highest installed Microsoft.NETCore.App major version, or None."""
try:
result = subprocess.run(
[str(exe), "--list-runtimes"], capture_output=True, text=True, timeout=30
)
except (OSError, subprocess.TimeoutExpired):
return False
return None
if result.returncode != 0:
return False
return None
majors = []
for line in result.stdout.splitlines():
parts = line.split()
if len(parts) >= 2 and parts[0] == "Microsoft.NETCore.App":
try:
if int(parts[1].split(".")[0]) >= 8:
return True
majors.append(int(parts[1].split(".")[0]))
except ValueError:
continue
return False
return max(majors, default=None)
def fetch_dotnet() -> str:
"""Resolve a dotnet host with a modern (>= 8) runtime from the system.
"""Resolve a system dotnet host able to run vpk (needs .NET >= 10).
The .NET SDK is a build prerequisite installed on the build machine —
downloading a runtime per build is slow and flaky. A plain `dotnet` may
resolve to a runtime-only installation, and long-running services (CI
runners) may carry a stale environment without DOTNET_ROOT or scoop
paths — so probe known locations explicitly.
downloading a runtime per build is slow and flaky. Several dotnet
installations may coexist (PATH may resolve to a runtime-only .NET 8
while scoop holds the SDK 10), so probe known locations and pick the
newest runtime rather than the first that runs.
"""
exe_name = "dotnet.exe" if sys.platform == "win32" else "dotnet"
candidates: list[Path] = []
@@ -281,14 +285,22 @@ def fetch_dotnet() -> str:
Path.home() / ".dotnet" / exe_name,
]
best: tuple[int, Path] | None = None
for exe in candidates:
if exe.exists() and _dotnet_has_runtime(exe):
print(f"Using dotnet at {exe}")
return str(exe)
if not exe.exists():
continue
major = _dotnet_runtime_major(exe)
if major is not None and (best is None or major > best[0]):
best = (major, exe)
if best is not None and best[0] >= _VPK_REQUIRED_DOTNET_MAJOR:
print(f"Using dotnet at {best[1]} (.NET {best[0]})")
return str(best[1])
found = f"newest found is .NET {best[0]} at {best[1]}" if best else "none found"
raise RuntimeError(
"No dotnet with Microsoft.NETCore.App >= 8 found. "
"Install the .NET SDK on this build machine "
f"vpk requires Microsoft.NETCore.App >= {_VPK_REQUIRED_DOTNET_MAJOR} ({found}). "
"Install the current .NET SDK on this build machine "
"(Windows: `scoop install dotnet-sdk`; macOS: `brew install dotnet-sdk`; "
"Linux: distro `dotnet-sdk` package or the dotnet-install script)."
)