VulpesOS/tundra/droidian/check-runtime.py
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

102 lines
5.1 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: MPL-2.0
"""Check the ARM64 reference in a private mount namespace, without starting services."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import subprocess
import sys
from urllib.parse import quote
def run(*args, check=True):
result = subprocess.run(args, check=False, capture_output=True, text=True, timeout=45)
if check and result.returncode:
raise RuntimeError(f'{args}: {result.stderr.strip()}')
return result
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--root', type=Path, required=True)
parser.add_argument('--product', type=Path, required=True)
parser.add_argument('--inside-namespace', action='store_true', help=argparse.SUPPRESS)
args = parser.parse_args()
if os.geteuid() != 0:
parser.error('chroot and the private mount namespace require administrator privileges')
root, product = args.root.resolve(), args.product.resolve()
base = Path('/home/droidian/vulpes-tundra-tests')
for path in (root, product):
if not path.is_relative_to(base) or path == base:
parser.error('Expected isolated device trial directories')
if not (root.parent/(root.name+'-stage.json')).is_file():
parser.error('Staging report missing')
if not args.inside_namespace:
result = subprocess.run(['unshare', '--mount', '--net', '--fork', sys.executable,
str(Path(__file__).resolve()), '--inside-namespace', '--root', str(root),
'--product', str(product)], check=False)
raise SystemExit(result.returncode)
if os.readlink('/proc/self/ns/mnt') == os.readlink('/proc/1/ns/mnt'):
parser.error('Refusing to mount in the system namespace')
run('mount', '--make-rprivate', '/')
run('mount', '--bind', str(product), str(root/'opt/vulpes'))
run('mount', '-o', 'remount,bind,ro', str(root/'opt/vulpes'))
run('mount', '-t', 'proc', 'proc', str(root/'proc'))
for name in ('null', 'urandom', 'random'):
(root/'dev'/name).touch(exist_ok=True)
run('mount', '--bind', '/dev/'+name, str(root/'dev'/name))
for name in ('run', 'tmp'):
run('mount', '-t', 'tmpfs', '-o', 'mode=1777,size=128m', 'tmpfs', str(root/name))
os.chown(root/'home/vulpes', 1000, 1000)
profile = root/'home/vulpes/render-profile'
profile.mkdir(exist_ok=True)
os.chown(profile, 1000, 1000)
(root/'home/vulpes/render.png').unlink(missing_ok=True)
run('chroot', str(root), '/sbin/ldconfig')
compiler = '/usr/lib/aarch64-linux-gnu/glib-2.0/glib-compile-schemas'
run('chroot', str(root), compiler, '/usr/share/glib-2.0/schemas')
engine = json.loads((product/'engine-lock.json').read_text())['version']
binary = f'/opt/vulpes/engines/{engine}/firefox/firefox'
commands = {
'python': ['/usr/bin/python3', '-c', 'import platform,ssl,json; print(json.dumps({"machine":platform.machine(),"python":platform.python_version(),"ssl":ssl.OPENSSL_VERSION}))'],
'compositor': ['/usr/bin/phoc', '--version'],
'gecko': [binary, '--headless', '--version'],
'geckoRendering': [binary, '--headless', '--no-remote',
'--profile', '/home/vulpes/render-profile', '--window-size', '360,740',
'--screenshot', '/home/vulpes/render.png',
'data:text/html,'+quote('<!doctype html><meta charset="utf-8"><title>Tundra runtime check</title><body style="background:#123456;color:white">Gecko runtime qualification</body>')],
'geckoLibraries': ['/usr/bin/ldd', binary],
'compositorLibraries': ['/usr/bin/ldd', '/usr/bin/phoc'],
}
results = {}
for name, command in commands.items():
result = run('chroot', '--userspec=1000:1000', str(root), '/usr/bin/env', '-i',
'HOME=/home/vulpes', 'PATH=/usr/bin:/bin:/usr/sbin:/sbin',
'LANG=C', *command, check=False)
results[name] = {'command': command, 'returncode': result.returncode,
'stdout': result.stdout, 'stderr': result.stderr}
report = {'scope': 'Offline non-root chroot programs; not a boot, HAL or graphical qualification',
'engine': engine, 'uid': 1000, 'commands': results, 'sandboxQualified': False,
'passed': all(r['returncode'] == 0 and 'not found' not in r['stdout']
for r in results.values())}
screenshot = root/'home/vulpes/render.png'
if screenshot.is_file():
data = screenshot.read_bytes()
report['render'] = {'sha256': hashlib.sha256(data).hexdigest(),
'width': int.from_bytes(data[16:20], 'big'),
'height': int.from_bytes(data[20:24], 'big')}
report['passed'] &= (data.startswith(b'\x89PNG\r\n\x1a\n') and
report['render']['width'] == 360 and report['render']['height'] == 740)
else:
report['passed'] = False
path = root.parent/'chroot-check.json'
path.write_text(json.dumps(report, indent=2)+'\n')
print(json.dumps(report, indent=2), flush=True)
if not report['passed']:
raise SystemExit(1)
if __name__ == '__main__':
main()