Preserve raw include diagnostic provenance and derive bands from the existing shaped editor. Retain deferred tooltip paragraphs through rendering. Include source, regression, failure and native-trial evidence; corrected native acceptance remains pending.
99 lines
8.6 KiB
Python
99 lines
8.6 KiB
Python
"""Gate-bound read-only Windows fixture lint. Prepared; parent supplies closed pins."""
|
|
from pathlib import Path
|
|
from datetime import datetime, timezone
|
|
import argparse, hashlib, json, os, re, shutil, subprocess, sys, zipfile
|
|
BASE = Path(__file__).resolve().parent
|
|
DLL_SHA = 'f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4'
|
|
def now(): return datetime.now(timezone.utc).isoformat()
|
|
def ident(p):
|
|
raw = p.read_bytes()
|
|
return {'bytes': len(raw), 'sha256': hashlib.sha256(raw).hexdigest()}
|
|
def read(p): return json.loads(p.read_text(encoding='utf-8'))
|
|
def require(ok, message):
|
|
if not ok: raise ValueError(message)
|
|
def pin(p, sha):
|
|
require(p.is_absolute() and re.fullmatch('[0-9a-f]{64}', sha), 'absolute path and SHA256 required')
|
|
require(ident(p)['sha256'] == sha, f'pin mismatch: {p}')
|
|
def tree(p): return {f.relative_to(p).as_posix(): ident(f) for f in sorted(p.rglob('*')) if f.is_file()}
|
|
def gate(path, sha):
|
|
pin(path, sha); r = read(path); d = path.parent
|
|
require(r['exitCode'] == 0 and r['inputsUnchanged'] is True and r['finish'], 'completed successful unchanged-source gate required')
|
|
for name, key in [('inputs.json','inputs'), ('inputs.zip','archive'), ('stdout.log','stdout'), ('stderr.log','stderr')]:
|
|
require(ident(d/name) == r[key], f'gate record mismatch: {name}')
|
|
rows = read(d/'inputs.json')
|
|
require(len(rows) == r['inputCount'], 'source count mismatch')
|
|
with zipfile.ZipFile(d/'inputs.zip') as z:
|
|
require(len(z.namelist()) == len(rows) and set(z.namelist()) == set(rows), 'exact input membership required')
|
|
for name, expected in rows.items():
|
|
raw = z.read(name)
|
|
require({'bytes':len(raw),'sha256':hashlib.sha256(raw).hexdigest()} == expected, 'input ZIP byte/CRC binding failed')
|
|
return r, rows
|
|
|
|
def main():
|
|
ap=argparse.ArgumentParser()
|
|
for name in ['cli','build-result','build-sha','source-result','source-sha','label']: ap.add_argument('--'+name, required=True)
|
|
a=ap.parse_args(); require(os.name == 'nt', 'Windows-only evaluator')
|
|
require(re.fullmatch('current-oracle-[a-z0-9-]+', a.label), 'fresh bounded output label required')
|
|
binary=Path(a.cli); require(binary.is_absolute() and binary.suffix.lower()=='.exe', 'preserved Windows CLI path required')
|
|
build, rows=gate(Path(a.build_result), a.build_sha)
|
|
source, source_rows=gate(Path(a.source_result), a.source_sha)
|
|
require(rows == source_rows, 'CLI build and completed source acceptance inputs differ')
|
|
require('build' in build['command'] and build['environment']['RUSTUP_TOOLCHAIN']=='stable-x86_64-pc-windows-gnullvm', 'actual Windows build receipt required, not a test binary')
|
|
matches=[v for v in build['artifacts'] if v['cargo']['target']['name']=='arcadia' and v['cargo']['target']['kind']==['bin'] and v['cargo']['profile']['test'] is False and v['cargo']['executable']]
|
|
require(len(matches)==1, 'exactly one non-test arcadia Cargo artifact required')
|
|
artifact=matches[0]; before=ident(binary); require(before==artifact['readback'], 'preserved CLI differs from Cargo artifact readback')
|
|
require(Path(artifact['cargo']['manifest_path']).resolve()==(Path(build['cwd'])/'crates/oa-cli/Cargo.toml').resolve() and 'crates/oa-cli/Cargo.toml' in rows, 'artifact must belong to captured oa-cli crate')
|
|
if '--target' in build['command']: require(build['command'][build['command'].index('--target')+1]=='x86_64-pc-windows-gnullvm', 'non-Windows target rejected')
|
|
raw_artifacts=[]
|
|
for line in (Path(a.build_result).parent/'stdout.log').read_text(encoding='utf-8').splitlines():
|
|
try: value=json.loads(line)
|
|
except json.JSONDecodeError: continue
|
|
if isinstance(value,dict) and value.get('reason')=='compiler-artifact': raw_artifacts.append(value)
|
|
require(artifact['cargo'] in raw_artifacts, 'artifact is not present in bound raw Cargo output')
|
|
evidence_before={str(p):ident(p) for result in [Path(a.build_result),Path(a.source_result)] for p in [result,*[result.parent/n for n in ['inputs.json','inputs.zip','stdout.log','stderr.log']]]}
|
|
dll=BASE/'runtime/libunwind.dll'; require(ident(dll)['sha256']==DLL_SHA,'runtime DLL mismatch')
|
|
fixture_before=tree(BASE/'fixtures')
|
|
require(set(fixture_before)=={'missing-include/project.arcadia','functions-to-discover.arcadia'}, 'unexpected fixture membership')
|
|
out=BASE/a.label; out.mkdir()
|
|
record={'started':now(),'scope':'Current source-bound Windows CLI lint of saved fixture bytes only; no GUI, unsaved model, Linux or release acceptance.', 'helper':ident(Path(__file__)), 'binaryPath':str(binary),'binary':before,'artifact':artifact,'buildResult':{'path':a.build_result,'sha256':a.build_sha},'sourceResult':{'path':a.source_result,'sha256':a.source_sha},'sourceFiles':len(rows),'sourceInputs':build['inputs'],'fixtures':fixture_before,'runs':[],'passed':False}
|
|
try:
|
|
for name in ['missing','complete','cwd','profile','tmp']: (out/name).mkdir()
|
|
for name in ['missing','complete']: shutil.copyfile(BASE/'fixtures/missing-include/project.arcadia',out/name/'project.arcadia')
|
|
shutil.copyfile(BASE/'fixtures/functions-to-discover.arcadia',out/'complete/functions.arcadia')
|
|
prepared={name:tree(out/name) for name in ['missing','complete']}
|
|
inherited={k.upper():v for k,v in os.environ.items()}; system=inherited['SYSTEMROOT']
|
|
env={'SystemRoot':system,'WINDIR':system,'SystemDrive':inherited['SYSTEMDRIVE'],'PATH':str(binary.parent)+';'+str(dll.parent)+';'+system+'\\System32;'+system,'USERPROFILE':str(out/'profile'),'TEMP':str(out/'tmp'),'TMP':str(out/'tmp')}
|
|
record['environment']=env; record['preparedFixtures']=prepared
|
|
for label, expected in [('missing',3),('complete',0)]:
|
|
argv=[str(binary),'lint',str(out/label/'project.arcadia'),'--json']; run={'label':label,'started':now(),'command':argv,'expectedExitCode':expected}; record['runs'].append(run)
|
|
with (out/(label+'.stdout.json')).open('xb') as stdout, (out/(label+'.stderr.log')).open('xb') as stderr:
|
|
try:
|
|
result=subprocess.run(argv,cwd=out/'cwd',env=env,stdout=stdout,stderr=stderr,timeout=55,creationflags=subprocess.CREATE_NO_WINDOW)
|
|
run['exitCode']=result.returncode
|
|
except (OSError,subprocess.TimeoutExpired) as error:
|
|
run['failure']=repr(error); raise
|
|
finally: run['finished']=now()
|
|
run['stdout']=ident(out/(label+'.stdout.json'));run['stderr']=ident(out/(label+'.stderr.log'))
|
|
report=read(out/(label+'.stdout.json'));run['report']=report
|
|
require(result.returncode==expected and report['exit_code']==expected, 'unexpected CLI/report exit')
|
|
require(report['written'] is False and report['operation']=='check' and report['changes']==[], 'lint must not write or propose rewrites')
|
|
require(report['status']==('invalid' if label=='missing' else 'checked'), 'unexpected lint status')
|
|
require(isinstance(report['diagnostics'],list), 'missing exact diagnostic list')
|
|
if label=='missing': require(any(d['code']=='missing_include' and d['severity']=='error' for d in report['diagnostics']), 'missing include fixture did not produce expected error')
|
|
else: require(report['diagnostics'] and all(d['severity']=='warning' for d in report['diagnostics']), 'complete raw fixture must retain warnings without errors')
|
|
require(tree(out/label)==prepared[label], 'CLI mutated physical fixture bytes')
|
|
record['passed']=True
|
|
except Exception as error: record['failure']=repr(error)
|
|
finally:
|
|
record['finished']=now();record['binaryUnchanged']=ident(binary)==before;record['originalFixturesUnchanged']=tree(BASE/'fixtures')==fixture_before;record['dllUnchanged']=ident(dll)['sha256']==DLL_SHA
|
|
record['evidenceUnchanged']=all(Path(p).is_file() and ident(Path(p))==v for p,v in evidence_before.items())
|
|
record['passed']=record['passed'] and record['binaryUnchanged'] and record['originalFixturesUnchanged'] and record['dllUnchanged'] and record['evidenceUnchanged']
|
|
for run in record['runs']:
|
|
for suffix,key in [('.stdout.json','stdout'),('.stderr.log','stderr')]:
|
|
p=out/(run['label']+suffix)
|
|
if p.exists(): run[key]=ident(p)
|
|
with (out/'result.json').open('x',encoding='utf-8',newline='\n') as f: json.dump(record,f,indent=2);f.write('\n')
|
|
print(json.dumps({'output':str(out),'passed':record['passed'],'receipt':ident(out/'result.json')}),flush=True)
|
|
return 0 if record['passed'] else 1
|
|
if __name__=='__main__': sys.exit(main())
|