VulpesOS/tundra/tools/test-rescue-boot.py
OverSu d04824ec89 Prepare Sargo installations with private per-device workspaces
Bind backups, storage layout, RAM builds, file staging and recovery to each phone. Verify atomic transfers without replacing existing state and record the unresolved Android HAL source provenance.

Validated with 53 native tooling tests, 5 changelog tests and two RAM boots with persistent Gaia state on the development Sargo. No new flash or native binary release.
2026-10-04 00:06:13 +02:00

53 lines
2.8 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: MPL-2.0
"""Temporarily boot the private rescue image on the inventoried Sargo. Never flash."""
import argparse
import importlib.util
import json
from pathlib import Path
import subprocess
import time
from tundra import ROOT,digest,save,now
def verify_sources(folder, build):
base=folder/'sources' if build.get('sourceSnapshot') else ROOT.parent
for name,sha in {**{'tundra/'+n:h for n,h in build['sources'].items()},
**build.get('sharedUiHashes',{})}.items():
path=(base/name).resolve()
path.relative_to(base.resolve())
if digest(path)!=sha:raise ValueError('Build source changed: '+name)
def main():
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--build',type=Path,required=True);p.add_argument('--boot',action='store_true')
p.add_argument('--device-workspace',type=Path)
args=p.parse_args()
spec=importlib.util.spec_from_file_location('boot',ROOT/'tools/test-sargo-boot.py');boot=importlib.util.module_from_spec(spec);spec.loader.exec_module(boot)
folder=args.build.resolve();build=json.loads((folder/'build.json').read_text());image=folder/build['image']
if args.device_workspace:
from sargo_workspace import checked_workspace
plan,layout=checked_workspace(args.device_workspace)
if build.get('deviceWorkspaceSha256') != plan['workspaceSha256'] or build['layout'] != layout:
raise ValueError('Build was not prepared with this device workspace')
else:
if build.get('deviceWorkspaceSha256'):
raise ValueError('This build requires --device-workspace; no fallback to shared inventory')
plan,_=boot.checked_plan()
if build['imageSha256']!=digest(image) or build['serialSha256']!=plan['serialSha256'] or not build['roundtripPassed']:
raise ValueError('Image or target provenance mismatch')
verify_sources(folder,build)
print('Private image, source and backup provenance checked.',flush=True)
if not args.boot:return
# The inventoried HAL can take about three minutes to shut down after SSH
# closes. Allow that transition; identity checks still precede any boot.
serial=boot.find_fastboot(plan['serialSha256'],300);values=boot.check_device(serial,plan['serialSha256'])
report={'recordedAt':now(),'imageSha256':digest(image),'bootloader':values,'operation':'fastboot boot','flashCommandsSent':False}
result=boot.fastboot(serial,'boot',str(image));report['output']=result.stdout+result.stderr
save(folder/'boot-report.json',report)
deadline=build['watchdogSeconds']
message=f'Automatic return after {deadline} seconds.' if deadline else 'Persistent development boot; no automatic reboot.'
print(f'RAM boot sent. USB SSH: 10.15.19.82:2222. {message}',flush=True)
if __name__=='__main__':main()