Files
ARCADIA-Studio/harness/experiments/canvas-native-dd39e1f-20260913/tools/lifecycle-palette-native-session.py
T

72 lines
4.6 KiB
Python

"""Prepare a new disposable canvas trial; retain the real GUI process exit code."""
from pathlib import Path
from datetime import datetime, timezone
import hashlib
import json
import os
import shutil
import subprocess
import sys
root = Path(__file__).resolve().parent.parent
trial = root / '.local/native-qa/lifecycle-palette-20260913-01'
mode = sys.argv[1]
if mode == 'prepare':
assert not trial.exists(), 'Never overwrite a native trial'
sources = {
'compact-baseline': root / '.local/native-qa/delete-20260912/compact-baseline-v2',
'compact-work': root / '.local/native-qa/delete-20260912/compact-baseline-v2',
'lifecycle-baseline': root / '.local/nested-lifecycle-native-fixture-20260912',
'lifecycle-work': root / '.local/nested-lifecycle-native-fixture-20260912',
}
assert all(path.is_dir() for path in sources.values())
trial.mkdir()
rows = []
for label, source in sources.items():
files = ([source / name for name in ['lifecycle-native.arcadia', 'identities.tsv', 'README.md', 'preflight.rs']]
if label.startswith('lifecycle') else [path for path in sorted(source.rglob('*')) if path.is_file()])
for path in files:
relative = path.relative_to(source)
raw = path.read_bytes()
target = trial / label / relative
target.parent.mkdir(parents=True, exist_ok=True)
with target.open('xb') as stream:
stream.write(raw)
assert target.read_bytes() == raw
rows.append({'path': f'{label}/{relative.as_posix()}', 'source': str(path.relative_to(root)), 'bytes': len(raw), 'sha256': hashlib.sha256(raw).hexdigest()})
for label in ['profile/Desktop', 'profile/Documents', 'profile/AppData/Local', 'profile/AppData/Roaming', 'temp', 'evidence']:
(trial / label).mkdir(parents=True, exist_ok=True)
(trial / 'preparation.json').write_text(json.dumps({'utc': datetime.now(timezone.utc).isoformat(), 'files': rows}, indent=2) + '\n', encoding='utf-8', newline='\n')
print(json.dumps({'trial': str(trial), 'identical_fixture_files': len(rows)}))
elif mode == 'launch':
assert (trial / 'preparation.json').is_file()
assert not (trial / 'launch.json').exists()
stage = Path(sys.argv[2]).resolve()
assert stage.is_relative_to(root / '.local/native-qa')
record = json.loads((stage / 'manifest.json').read_bytes())
assert record['source'] == sys.argv[3], 'Require explicitly identified acceptance source'
for gate in ['full', 'clippy', 'gui']:
result = record['results'][gate]
assert result['exit'] == 0 and not result['changed_inputs']
assert result['source'] == record['source'], 'Require same-source gates for this production correction'
for name in ['arcadia-studio.exe', 'libunwind.dll']:
raw = (stage / name).read_bytes()
assert len(raw) == record['artifacts'][name]['bytes']
assert hashlib.sha256(raw).hexdigest() == record['artifacts'][name]['sha256']
env = {key: os.environ[key] for key in ['SystemRoot', 'WINDIR', 'SystemDrive', 'ProgramData', 'ALLUSERSPROFILE', 'PUBLIC', 'COMSPEC', 'PATHEXT', 'NUMBER_OF_PROCESSORS', 'PROCESSOR_ARCHITECTURE', 'OS'] if key in os.environ}
profile = trial / 'profile'
windows = Path(env['SystemRoot'])
env.update({'PATH': os.pathsep.join(map(str, [stage, windows / 'System32', windows])), 'USERPROFILE': str(profile), 'LOCALAPPDATA': str(profile / 'AppData/Local'), 'APPDATA': str(profile / 'AppData/Roaming'), 'TEMP': str(trial / 'temp'), 'TMP': str(trial / 'temp')})
with (trial / 'stdout.log').open('xb') as out, (trial / 'stderr.log').open('xb') as err:
process = subprocess.Popen([str(stage / 'arcadia-studio.exe')], cwd=trial, env=env, stdin=subprocess.DEVNULL, stdout=out, stderr=err, creationflags=subprocess.CREATE_NO_WINDOW)
launch = {'utc': datetime.now(timezone.utc).isoformat(), 'pid': process.pid, 'source': record['source'], 'binary': str(stage / 'arcadia-studio.exe'), 'sha256': record['artifacts']['arcadia-studio.exe']['sha256'], 'private_profile': str(profile)}
(trial / 'launch.json').write_text(json.dumps(launch, indent=2) + '\n', encoding='utf-8', newline='\n')
print(json.dumps(launch), flush=True)
code = process.wait()
closure = {'utc': datetime.now(timezone.utc).isoformat(), 'pid': process.pid, 'exit': code}
(trial / 'closure.json').write_text(json.dumps(closure, indent=2) + '\n', encoding='utf-8', newline='\n')
print(json.dumps(closure), flush=True)
sys.exit(code)
else:
raise SystemExit('Expected prepare or launch STAGED_DIRECTORY EXPECTED_SOURCE')