#!/usr/bin/env python3 import hashlib import json import shutil import subprocess from pathlib import Path import sys ROOT = Path(__file__).resolve().parent MANIFEST = ROOT / "offline" / "manifest.json" BASE_IMAGE = "redroid/redroid:13.0.0-latest" def digest(path, algorithm): hasher = hashlib.new(algorithm) with path.open("rb") as handle: for chunk in iter(lambda: handle.read(1024 * 1024), b""): hasher.update(chunk) return hasher.hexdigest() def runtime_available(runtime): return shutil.which(runtime) is not None def local_base_image_present(): for runtime in ("docker", "podman"): if not runtime_available(runtime): continue result = subprocess.run( [runtime, "image", "inspect", BASE_IMAGE], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, ) if result.returncode == 0: return True, runtime return False, "" def main(): manifest = json.loads(MANIFEST.read_text(encoding="utf-8")) failures = [] base_image_present, base_runtime = local_base_image_present() for entry in manifest["artifacts"]: path = ROOT / entry["relative_path"] if path.name == "redroid-redroid-13.0.0-latest.tar" and base_image_present: print("SKIP {} ({} already present locally)".format(entry["relative_path"], BASE_IMAGE)) continue if not path.is_file(): if path.name == "redroid-redroid-13.0.0-latest.tar": failures.append( "{} missing (and {} not found in local {} storage)".format( entry["relative_path"], BASE_IMAGE, base_runtime or "docker/podman" ) ) continue failures.append("{} missing".format(entry["relative_path"])) continue if path.stat().st_size != entry["size"]: failures.append("{} size mismatch".format(entry["relative_path"])) continue sha256 = digest(path, "sha256") if sha256 != entry["sha256"]: failures.append("{} sha256 mismatch".format(entry["relative_path"])) continue print("OK {}".format(entry["relative_path"])) if failures: for item in failures: print("FAIL {}".format(item)) sys.exit(1) print("Offline bundle is complete.") if __name__ == "__main__": main()