206 lines
6.2 KiB
Python
206 lines
6.2 KiB
Python
"""Check for and apply WolfDawn ``wolf`` binary updates.
|
|
|
|
Resolution order for each platform:
|
|
1. Temporary shallow clone + ``cargo`` source build (when possible on this machine)
|
|
2. Prebuilt zip from the latest GitLab release
|
|
3. Keep the existing offline bundled binary
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Sequence
|
|
|
|
from util.wolfdawn import (
|
|
WolfDawnError,
|
|
_latest_release_asset,
|
|
_platform_dir,
|
|
bundled_binary_path,
|
|
download_wolf_binary,
|
|
)
|
|
from util.wolfdawn.build_tools import (
|
|
build_and_install_platforms,
|
|
buildable_from_source,
|
|
upstream_commit,
|
|
)
|
|
|
|
_PKG_ROOT = Path(__file__).resolve().parent
|
|
VERSION_FILE = _PKG_ROOT / ".wolf_version.json"
|
|
BUNDLED_PLATFORMS: tuple[str, ...] = ("linux", "windows")
|
|
|
|
|
|
def _load_versions() -> dict:
|
|
if not VERSION_FILE.is_file():
|
|
return {}
|
|
try:
|
|
return json.loads(VERSION_FILE.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return {}
|
|
|
|
|
|
def _save_versions(data: dict) -> None:
|
|
VERSION_FILE.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
|
|
|
|
def _log(msg: str, log_fn) -> None:
|
|
if log_fn:
|
|
log_fn(msg)
|
|
else:
|
|
print(msg, flush=True)
|
|
|
|
|
|
def _platform_versions() -> dict[str, str]:
|
|
versions = _load_versions()
|
|
platforms = versions.get("platforms")
|
|
if isinstance(platforms, dict):
|
|
return {str(k): str(v) for k, v in platforms.items() if v}
|
|
tag = versions.get("tag", "")
|
|
if tag:
|
|
return {p: str(tag) for p in BUNDLED_PLATFORMS}
|
|
commit = versions.get("commit", "")
|
|
if commit:
|
|
return {p: str(commit) for p in BUNDLED_PLATFORMS}
|
|
return {}
|
|
|
|
|
|
def _local_version(platform: str) -> str:
|
|
return _platform_versions().get(platform, "")
|
|
|
|
|
|
def _record_platform_version(platform: str, version: str) -> None:
|
|
versions = _load_versions()
|
|
platforms = versions.setdefault("platforms", {})
|
|
if not isinstance(platforms, dict):
|
|
platforms = {}
|
|
versions["platforms"] = platforms
|
|
platforms[platform] = version
|
|
if version and not versions.get("commit"):
|
|
versions["commit"] = version
|
|
_save_versions(versions)
|
|
|
|
|
|
def _desired_version(platform: str) -> str:
|
|
"""Best upstream version we can install for ``platform`` on this machine."""
|
|
if buildable_from_source(platform):
|
|
return upstream_commit()
|
|
asset = _latest_release_asset(platform)
|
|
if asset:
|
|
return asset[0]
|
|
return upstream_commit()
|
|
|
|
|
|
def check_wolfdawn_update(platform: str | None = None) -> bool:
|
|
"""Return True when upstream WolfDawn differs from the bundled binary."""
|
|
platform = platform or _platform_dir()
|
|
if not bundled_binary_path(platform).is_file():
|
|
return True
|
|
try:
|
|
return _local_version(platform) != _desired_version(platform)
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _refresh_from_release(platform: str, log_fn=print) -> str | None:
|
|
asset = _latest_release_asset(platform)
|
|
if not asset:
|
|
return None
|
|
tag, _ = asset
|
|
try:
|
|
download_wolf_binary(platform=platform, log_fn=log_fn)
|
|
except WolfDawnError as exc:
|
|
_log(f"Warning: release download failed for {platform} ({exc}).", log_fn)
|
|
return None
|
|
return tag
|
|
|
|
|
|
def refresh_wolfdawn_binary(
|
|
platform: str | None = None,
|
|
*,
|
|
platforms: Sequence[str] | None = None,
|
|
log_fn=print,
|
|
) -> bool:
|
|
"""Refresh WolfDawn binaries, trying source builds first."""
|
|
targets = tuple(platforms or (platform or _platform_dir(),))
|
|
try:
|
|
upstream = upstream_commit()
|
|
except Exception as exc:
|
|
_log(f"ERROR: could not contact WolfDawn upstream ({exc})", log_fn)
|
|
return False
|
|
|
|
_log(f"Upstream WolfDawn commit: {upstream[:12]}", log_fn)
|
|
source_results = build_and_install_platforms(
|
|
tuple(p for p in targets if p in BUNDLED_PLATFORMS),
|
|
log_fn=log_fn,
|
|
)
|
|
|
|
ok = True
|
|
versions = _load_versions()
|
|
versions["commit"] = upstream
|
|
platform_versions = versions.setdefault("platforms", {})
|
|
if not isinstance(platform_versions, dict):
|
|
platform_versions = {}
|
|
versions["platforms"] = platform_versions
|
|
|
|
for plat in targets:
|
|
if source_results.get(plat):
|
|
platform_versions[plat] = upstream
|
|
_log(f"WolfDawn updated from source ({plat}, {upstream[:12]})", log_fn)
|
|
continue
|
|
|
|
tag = _refresh_from_release(plat, log_fn=log_fn)
|
|
if tag:
|
|
platform_versions[plat] = tag
|
|
_log(f"WolfDawn updated from release ({plat}, {tag})", log_fn)
|
|
continue
|
|
|
|
if bundled_binary_path(plat).is_file():
|
|
_log(f"Keeping existing offline WolfDawn binary ({plat}).", log_fn)
|
|
if plat not in platform_versions:
|
|
platform_versions[plat] = _local_version(plat) or "offline"
|
|
continue
|
|
|
|
_log(f"ERROR: no WolfDawn binary available for '{plat}'.", log_fn)
|
|
ok = False
|
|
|
|
_save_versions(versions)
|
|
return ok
|
|
|
|
|
|
def ensure_wolfdawn_binary(force: bool = False, log_fn=print) -> bool:
|
|
"""Ensure the ``wolf`` binary is present; refresh when missing or stale."""
|
|
platform = _platform_dir()
|
|
bundled = bundled_binary_path(platform)
|
|
missing = not bundled.is_file()
|
|
|
|
need_fetch = missing or force
|
|
if not need_fetch:
|
|
try:
|
|
need_fetch = check_wolfdawn_update(platform)
|
|
except Exception as exc:
|
|
if missing:
|
|
_log(f"ERROR: WolfDawn upstream unavailable ({exc})", log_fn)
|
|
return False
|
|
_log(f"Warning: could not check WolfDawn update ({exc}); using local copy.", log_fn)
|
|
|
|
if need_fetch:
|
|
if refresh_wolfdawn_binary(platforms=BUNDLED_PLATFORMS, log_fn=log_fn):
|
|
return bundled_binary_path(platform).is_file()
|
|
if force or missing:
|
|
_log("ERROR: WolfDawn update failed.", log_fn)
|
|
return False
|
|
_log("Warning: could not update WolfDawn; using local copy.", log_fn)
|
|
|
|
return bundled.is_file()
|
|
|
|
|
|
def main() -> int:
|
|
if "--refresh-all" in sys.argv:
|
|
return 0 if refresh_wolfdawn_binary(platforms=BUNDLED_PLATFORMS) else 1
|
|
force = "--force" in sys.argv or "-f" in sys.argv
|
|
return 0 if ensure_wolfdawn_binary(force=force) else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|