98 lines
5.1 KiB
Python
98 lines
5.1 KiB
Python
"""Serialized, stamped core-only experiment gates; never uses root target artifacts."""
|
|
from pathlib import Path
|
|
from datetime import datetime, timezone
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import time
|
|
|
|
worktree = Path(__file__).resolve().parent.parent
|
|
root = worktree.parent.parent
|
|
source = '7e25b3d3907693cbbf03bb51b5e08b01d743a934'
|
|
out = worktree / '.local' / 'gate-7e25b3d-20260912-01'
|
|
target = worktree / '.local' / 'target-7e25b3d-01'
|
|
assert not out.exists() and not target.exists(), 'Never overwrite identified gate outputs'
|
|
assert target.resolve().is_relative_to(worktree / '.local')
|
|
assert target.resolve() != (root / 'target').resolve()
|
|
out.mkdir()
|
|
|
|
def utc():
|
|
return datetime.now(timezone.utc).isoformat()
|
|
|
|
def sha(path):
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
def checked(args):
|
|
return subprocess.check_output(args, cwd=worktree, stderr=subprocess.STDOUT).decode().strip()
|
|
|
|
def write(name, record):
|
|
(out / name).write_text(json.dumps(record, indent=2) + '\n', encoding='utf-8', newline='\n')
|
|
|
|
assert checked(['git', 'rev-parse', 'HEAD']) == source
|
|
assert not checked(['git', 'status', '--porcelain', '--untracked-files=no'])
|
|
tracked = subprocess.check_output(['git', 'ls-files', '-z'], cwd=worktree).decode().split('\0')
|
|
inputs = [name for name in tracked if name and (
|
|
name.startswith(('crates/oa-core/', '.cargo/'))
|
|
or name in ['Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml']
|
|
or name.endswith('/Cargo.toml'))]
|
|
before = {name: sha(worktree / name) for name in inputs}
|
|
protected_paths = [root / 'target/debug/deps/liboa_core-145180297aa692e4.rlib', root / '.local/toolchain/env.ps1']
|
|
protected = {str(path): sha(path) for path in protected_paths}
|
|
env = os.environ.copy()
|
|
env['CARGO_TARGET_DIR'] = str(target)
|
|
env['CARGO_BUILD_JOBS'] = '1'
|
|
tools = {
|
|
'cargo': checked(['cargo', '-V']),
|
|
'rustc': checked(['rustc', '-Vv']),
|
|
'active_toolchain': checked(['rustup', 'show', 'active-toolchain']),
|
|
'paths': {},
|
|
}
|
|
for tool in ['cargo', 'rustc']:
|
|
path = Path(checked(['rustup', 'which', tool]))
|
|
tools['paths'][tool] = {'path': str(path), 'sha256': sha(path)}
|
|
linker = Path(shutil.which('clang.exe'))
|
|
tools['paths']['linker'] = {'path': str(linker), 'sha256': sha(linker)}
|
|
write('inputs.json', {'source': source, 'start': utc(), 'worktree': str(worktree),
|
|
'target': str(target), 'inputs': before, 'protected': protected, 'toolchain': tools,
|
|
'environment': {key: env.get(key) for key in [
|
|
'CARGO_HOME', 'RUSTUP_HOME', 'CARGO_TARGET_DIR', 'CARGO_BUILD_JOBS',
|
|
'CARGO_TARGET_X86_64_PC_WINDOWS_GNULLVM_LINKER']}})
|
|
|
|
gates = [
|
|
('comparison', ['cargo', 'test', '--locked', '--jobs', '1', '-p', 'oa-core', '--test', 'experimental_port_policy', '--', '--nocapture', '--test-threads=1']),
|
|
('allocator', ['cargo', 'test', '--locked', '--jobs', '1', '-p', 'oa-core', '--lib', 'port_capacity_experiment', '--', '--nocapture', '--test-threads=1']),
|
|
('port-anchors', ['cargo', 'test', '--locked', '--jobs', '1', '-p', 'oa-core', '--test', 'port_anchors', '--', '--nocapture', '--test-threads=1']),
|
|
]
|
|
results = []
|
|
for name, command in gates:
|
|
started = utc()
|
|
clock = time.monotonic()
|
|
print(f'START {name}: {command}', flush=True)
|
|
with (out / (name + '.log')).open('wb') as log:
|
|
result = subprocess.run(command, cwd=worktree, env=env, stdout=log, stderr=subprocess.STDOUT)
|
|
changed = [name for name, digest in before.items() if sha(worktree / name) != digest]
|
|
protected_changed = [name for name, digest in protected.items() if sha(Path(name)) != digest]
|
|
artifacts = []
|
|
for folder in [target / 'debug/deps', target / 'x86_64-pc-windows-gnullvm/debug/deps']:
|
|
if folder.is_dir():
|
|
for pattern in ['liboa_core-*.rlib', 'experimental_port_policy-*.exe', 'oa_core-*.exe', 'port_anchors-*.exe']:
|
|
for path in folder.glob(pattern):
|
|
artifacts.append({'path': str(path.relative_to(worktree)), 'bytes': path.stat().st_size, 'sha256': sha(path)})
|
|
record = {'source': source, 'name': name, 'command': command, 'start': started, 'end': utc(),
|
|
'elapsed_seconds': round(time.monotonic() - clock, 3), 'exit': result.returncode,
|
|
'changed_inputs': changed, 'changed_protected': protected_changed,
|
|
'head_after': checked(['git', 'rev-parse', 'HEAD']), 'log_sha256': sha(out / (name + '.log')),
|
|
'artifacts': artifacts}
|
|
write(name + '.json', record)
|
|
results.append(record)
|
|
print(f'END {name}: exit={result.returncode} seconds={record["elapsed_seconds"]} changed={changed} protected_changed={protected_changed}', flush=True)
|
|
if result.returncode or changed or protected_changed or record['head_after'] != source:
|
|
break
|
|
|
|
write('summary.json', {'source': source, 'end': utc(), 'results': results,
|
|
'all_passed': len(results) == len(gates) and all(r['exit'] == 0 and not r['changed_inputs'] and not r['changed_protected'] for r in results)})
|
|
print(f'RECEIPTS {out}', flush=True)
|
|
raise SystemExit(0 if len(results) == len(gates) and all(r['exit'] == 0 and not r['changed_inputs'] and not r['changed_protected'] for r in results) else 1)
|