This commit is contained in:
eric
2026-04-04 15:33:17 -05:00
parent f0bfb952e8
commit 898238b3e0
5 changed files with 94 additions and 63 deletions

View File

@@ -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).

View File

@@ -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)

View File

@@ -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):

View File

@@ -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)

View File

@@ -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: