From 898238b3e041c80ce20b3ccd5b85e42ffeb280aa Mon Sep 17 00:00:00 2001 From: eric Date: Sat, 4 Apr 2026 15:33:17 -0500 Subject: [PATCH] 's' --- offline/README.md | 6 ++--- redroid.py | 51 +++++++++++++++++++++++++------------ stuff/general.py | 29 ++++++++++++--------- stuff/magisk.py | 65 ++++++++++++++++++++++++----------------------- tools/helper.py | 6 +++++ 5 files changed, 94 insertions(+), 63 deletions(-) diff --git a/offline/README.md b/offline/README.md index f8b6eac..fd8da4c 100644 --- a/offline/README.md +++ b/offline/README.md @@ -26,6 +26,6 @@ python verify_offline_bundle.py 1. Make sure your container runtime can load local image archives. 2. Populate `downloads/` and `images/` with the files listed in `manifest.json`. -3. Run `python redroid.py -mtg -i -m`. -4. The build first tries to pull the base image and download dependency archives from their online sources. -5. If an online pull/download fails, the build falls back to the validated files in `downloads/` and `images/`. +3. Run `python redroid.py -mtg -i -m` (add `--offline` or set `REDROID_OFFLINE=1` to forbid any network use). +4. The build prefers a local container image, then `images/redroid-redroid-13.0.0-latest.tar`, then a registry pull (skipped when offline-only). +5. Archives in `downloads/` are preferred when present and match the expected checksum; otherwise the build downloads from upstream (skipped when offline-only). diff --git a/redroid.py b/redroid.py index c7dcbfb..e88ebdc 100644 --- a/redroid.py +++ b/redroid.py @@ -24,37 +24,48 @@ def ensure_base_image(container_runtime, android_version): ) local_present = inspect.returncode == 0 - helper.print_color( - "Trying to pull base image {} first ...".format(base_image), - helper.bcolors.GREEN, - ) - pull = subprocess.run([container_runtime, "pull", base_image], check=False) - if pull.returncode == 0: - return "pulled" + tar_name = "redroid-redroid-{}-latest.tar".format(android_version) + tar_path = os.path.join(helper.get_image_dir(), tar_name) + tar_exists = os.path.isfile(tar_path) + offline_only = helper.is_offline_only() if local_present: helper.print_color( - "Online pull failed, falling back to local image cache ...", - helper.bcolors.YELLOW, + "Using existing local base image {} ...".format(base_image), + helper.bcolors.GREEN, ) return "present" - tar_name = "redroid-redroid-{}-latest.tar".format(android_version) - tar_path = os.path.join(helper.get_image_dir(), tar_name) - if not os.path.isfile(tar_path): + if tar_exists: + helper.print_color( + "Loading base image from offline archive {} ...".format(tar_path), + helper.bcolors.GREEN, + ) + subprocess.run([container_runtime, "load", "-i", tar_path], check=True) + return tar_path + + if offline_only: raise FileNotFoundError( - "Online pull for {} failed, local image cache is missing, and offline archive {} was not found".format( + "REDROID_OFFLINE is set but {} is not in local storage and {} was not found".format( base_image, tar_path, ) ) helper.print_color( - "Online pull failed, loading local image archive {} ...".format(tar_path), + "No local base image or offline .tar; pulling {} ...".format(base_image), helper.bcolors.YELLOW, ) - subprocess.run([container_runtime, "load", "-i", tar_path], check=True) - return tar_path + pull = subprocess.run([container_runtime, "pull", base_image], check=False) + if pull.returncode == 0: + return "pulled" + + raise FileNotFoundError( + "Pull failed for {} and offline archive {} was not found".format( + base_image, + tar_path, + ) + ) def build_layers(args): @@ -145,8 +156,16 @@ def main(): choices=["docker", "podman"], help="Container runtime", ) + parser.add_argument( + "--offline", + dest="offline", + action="store_true", + help="Do not pull images or download archives; require offline/downloads and offline/images", + ) args = parser.parse_args() + if args.offline: + os.environ["REDROID_OFFLINE"] = "1" base_image_source = ensure_base_image(args.container, args.android) dockerfile, tags = build_layers(args) diff --git a/stuff/general.py b/stuff/general.py index 81610ab..1b74e03 100644 --- a/stuff/general.py +++ b/stuff/general.py @@ -2,7 +2,7 @@ import os import zipfile -from tools.helper import bcolors, download_file, file_md5, print_color +from tools.helper import bcolors, download_file, file_md5, is_offline_only, print_color class General: def _local_md5(self): @@ -17,10 +17,24 @@ class General: temp_path = self.dl_file_name + ".tmp" local_valid = self._has_valid_local_file() + if local_valid: + print_color( + "Using validated offline bundle {} ...".format(self.dl_file_name), + bcolors.GREEN, + ) + return + + if is_offline_only(): + raise FileNotFoundError( + "REDROID_OFFLINE is set but offline file is missing or MD5 mismatch: {}".format( + self.dl_file_name + ) + ) + try: if os.path.isfile(temp_path): os.remove(temp_path) - print_color("Trying online download first ...", bcolors.GREEN) + print_color("Local bundle not found or MD5 mismatch; downloading ...", bcolors.YELLOW) remote_md5 = download_file(self.dl_link, temp_path) if remote_md5 != self.act_md5: raise ValueError( @@ -30,18 +44,9 @@ class General: ) ) os.replace(temp_path, self.dl_file_name) - except Exception as exc: + except Exception: if os.path.isfile(temp_path): os.remove(temp_path) - if local_valid: - print_color( - "Online download failed, falling back to local {} ({})".format( - self.dl_file_name, - exc, - ), - bcolors.YELLOW, - ) - return raise def extract(self): diff --git a/stuff/magisk.py b/stuff/magisk.py index 11a4da5..4661153 100644 --- a/stuff/magisk.py +++ b/stuff/magisk.py @@ -4,7 +4,15 @@ import re import zipfile import requests from stuff.general import General -from tools.helper import bcolors, download_file, host, print_color, run, get_download_dir +from tools.helper import ( + bcolors, + download_file, + get_download_dir, + host, + is_offline_only, + print_color, + run, +) class Magisk(General): _default_stable_json = ( @@ -225,39 +233,32 @@ on property:init.svc.zygote=stopped local_valid = self._has_valid_local_archive() local_version = self._cached_version() or "bundled" - try: - if self.custom_url: - self.dl_link = self.custom_url - self.release_version = "url:" + os.path.basename(self.custom_url) - else: - self.resolve_release() - except Exception as exc: - if local_valid: - self.release_version = local_version - print_color( - "Resolving Kitsune Mask online failed, falling back to local {} ({})".format( - self.release_version, - exc, - ), - bcolors.YELLOW, - ) - return - raise + if local_valid: + self.release_version = local_version + print_color( + "Using offline Magisk/Kitsune APK {} ({}) ...".format( + self.dl_file_name, + self.release_version, + ), + bcolors.GREEN, + ) + self._validate_archive() + return - try: - self._download_remote_archive() - except Exception as exc: - if local_valid: - self.release_version = local_version - print_color( - "Downloading Kitsune Mask online failed, falling back to local {} ({})".format( - self.release_version, - exc, - ), - bcolors.YELLOW, + if is_offline_only(): + raise FileNotFoundError( + "REDROID_OFFLINE is set but no valid offline magisk.apk at {}".format( + self.dl_file_name ) - return - raise + ) + + if self.custom_url: + self.dl_link = self.custom_url + self.release_version = "url:" + os.path.basename(self.custom_url) + else: + self.resolve_release() + + self._download_remote_archive() def extract(self): print_color("Extracting archive...", bcolors.GREEN) diff --git a/tools/helper.py b/tools/helper.py index 2fc9865..5870a50 100644 --- a/tools/helper.py +++ b/tools/helper.py @@ -26,6 +26,12 @@ def get_image_dir(): return ensure_dir(IMAGE_ROOT) +def is_offline_only(): + """If true, redroid build must not use the network (env REDROID_OFFLINE or CLI --offline).""" + v = os.environ.get("REDROID_OFFLINE", "").strip().lower() + return v in ("1", "true", "yes", "on") + + def file_md5(path, block_size=1024 * 1024): hasher = hashlib.md5() with open(path, "rb") as handle: