73 lines
3.9 KiB
Python
73 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
||
"""Record this dedicated guest build without copying VM credentials or user data."""
|
||
import argparse
|
||
import ctypes
|
||
import hashlib
|
||
import json
|
||
from pathlib import Path
|
||
import platform
|
||
import re
|
||
import subprocess
|
||
import xml.etree.ElementTree as ET
|
||
|
||
|
||
def sha(path):
|
||
with path.open('rb') as handle:
|
||
return hashlib.file_digest(handle, 'sha256').hexdigest()
|
||
|
||
|
||
parser = argparse.ArgumentParser(description=__doc__)
|
||
parser.add_argument('--ctest-name', default='ctest-font-final')
|
||
parser.add_argument('--output-name', default='build-record.json')
|
||
parser.add_argument('--expected-groups', type=int, default=22)
|
||
parser.add_argument('--scope-note', default='Full 22-group baseline; later focused results are recorded separately.')
|
||
args = parser.parse_args()
|
||
if (not re.fullmatch(r'[a-z0-9][a-z0-9-]{0,63}', args.ctest_name)
|
||
or not re.fullmatch(r'[a-z0-9][a-z0-9-]{0,63}\.json', args.output_name)
|
||
or not 1 <= args.expected_groups <= 100):
|
||
parser.error('Use bounded local evidence names and 1–100 expected groups')
|
||
|
||
home = Path.home()
|
||
source, build = home / 'docview-source', home / 'docview-build'
|
||
validation = home / 'validation'
|
||
output = validation / args.output_name
|
||
if output.exists():
|
||
raise SystemExit('Keep previous evidence; output already exists')
|
||
ctest = validation / args.ctest_name
|
||
cases = ET.parse(ctest / 'tests.xml').findall('.//testcase')
|
||
if len(cases) != args.expected_groups or any(x.find('failure') is not None or x.find('skipped') is not None for x in cases):
|
||
raise SystemExit('Expected all selected CTest groups to pass')
|
||
names = ['docview', 'docview-pdf-worker', 'docview-archive-worker', 'test_app',
|
||
'test_core', 'test_pdf', 'test_pdf_canvas', 'test_web_qml', 'test_translations']
|
||
files = [source / n for n in ('CMakeLists.txt', 'resources.qrc')]
|
||
for folder in ('src', 'qml', 'cmake', 'resources', 'tools'):
|
||
files += [p for p in (source / folder).rglob('*')
|
||
if p.is_file() and not p.is_symlink() and '__pycache__' not in p.parts]
|
||
files += [p for p in (source / 'tests').rglob('*')
|
||
if p.is_file() and not p.is_symlink() and 'results' not in p.parts
|
||
and '__pycache__' not in p.parts and p.suffix in ('.cpp', '.h', '.py', '.qml')]
|
||
libc = ctypes.CDLL(None, use_errno=True)
|
||
landlock_abi = libc.syscall(444, 0, 0, 1)
|
||
record = {
|
||
'scope': 'Ubuntu 24.04 dedicated KVM guest; Xvfb/Sway software rendering, no physical GPU or IME acceptance',
|
||
'os': platform.freedesktop_os_release(), 'kernel': platform.release(),
|
||
'compiler': subprocess.check_output(['c++', '--version'], text=True).splitlines()[0],
|
||
'cmake': subprocess.check_output(['cmake', '--version'], text=True).splitlines()[0],
|
||
'qt': subprocess.check_output(['/opt/docview-qt/6.11.2/gcc_64/bin/qtpaths', '--query', 'QT_VERSION'], text=True).strip(),
|
||
'landlockABI': landlock_abi,
|
||
'apparmorRestrictUnprivilegedUserns': Path('/proc/sys/kernel/apparmor_restrict_unprivileged_userns').read_text().strip(),
|
||
'qtWebEngineApparmorProfileSha256': sha(Path('/etc/apparmor.d/docview-qtwebengine')),
|
||
'binaries': {n: sha(build / n) for n in names},
|
||
'installedBinaries': {n: sha(home / 'docview-install/bin' / n) for n in names[:3]},
|
||
'sourceSha256': {str(p.relative_to(source)): sha(p) for p in sorted(set(files))},
|
||
'ctest': {'directory': args.ctest_name, 'groups': len(cases), 'failures': 0, 'junitSha256': sha(ctest / 'tests.xml'),
|
||
'logSha256': sha(ctest / 'LastTest.log')},
|
||
'focusedTestScope': args.scope_note,
|
||
'pythonProvenanceScope': '30 passed and 5 explicit zstd skips with Python 3.12; Arch Python 3.14 executes all 35 cases.',
|
||
'productionBinariesChangedByTestFixes': False,
|
||
}
|
||
with output.open('x') as handle:
|
||
handle.write(json.dumps(record, ensure_ascii=False, indent=2) + '\n')
|
||
print(json.dumps({'ctestGroups': len(cases), 'sourceFiles': len(record['sourceSha256']),
|
||
'productionBinarySha256': record['binaries']['docview']}))
|