Files

54 lines
3.7 KiB
Python

# One file per invocation. All test processes receive a private HOME/config/TMPDIR.
import datetime,hashlib,json,os,pathlib,subprocess,sys,time
file=sys.argv[1]
bound=int(sys.argv[2])
if not file.endswith('.test.mjs') or 'case1-accept' in file: raise SystemExit('unlisted test refused')
root=os.environ['TMPDIR']; home=pathlib.Path(root,'home'); home.mkdir(mode=0o700)
config=home/'config'; config.mkdir(mode=0o700)
env={'PATH':'/usr/bin:/bin','HOME':str(home),'TMPDIR':root,'MOSAIC_CONFIG':str(config/'unconfigured.json'),'XDG_CONFIG_HOME':str(home/'.config'),'LANG':'C.UTF-8','TZ':'UTC'}
if os.environ.get('P332_CLAUDE_EXECUTABLE'):
pin=os.environ['P332_CLAUDE_EXECUTABLE']
if not os.path.isabs(pin) or not os.path.isfile(pin) or not os.access(pin,os.X_OK):raise SystemExit('declared executable prerequisite missing')
profile=json.loads((pathlib.Path(__file__).resolve().parent/'runner-profile.json').read_text())['claudeExecutable']
if pin!=profile['path'] or hashlib.sha256(pathlib.Path(pin).read_bytes()).hexdigest()!=profile['sha256']:raise SystemExit('declared executable hash mismatch')
env['MOSAIC_CLAUDE_BIN']=pin
elif file=='packages/cli/tests/launcher.test.mjs':raise SystemExit('launcher profile requires explicit verified pinned executable')
os.setpriority(os.PRIO_PROCESS,0,19)
print(json.dumps({'phase':'start','utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'test':file,'bound':bound,'nice':os.getpriority(os.PRIO_PROCESS,0),'privateHome':str(home),'privateConfig':env['MOSAIC_CONFIG']}),flush=True)
pattern=sys.argv[3] if len(sys.argv)>3 else None
node_args=['/usr/bin/node']+(['--import',str(pathlib.Path(__file__).resolve().parent/'capture-cleanup.mjs')] if pattern else [])+['--test']+(['--test-name-pattern',pattern] if pattern else [])+[file]
if pattern:env['P332_CLEANUP_DIAGNOSTIC']=str(pathlib.Path(__file__).resolve().parent/'launcher-cleanup-diagnostic.json')
child=subprocess.Popen(['/usr/bin/timeout','-k','10',str(bound),*node_args],env=env)
corrections={}; known=set()
retained={}; presence={}
def retain_diagnostics():
if not pattern:return
for item in pathlib.Path(root).rglob('*.stderr'):
try:
text=item.read_text()
if text:
for value in ['stand-in','fx332-pi','fx653-pi']:text=text.replace(value,'[fake-value-redacted]')
retained[str(item.relative_to(root))]=text
except (OSError,UnicodeError):pass
for pid in list(known):
try:
argv=pathlib.Path(f'/proc/{pid}/cmdline').read_bytes().split(b'\0')
environment=pathlib.Path(f'/proc/{pid}/environ').read_bytes().split(b'\0')
names=sorted({entry.split(b'=',1)[0].decode() for entry in environment if b'=' in entry})
if 'MOSAIC_CLAUDE_SETTINGS' in names:
presence[str(pid)]={'executable':argv[0].decode(),'claudeAdapterArgumentPresent':any(b'adapters/claude/adapter.sh' in arg for arg in argv),'environmentNames':names}
except (OSError,UnicodeError):pass
def guard(pid):
try:
known.add(pid); before=os.getpriority(os.PRIO_PROCESS,pid)
if before!=19:
corrections[str(before)]=corrections.get(str(before),0)+1;os.setpriority(os.PRIO_PROCESS,pid,19)
for raw in pathlib.Path(f'/proc/{pid}/task/{pid}/children').read_text().split(): guard(int(raw))
except (FileNotFoundError,ProcessLookupError,PermissionError): pass
while child.poll() is None: guard(child.pid);retain_diagnostics();time.sleep(.02)
if pattern:
artifact=pathlib.Path(__file__).parent/'launcher-focused-diagnostic.json'
artifact.write_text(json.dumps({'stderr':retained,'presenceOnly':presence,'testNamePattern':pattern},indent=2)+'\n')
print(json.dumps({'phase':'end','utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),'exit':child.returncode,'niceRestoredTo':19,'priorityCorrections':corrections,'ownedPids':sorted(known)}),flush=True)
sys.exit(child.returncode)