72 lines
4.6 KiB
Python
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')
|