IQ.Pilot Release Commit @ bec7652
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "xbl",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/xbl-dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/xbl-dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6.img.xz",
|
||||
"hash": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6",
|
||||
"hash_raw": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6",
|
||||
"size": 3282256,
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
{
|
||||
"name": "xbl_config",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/xbl_config-1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/xbl_config-1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9.img.xz",
|
||||
"hash": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9",
|
||||
"hash_raw": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9",
|
||||
"size": 98124,
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
{
|
||||
"name": "abl",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/abl-556bbb4ed1c671402b217bd2f3c07edce4f88b0bbd64e92241b82e396aa9ebee.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/abl-556bbb4ed1c671402b217bd2f3c07edce4f88b0bbd64e92241b82e396aa9ebee.img.xz",
|
||||
"hash": "556bbb4ed1c671402b217bd2f3c07edce4f88b0bbd64e92241b82e396aa9ebee",
|
||||
"hash_raw": "556bbb4ed1c671402b217bd2f3c07edce4f88b0bbd64e92241b82e396aa9ebee",
|
||||
"size": 274432,
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"name": "aop",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/aop-4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/aop-4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788.img.xz",
|
||||
"hash": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788",
|
||||
"hash_raw": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788",
|
||||
"size": 184364,
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
{
|
||||
"name": "devcfg",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/devcfg-2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/devcfg-2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585.img.xz",
|
||||
"hash": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585",
|
||||
"hash_raw": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585",
|
||||
"size": 40336,
|
||||
@@ -56,7 +56,7 @@
|
||||
},
|
||||
{
|
||||
"name": "splash",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/splash-993d7fb8ddfa552bd7f60e8a78b8735efbc716a0978682ed3c92fa0d694528d2.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/splash-993d7fb8ddfa552bd7f60e8a78b8735efbc716a0978682ed3c92fa0d694528d2.img.xz",
|
||||
"hash": "993d7fb8ddfa552bd7f60e8a78b8735efbc716a0978682ed3c92fa0d694528d2",
|
||||
"hash_raw": "993d7fb8ddfa552bd7f60e8a78b8735efbc716a0978682ed3c92fa0d694528d2",
|
||||
"size": 34226176,
|
||||
@@ -67,7 +67,7 @@
|
||||
},
|
||||
{
|
||||
"name": "boot",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/boot-aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/boot-aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb.img.xz",
|
||||
"hash": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb",
|
||||
"hash_raw": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb",
|
||||
"size": 18216960,
|
||||
@@ -78,7 +78,7 @@
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/system-44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/system-44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa.img.xz",
|
||||
"hash": "44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa",
|
||||
"hash_raw": "44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa",
|
||||
"size": 6291456000,
|
||||
|
||||
@@ -1 +1 @@
|
||||
IE+GKvyryxGDx98VImAM+UaqOPcs+mzWtlVs7r0h4Va2Tb9glgFBFlpaB3Btll+QI5zoo/tMlDrj463kaYQQBg==
|
||||
qT72MCHtDUWnARJbSsLUcPaISRZxjFPNf282R9ZC1SXvFJz6X6NmjgknK3OLDLijOQWlGvvJZGsDgz2vsdPVAg==
|
||||
|
||||
@@ -9,15 +9,11 @@ import subprocess
|
||||
import time
|
||||
from collections.abc import Generator
|
||||
|
||||
# agnos.py needs venv-only deps (casync->pycryptodome, swaglog->zmq, cereal). The boot runs
|
||||
# it via `env python3` = /usr/bin/python3, which lacks them on stock AGNOS -> the IQ.OS
|
||||
# bootstrap flash silently dies on import and the device hangs on the logo. Re-exec under the
|
||||
# venv python when the current interpreter lacks them (no-op once already the venv python).
|
||||
import sys
|
||||
_VENV_PY = "/usr/local/venv/bin/python3"
|
||||
if sys.executable != _VENV_PY and os.path.exists(_VENV_PY):
|
||||
try:
|
||||
import Crypto # noqa: F401 probe for the venv-only deps
|
||||
import Crypto # noqa: F401
|
||||
except ImportError:
|
||||
os.execv(_VENV_PY, [_VENV_PY, os.path.abspath(__file__), *sys.argv[1:]])
|
||||
|
||||
@@ -41,11 +37,7 @@ AGNOS_MANIFEST_FILE = "system/hardware/tici/agnos.json"
|
||||
|
||||
LFS_POINTER_MAGIC = b"version https://git-lfs"
|
||||
|
||||
|
||||
def _image_auth_module():
|
||||
"""Return the git_remote auth module, or None. On IQ.OS this comes through the
|
||||
verified loader; on stock AGNOS (an AGNOS->IQ.OS upgrade) that loader is not
|
||||
present, but the compiled bundle IS in every checkout -- import it directly."""
|
||||
try:
|
||||
from iqpilot.system.proprietary_runtime._verified_import import import_verified_module
|
||||
return import_verified_module("iqpilot_updater_private", "iqpilot_private.updater.git_remote")
|
||||
@@ -63,7 +55,6 @@ def _image_auth_module():
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _download_headers(url: str) -> dict:
|
||||
mod = _image_auth_module()
|
||||
if mod is not None:
|
||||
@@ -82,11 +73,7 @@ def _download_headers(url: str) -> dict:
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
||||
def _open_image_response(url: str) -> requests.Response:
|
||||
"""GET an image URL; when the server answers with a Git-LFS pointer (the image
|
||||
repo stores partitions as LFS objects and its raw endpoint does not resolve
|
||||
them), follow it through the LFS batch API using the same credentials."""
|
||||
auth = _download_headers(url)
|
||||
req = requests.get(url, stream=True, headers={'Accept-Encoding': None, **auth}, timeout=60)
|
||||
req.raise_for_status()
|
||||
@@ -99,9 +86,14 @@ def _open_image_response(url: str) -> requests.Response:
|
||||
meta = dict(line.split(" ", 1) for line in body.decode().strip().splitlines() if " " in line)
|
||||
oid = meta["oid"].split(":", 1)[1]
|
||||
size = int(meta["size"])
|
||||
lfs_base = url.split("/raw/", 1)[0] + ".git/info/lfs"
|
||||
|
||||
batch_url = url.split("/raw/", 1)[0] + ".git/info/lfs/objects/batch"
|
||||
batch = requests.post(batch_url,
|
||||
req = requests.get(f"{lfs_base}/objects/{oid}", stream=True,
|
||||
headers={'Accept-Encoding': None, 'Accept': 'application/vnd.git-lfs', **auth}, timeout=60)
|
||||
if req.status_code == 200:
|
||||
return req
|
||||
|
||||
batch = requests.post(f"{lfs_base}/objects/batch",
|
||||
data=json.dumps({"operation": "download", "transfers": ["basic"],
|
||||
"objects": [{"oid": oid, "size": size}]}),
|
||||
headers={"Content-Type": "application/vnd.git-lfs+json",
|
||||
@@ -128,13 +120,7 @@ def verify_manifest_signature(manifest_path: str) -> None:
|
||||
public_key = Ed25519PublicKey.from_public_bytes(IQPILOT_MANIFEST_PUBLIC_KEY)
|
||||
public_key.verify(signature, digest)
|
||||
|
||||
|
||||
class _ChainedParts:
|
||||
"""Response-like wrapper streaming N sequential part files as one body.
|
||||
|
||||
The image host caps single uploads well below the system image size, so big
|
||||
images are stored as `<name>.pNN` LFS objects; devices re-join them here."""
|
||||
|
||||
def __init__(self, urls: list[str]) -> None:
|
||||
self.urls = urls
|
||||
self.req: requests.Response | None = None
|
||||
@@ -148,7 +134,6 @@ class _ChainedParts:
|
||||
self.req = _open_image_response(u)
|
||||
yield from self.req.iter_content(chunk_size=chunk_size)
|
||||
|
||||
|
||||
class StreamingDecompressor:
|
||||
def __init__(self, url: str, parts: int = 0) -> None:
|
||||
self.buf = b""
|
||||
@@ -187,57 +172,48 @@ class StreamingDecompressor:
|
||||
self.sha256.update(result)
|
||||
return result
|
||||
|
||||
|
||||
def unsparsify(f: StreamingDecompressor) -> Generator[bytes, None, None]:
|
||||
# https://source.android.com/devices/bootloader/images#sparse-format
|
||||
magic = struct.unpack("I", f.read(4))[0]
|
||||
assert(magic == 0xed26ff3a)
|
||||
|
||||
# Version
|
||||
major = struct.unpack("H", f.read(2))[0]
|
||||
minor = struct.unpack("H", f.read(2))[0]
|
||||
assert(major == 1 and minor == 0)
|
||||
|
||||
f.read(2) # file header size
|
||||
f.read(2) # chunk header size
|
||||
f.read(2)
|
||||
f.read(2)
|
||||
|
||||
block_sz = struct.unpack("I", f.read(4))[0]
|
||||
f.read(4) # total blocks
|
||||
f.read(4)
|
||||
num_chunks = struct.unpack("I", f.read(4))[0]
|
||||
f.read(4) # crc checksum
|
||||
f.read(4)
|
||||
|
||||
for _ in range(num_chunks):
|
||||
chunk_type, out_blocks = SPARSE_CHUNK_FMT.unpack(f.read(12))
|
||||
|
||||
if chunk_type == 0xcac1: # Raw
|
||||
# TODO: yield in smaller chunks. Yielding only block_sz is too slow. Largest observed data chunk is 252 MB.
|
||||
if chunk_type == 0xcac1:
|
||||
yield f.read(out_blocks * block_sz)
|
||||
elif chunk_type == 0xcac2: # Fill
|
||||
elif chunk_type == 0xcac2:
|
||||
filler = f.read(4) * (block_sz // 4)
|
||||
for _ in range(out_blocks):
|
||||
yield filler
|
||||
elif chunk_type == 0xcac3: # Don't care
|
||||
elif chunk_type == 0xcac3:
|
||||
yield b""
|
||||
else:
|
||||
raise Exception("Unhandled sparse chunk type")
|
||||
|
||||
|
||||
# noop wrapper with same API as unsparsify() for non sparse images
|
||||
def noop(f: StreamingDecompressor) -> Generator[bytes, None, None]:
|
||||
while len(chunk := f.read(1024 * 1024)) > 0:
|
||||
yield chunk
|
||||
|
||||
|
||||
def get_target_slot_number() -> int:
|
||||
current_slot = subprocess.check_output(["abctl", "--boot_slot"], encoding='utf-8').strip()
|
||||
return 1 if current_slot == "_a" else 0
|
||||
|
||||
|
||||
def slot_number_to_suffix(slot_number: int) -> str:
|
||||
assert slot_number in (0, 1)
|
||||
return '_a' if slot_number == 0 else '_b'
|
||||
|
||||
|
||||
def get_partition_path(target_slot_number: int, partition: dict) -> str:
|
||||
path = f"/dev/disk/by-partlabel/{partition['name']}"
|
||||
|
||||
@@ -246,7 +222,6 @@ def get_partition_path(target_slot_number: int, partition: dict) -> str:
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def get_raw_hash(path: str, partition_size: int) -> str:
|
||||
raw_hash = hashlib.sha256()
|
||||
pos, chunk_size = 0, 1024 * 1024
|
||||
@@ -259,7 +234,6 @@ def get_raw_hash(path: str, partition_size: int) -> str:
|
||||
|
||||
return raw_hash.hexdigest().lower()
|
||||
|
||||
|
||||
def verify_partition(target_slot_number: int, partition: dict[str, str | int], force_full_check: bool = False) -> bool:
|
||||
full_check = partition['full_check'] or force_full_check
|
||||
path = get_partition_path(target_slot_number, partition)
|
||||
@@ -281,7 +255,6 @@ def verify_partition(target_slot_number: int, partition: dict[str, str | int], f
|
||||
out.seek(partition_size)
|
||||
return out.read(64) == partition_hash.lower().encode()
|
||||
|
||||
|
||||
def clear_partition_hash(target_slot_number: int, partition: dict) -> None:
|
||||
path = get_partition_path(target_slot_number, partition)
|
||||
with open(path, 'wb+') as out:
|
||||
@@ -291,13 +264,11 @@ def clear_partition_hash(target_slot_number: int, partition: dict) -> None:
|
||||
out.write(b"\x00" * 64)
|
||||
os.sync()
|
||||
|
||||
|
||||
def extract_compressed_image(target_slot_number: int, partition: dict, cloudlog):
|
||||
path = get_partition_path(target_slot_number, partition)
|
||||
downloader = StreamingDecompressor(partition['url'], parts=int(partition.get('url_parts', 0)))
|
||||
|
||||
with open(path, 'wb+') as out:
|
||||
# Flash partition
|
||||
last_p = 0
|
||||
raw_hash = hashlib.sha256()
|
||||
f = unsparsify if partition['sparse'] else noop
|
||||
@@ -320,7 +291,6 @@ def extract_compressed_image(target_slot_number: int, partition: dict, cloudlog)
|
||||
|
||||
os.sync()
|
||||
|
||||
|
||||
def extract_casync_image(target_slot_number: int, partition: dict, cloudlog):
|
||||
path = get_partition_path(target_slot_number, partition)
|
||||
seed_path = path[:-1] + ('b' if path[-1] == 'a' else 'a')
|
||||
@@ -329,7 +299,6 @@ def extract_casync_image(target_slot_number: int, partition: dict, cloudlog):
|
||||
|
||||
sources: list[tuple[str, casync.ChunkReader, casync.ChunkDict]] = []
|
||||
|
||||
# First source is the current partition.
|
||||
try:
|
||||
raw_hash = get_raw_hash(seed_path, partition['size'])
|
||||
caibx_url = f"{CAIBX_URL}{partition['name']}-{raw_hash}.caibx"
|
||||
@@ -342,10 +311,8 @@ def extract_casync_image(target_slot_number: int, partition: dict, cloudlog):
|
||||
except Exception:
|
||||
cloudlog.exception("casync failed to hash seed partition")
|
||||
|
||||
# Second source is the target partition, this allows for resuming
|
||||
sources += [('target', casync.FileChunkReader(path), casync.build_chunk_dict(target))]
|
||||
|
||||
# Finally we add the remote source to download any missing chunks
|
||||
sources += [('remote', casync.RemoteChunkReader(partition['casync_store']), casync.build_chunk_dict(target))]
|
||||
|
||||
last_p = 0
|
||||
@@ -364,7 +331,6 @@ def extract_casync_image(target_slot_number: int, partition: dict, cloudlog):
|
||||
if not verify_partition(target_slot_number, partition, force_full_check=True):
|
||||
raise Exception(f"Raw hash mismatch '{partition['hash_raw'].lower()}'")
|
||||
|
||||
|
||||
def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalone=False):
|
||||
cloudlog.info(f"Downloading and writing {partition['name']}")
|
||||
|
||||
@@ -372,7 +338,6 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalo
|
||||
cloudlog.info(f"Already flashed {partition['name']}")
|
||||
return
|
||||
|
||||
# Clear hash before flashing in case we get interrupted
|
||||
full_check = partition['full_check']
|
||||
if not full_check:
|
||||
clear_partition_hash(target_slot_number, partition)
|
||||
@@ -384,13 +349,11 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalo
|
||||
else:
|
||||
extract_compressed_image(target_slot_number, partition, cloudlog)
|
||||
|
||||
# Write hash after successful flash
|
||||
if not full_check:
|
||||
with open(path, 'wb+') as out:
|
||||
out.seek(partition['size'])
|
||||
out.write(partition['hash_raw'].lower().encode())
|
||||
|
||||
|
||||
def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
|
||||
verify_manifest_signature(manifest_path)
|
||||
update = json.load(open(manifest_path))
|
||||
@@ -406,14 +369,12 @@ def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
|
||||
else:
|
||||
cloudlog.error(f"Swap failed {out}")
|
||||
|
||||
|
||||
def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, standalone=False) -> None:
|
||||
verify_manifest_signature(manifest_path)
|
||||
update = json.load(open(manifest_path))
|
||||
|
||||
cloudlog.info(f"Target slot {target_slot_number}")
|
||||
|
||||
# set target slot as unbootable
|
||||
os.system(f"abctl --set_unbootable {target_slot_number}")
|
||||
|
||||
for partition in update:
|
||||
@@ -436,13 +397,11 @@ def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, st
|
||||
|
||||
cloudlog.info(f"AGNOS ready on slot {target_slot_number}")
|
||||
|
||||
|
||||
def verify_agnos_update(manifest_path: str, target_slot_number: int) -> bool:
|
||||
verify_manifest_signature(manifest_path)
|
||||
update = json.load(open(manifest_path))
|
||||
return all(verify_partition(target_slot_number, partition) for partition in update)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "xbl",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/xbl-dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/xbl-dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6.img.xz",
|
||||
"hash": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6",
|
||||
"hash_raw": "dd45c0febdf0e022dab82ed0219370a86e8e6c0dfabfe29f3dab7eb1174d6bc6",
|
||||
"size": 3282256,
|
||||
@@ -12,7 +12,7 @@
|
||||
},
|
||||
{
|
||||
"name": "xbl_config",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/xbl_config-1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/xbl_config-1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9.img.xz",
|
||||
"hash": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9",
|
||||
"hash_raw": "1074ae051df159ba6dba988d8f6ba2cfc304ed1466cce0db531df6f7b1e44aa9",
|
||||
"size": 98124,
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
{
|
||||
"name": "abl",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/abl-32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/abl-32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6.img.xz",
|
||||
"hash": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6",
|
||||
"hash_raw": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6",
|
||||
"size": 274432,
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"name": "aop",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/aop-4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/aop-4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788.img.xz",
|
||||
"hash": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788",
|
||||
"hash_raw": "4d925c9248672e4a69a236991983375008c44997a854ee7846d1b5fd7c787788",
|
||||
"size": 184364,
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
{
|
||||
"name": "devcfg",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/devcfg-2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/devcfg-2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585.img.xz",
|
||||
"hash": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585",
|
||||
"hash_raw": "2f374581243910db92f62bb13bd66ec8e3d56d434997ba007ded06d2d6cc8585",
|
||||
"size": 40336,
|
||||
@@ -56,7 +56,7 @@
|
||||
},
|
||||
{
|
||||
"name": "boot",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/boot-aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/boot-aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb.img.xz",
|
||||
"hash": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb",
|
||||
"hash_raw": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb",
|
||||
"size": 18216960,
|
||||
@@ -67,7 +67,7 @@
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"url": "https://gitlvb.teallvbs.xyz/IQ.Lvbs/iqos/raw/branch/master/system-44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa.img.xz",
|
||||
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/system-44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa.img.xz",
|
||||
"hash": "44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa",
|
||||
"hash_raw": "44b251e1b3d8cd9243d5c79287d2a0e74b5063d3e21b24192b1293430a3471aa",
|
||||
"size": 6291456000,
|
||||
|
||||
@@ -1 +1 @@
|
||||
9LDZugtT9q8jFab1Gs8qTmfJukKU8NzDAuT9VakInuOUHCCnN5SDFI/Ew/6C/+3SAlcROgZON/4J8FQyQGV+Dg==
|
||||
fL46Z+k/wqjavP3J1S/VzE90BXcvvRF+S41MgHEXYjW+Jlnu5REOp6rvp3SJFEkKIEaiSf9PsHyiDgQkC6J2Aw==
|
||||
|
||||
@@ -8,9 +8,8 @@ MANIFESTS = [
|
||||
os.path.join(TEST_DIR, "../agnos_tici_15_1.json"),
|
||||
]
|
||||
|
||||
IMAGE_HOST = "gitlvb.teallvbs.xyz"
|
||||
IMAGE_HOST = "git.konn3kt.com"
|
||||
|
||||
# image payloads are xz streams; the repo raw endpoint would serve an LFS pointer
|
||||
XZ_MAGIC = b"\xfd7zXZ\x00"
|
||||
LFS_POINTER_MAGIC = b"version https://git-lfs"
|
||||
|
||||
@@ -27,12 +26,6 @@ class TestAgnosUpdater:
|
||||
if not img['sparse']:
|
||||
assert img['hash'] == img['hash_raw']
|
||||
|
||||
# contract: images are distributed from a private repo, so an anonymous
|
||||
# request must never receive image content. The denial status varies by
|
||||
# route (404 via the CDN, catch-all HTML page when resolved directly to
|
||||
# the origin), so assert on the payload, not the status code. trust_env
|
||||
# off: requests otherwise picks up ~/.netrc (CI runners have gitlvb
|
||||
# credentials), silently authenticating the "anonymous" probe.
|
||||
s = requests.Session()
|
||||
s.trust_env = False
|
||||
r = s.get(img['url'], timeout=10, stream=True,
|
||||
|
||||
@@ -43,8 +43,6 @@ def required_agnos_version(install_path: str) -> str:
|
||||
|
||||
|
||||
def _hardware_dir(install_path: str) -> str:
|
||||
# the nested layout keeps the package at <install>/iqpilot/; older checkouts had
|
||||
# system/ at the top level. Accept either so callers can pass the install root.
|
||||
nested = os.path.join(install_path, "iqpilot", "system", "hardware", "tici")
|
||||
if os.path.isdir(nested):
|
||||
return nested
|
||||
|
||||
Reference in New Issue
Block a user