Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,129 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MPL-2.0
"""Operate only the last isolated trial, using the existing authenticated SSH master."""
import argparse
import importlib.util
import json
from pathlib import Path
import shlex
import subprocess
ROOT = Path(__file__).resolve().parents[1]
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('action', choices=['status', 'collect', 'capture', 'restart', 'stop'])
parser.add_argument('--hybris-egl', action='store_true')
parser.add_argument('--software', action='store_true')
parser.add_argument('--headless', action='store_true')
parser.add_argument('--no-forkserver', action='store_true')
parser.add_argument('--trace-navigation', action='store_true')
parser.add_argument('--control-socket', type=Path, default=ROOT/'logs/device/ssh-control')
args = parser.parse_args()
if not args.control_socket.is_socket():
parser.error('SSH session unavailable. Reopen the authenticated master described in docs/SARGO.md.')
spec = importlib.util.spec_from_file_location('collector', ROOT/'tools/collect-device-ssh.py')
collector = importlib.util.module_from_spec(spec)
spec.loader.exec_module(collector)
base = collector.ssh_base(args.control_socket.resolve(), '10.15.19.82', 'droidian')
master = subprocess.run(base[:-1]+['-O', 'check', base[-1]], capture_output=True)
if master.returncode:
parser.error('SSH master disconnected. Reopen it before operating the trial.')
deployment = json.loads((ROOT/'logs/device/droidian-deployment.json').read_text())
remote = deployment['directory']
def run(code, *extra, **kwargs):
return subprocess.run(base+[shlex.join(['python3','-c',code,remote,*extra])],
check=True, timeout=180, **kwargs)
if args.action in ('stop', 'restart'):
run(r'''import os,pathlib,signal,sys,time
p=pathlib.Path(sys.argv[1]); product=p/'product'; pidfile=product/'logs/session.pid'
if pidfile.exists():
pid=int(pidfile.read_text()); proc=pathlib.Path('/proc')/str(pid)
if proc.exists():
command=(proc/'cmdline').read_bytes().split(b'\0')
if str(product/'trial/session.py').encode() not in command: raise RuntimeError('PID no longer belongs to trial')
os.kill(pid,signal.SIGTERM)
for _ in range(1500):
if not pidfile.exists(): break
time.sleep(.1)
else: raise RuntimeError('Session did not stop; left untouched')
''')
if args.action == 'restart':
for name in ('session.py', 'probe.sys.mjs'):
run("import pathlib,sys; (pathlib.Path(sys.argv[1])/'product/trial'/sys.argv[2]).write_bytes(sys.stdin.buffer.read())",
name, input=(ROOT/'droidian'/name).read_bytes())
if args.hybris_egl:
subprocess.run(['python3',str(ROOT/'tools/build-droidian-graphics.py')],check=True)
for name in ('libEGL_vulpes_hybris.so','manifest.json'):
run("import pathlib,sys;p=pathlib.Path(sys.argv[1])/'product/trial/graphics';p.mkdir(exist_ok=True);(p/sys.argv[2]).write_bytes(sys.stdin.buffer.read())",
name,input=(ROOT/'out/droidian-graphics'/name).read_bytes())
run('''import pathlib,shutil,subprocess,sys,time
p=pathlib.Path(sys.argv[1]); product=p/'product'; logs=product/'logs'
backup=logs/('attempt-'+str(time.time_ns())); backup.mkdir()
for name in ('gecko.log','server.log','test.json','home.png'):
f=logs/name
if f.exists(): shutil.move(f,backup/name)
for f in logs.glob('navigation.log*'): shutil.move(f,backup/f.name)
if (p/'session.log').exists(): shutil.move(p/'session.log',backup/'session.log')
with (p/'session.log').open('a') as output:
proc=subprocess.Popen(['python3',str(product/'trial/session.py'),*sys.argv[2:]],
stdin=subprocess.DEVNULL,stdout=output,stderr=subprocess.STDOUT,start_new_session=True)
print('Started trial supervisor',proc.pid)
''', *(['--software'] if args.software else []), *(['--headless'] if args.headless else []),
*(['--no-forkserver'] if args.no_forkserver else []),
*(['--trace-navigation'] if args.trace_navigation else []),
*(['--hybris-egl'] if args.hybris_egl else []))
if args.action == 'capture':
result=run('''import os,pathlib,subprocess,sys,time
p=pathlib.Path(sys.argv[1])/'product/logs'; shot=p/('physical-'+str(time.time_ns())+'.png')
env=dict(os.environ,DBUS_SESSION_BUS_ADDRESS='unix:path=/run/user/'+str(os.getuid())+'/bus')
result=subprocess.run(['gdbus','call','--session','--dest','org.gnome.Shell.Screenshot',
'--object-path','/org/gnome/Shell/Screenshot','--method',
'org.gnome.Shell.Screenshot.Screenshot','false','false',str(shot)],
env=env,capture_output=True,text=True,check=True,timeout=15)
if not result.stdout.startswith('(true,'): raise RuntimeError(result.stdout)
sys.stdout.buffer.write(shot.read_bytes())
''',capture_output=True)
target=ROOT/'logs/device/droidian-trial/physical-display.png'
target.parent.mkdir(parents=True,exist_ok=True)
target.write_bytes(result.stdout)
print(target)
if args.action == 'status':
run('''import pathlib,sys
p=pathlib.Path(sys.argv[1])
for name in ('session.log','product/logs/gecko.log','product/logs/test.json'):
f=p/name; print(name, f.read_text(errors='replace')[-12000:] if f.exists() else 'pending')
''')
if args.action == 'collect':
target=ROOT/'logs/device/droidian-trial'
target.mkdir(exist_ok=True)
for name in ('gecko.log','server.log','test.json','home.png'):
result=run("import pathlib,sys; f=pathlib.Path(sys.argv[1])/'product/logs'/sys.argv[2]; sys.stdout.buffer.write(f.read_bytes() if f.exists() else b'')",
name,capture_output=True)
if result.stdout:
(target/name).write_bytes(result.stdout)
else:
(target/name).unlink(missing_ok=True)
result=run(r'''import json,pathlib,platform,sys
p=pathlib.Path(sys.argv[1]); product=p/'product'; logs=product/'logs'
report={'kernel':platform.release(),'architecture':platform.machine(),'attempts':{},'processes':[]}
for folder in sorted(logs.glob('attempt-*')):
report['attempts'][folder.name]={f.name:f.read_text(errors='replace')[-80000:]
for f in folder.iterdir() if f.name in ('gecko.log','test.json')}
for proc in pathlib.Path('/proc').glob('[0-9]*'):
try:
cmd=(proc/'cmdline').read_bytes().split(b'\0')
if cmd and str(product/'engines').encode() in cmd[0]:
status={k:v.strip() for k,v in (line.split(':',1) for line in (proc/'status').read_text().splitlines() if ':' in line)
if k in ('Name','State','VmRSS','Seccomp','NoNewPrivs')}
report['processes'].append({'pid':proc.name,'status':status,'wchan':(proc/'wchan').read_text()})
except OSError: pass
print(json.dumps(report,indent=2))
''',capture_output=True)
(target/'diagnostics.json').write_bytes(result.stdout)
print(target)
if __name__ == '__main__':
main()