Files
ARCADIA-Studio/harness/prerelease37-windows-gates-20260924/attempts-8dd/v2-final.py
T

290 lines
18 KiB
Python

"""Prepared Windows workspace gates. No default candidate; never launches artifacts."""
from pathlib import Path
import argparse
import json
import os
import re
import shutil
import subprocess
import sys
import tomllib
sys.dont_write_bytecode = True
from run import clean, environment, inputs, now, read, record, save, verify_environment
ROOT = Path(__file__).resolve().parent
TOOLCHAIN = ROOT.parent / 'toolchain'
HOST = 'x86_64-pc-windows-gnullvm'
TOOLCHAIN_NAME = 'stable-' + HOST
RUNTIME = TOOLCHAIN / 'llvm-mingw-20260908-ucrt-x86_64/bin/libunwind.dll'
RUNTIME_SHA = 'aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a'
def manifest(path):
with path.open('rb') as stream:
return tomllib.load(stream)
def example(repo, name):
core = repo / 'crates/oa-core'
cfg = manifest(core / 'Cargo.toml')
declared = [x for x in cfg.get('example', []) if x.get('name') == name]
if len(declared) > 1:
raise RuntimeError('Duplicate example declaration')
if declared:
path = core / declared[0].get('path', 'examples/' + name + '.rs')
elif cfg['package'].get('autoexamples', True):
choices = [p for p in (core / ('examples/' + name + '.rs'), core / ('examples/' + name + '/main.rs')) if p.is_file()]
if len(choices) != 1:
return None
path = choices[0]
else:
return None
if not path.resolve().is_relative_to(repo) or not path.is_file():
raise RuntimeError('Example source missing or outside candidate')
return record(path)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--repo', required=True, type=Path)
parser.add_argument('--source-commit', required=True)
parser.add_argument('--attempt', required=True)
parser.add_argument('--profile', required=True, choices=('dev', 'release'))
parser.add_argument('--snapshot-example', choices=('attachment_snapshot',), help='Build only when explicitly requested and present in the candidate')
parser.add_argument('--source-converter-example', help='Explicit reviewed oa-core example name; never reuse an older executable')
args = parser.parse_args()
repo, pin = args.repo.resolve(), args.source_commit
if not re.fullmatch('[0-9a-f]{40}', pin) or not re.fullmatch('final-[0-9]{2}', args.attempt):
raise RuntimeError('Full explicit source pin and fresh final-NN required')
if args.source_converter_example and not re.fullmatch('[A-Za-z0-9_-]+', args.source_converter_example):
raise RuntimeError('Invalid converter example name')
out = ROOT / args.attempt
if out.exists() or not repo.is_dir() or os.name != 'nt':
raise RuntimeError('Fresh output and existing Windows candidate required')
clean(repo, pin)
for package, binary in (('oa-desktop', 'arcadia-studio'), ('oa-server', 'arcadia-server')):
cfg = manifest(repo / ('crates/' + package + '/Cargo.toml'))
if cfg['package']['name'] != package or not any(x.get('name') == binary for x in cfg.get('bin', [])):
raise RuntimeError('Candidate executable contract differs: ' + binary)
target_text = os.environ.get('CARGO_TARGET_DIR', '')
if not target_text or not Path(target_text).is_absolute():
raise RuntimeError('Explicit absolute CARGO_TARGET_DIR required')
target = Path(target_text).resolve()
if os.environ.get('CARGO_BUILD_TARGET'):
raise RuntimeError('Host-native cache required; CARGO_BUILD_TARGET must be unset')
compiler_keys = ('RUSTC', 'RUSTDOC', 'RUSTC_WRAPPER', 'RUSTC_WORKSPACE_WRAPPER',
'CARGO_BUILD_RUSTC', 'CARGO_BUILD_RUSTDOC',
'CARGO_BUILD_RUSTC_WRAPPER', 'CARGO_BUILD_RUSTC_WORKSPACE_WRAPPER')
if any(os.environ.get(key) for key in compiler_keys):
raise RuntimeError('Unreviewed Cargo compiler override; use the verified pinned toolchain')
required_env = {'CARGO_HOME': str((TOOLCHAIN / 'cargo').resolve()),
'RUSTUP_HOME': str((TOOLCHAIN / 'rustup').resolve()),
'RUSTUP_TOOLCHAIN': TOOLCHAIN_NAME, 'CARGO_BUILD_JOBS': '1', 'CARGO_INCREMENTAL': '0'}
for key, value in required_env.items():
actual = os.environ.get(key, '')
if (Path(actual).resolve() != Path(value).resolve()) if key.endswith('_HOME') else actual != value:
raise RuntimeError('Required environment differs: ' + key)
if record(RUNTIME)['sha256'] != RUNTIME_SHA:
raise RuntimeError('Admitted MinGW runtime differs')
tools = [TOOLCHAIN / 'env.ps1', RUNTIME, ROOT / 'run.py', ROOT / 'final.py', ROOT / 'README.md']
actual_bin = TOOLCHAIN / 'rustup/toolchains' / TOOLCHAIN_NAME / 'bin'
for name in ('cargo.exe', 'rustc.exe', 'rustdoc.exe', 'cargo-clippy.exe', 'clippy-driver.exe'):
tools.append(actual_bin / name)
tools.extend(actual_bin.glob('rustc_driver-*.dll'))
for name in ('cargo', 'rustc', 'clang.exe'):
resolved = shutil.which(name)
if not resolved:
raise RuntimeError('Required command unavailable: ' + name)
tools.append(Path(resolved))
rc = os.environ.get('RC')
if not rc:
raise RuntimeError('Explicit windres path required')
tools.append(Path(rc))
configs = []
cargo_home = TOOLCHAIN / 'cargo'
for folder in dict.fromkeys([repo, *repo.parents, cargo_home.resolve()]):
for name in ('config', 'config.toml'):
path = folder / name if folder == cargo_home.resolve() else folder / '.cargo' / name
configs.append(dict(path=str(path), present=path.is_file(), record=record(path) if path.is_file() else None))
if path.is_file():
build = manifest(path).get('build', {})
if 'target' in build:
raise RuntimeError('Host-native cache required; Cargo build.target must be absent: ' + str(path))
if any(key in build for key in ('rustc', 'rustdoc', 'rustc-wrapper', 'rustc-workspace-wrapper')):
raise RuntimeError('Unreviewed Cargo compiler configuration: ' + str(path))
frozen = dict(environment=environment(), target=HOST, profile=args.profile,
profile_scope='selected build profile only', target_mode='implicit verified rustc host',
target_directory=str(target), cargo_configs=configs,
bound_files=[record(p) for p in dict.fromkeys(p.resolve() for p in tools)])
before = inputs(repo, pin)
common = ['--locked', '--offline']
build_flags = [*common, *(['--release'] if args.profile == 'release' else [])]
commands = [('toolchain', ['rustc', '--version', '--verbose'], None),
('cargo-version', ['cargo', '--version', '--verbose'], None),
('workspace-test-build', ['cargo', 'test', '--locked', '--offline', '--workspace', '--no-run', '--message-format=json-render-diagnostics'], None),
('workspace-tests', ['cargo', 'test', '--locked', '--offline', '--workspace', '--no-fail-fast', '--', '--test-threads=1'], None),
('workspace-clippy', ['cargo', 'clippy', '--locked', '--offline', '--workspace', '--all-targets', '--', '-D', 'warnings'], None)]
identified = {}
for package, binary in (('oa-desktop', 'arcadia-studio'), ('oa-server', 'arcadia-server')):
commands.append((binary + '-build', ['cargo', 'build', '-p', package, '--bin', binary, *build_flags, '--message-format=json-render-diagnostics'], binary))
snapshot = example(repo, 'attachment_snapshot') if args.snapshot_example else None
if args.snapshot_example and snapshot is None:
raise RuntimeError('Requested attachment_snapshot example is absent')
if snapshot:
identified['attachment_snapshot'] = snapshot
if args.source_converter_example:
converter = example(repo, args.source_converter_example)
if converter is None or args.source_converter_example in ('attachment_snapshot', 'arcadia-studio', 'arcadia-server'):
raise RuntimeError('Explicit distinct converter example not present')
identified[args.source_converter_example] = converter
for name in identified:
commands.append((name + '-build', ['cargo', 'build', '-p', 'oa-core', '--example', name, *build_flags, '--message-format=json-render-diagnostics'], name))
out.mkdir()
(out / 'artifacts').mkdir()
save(out / 'inputs-before.json', before)
save(out / 'environment.json', frozen)
save(out / 'plan.json', dict(source_commit=pin, repo=str(repo), target=HOST, profile=args.profile,
profile_scope='selected build profile only', target_mode='implicit verified rustc host',
test_profile='default test', clippy_profile='default dev',
commands=commands, examples=identified,
attachment_snapshot_requested=args.snapshot_example,
source_converter_requested=args.source_converter_example,
no_prior_gate_or_binary_reuse=True))
started = now()
steps, outputs, relocations = [], [], []
failure = None
unchanged = False
def preserve(path, build_record=None):
original = record(path)
dest = out / 'artifacts' / path.name
with path.open('rb') as src, dest.open('xb') as dst:
shutil.copyfileobj(src, dst)
copied = record(dest)
if record(path) != original or any(original[k] != copied[k] for k in ('bytes', 'sha256')):
raise RuntimeError('Artifact changed during preservation')
outputs.append(copied)
relocations.append(dict(original=original, preserved=copied, cargo_artifact=build_record))
try:
for name, argv, executable in commands:
command_profile = args.profile if executable else ('test' if name in ('workspace-test-build', 'workspace-tests') else 'dev' if name == 'workspace-clippy' else 'none')
verify_environment(frozen)
if inputs(repo, pin) != before:
raise RuntimeError('Source changed before ' + name)
wrapper = out / (name + '.orchestration.log')
inventory_args = ['--test-inventory', str(out / 'test-artifact-inventory.json')] if name == 'workspace-tests' else []
with wrapper.open('xb') as stream:
proc = subprocess.run([sys.executable, str(ROOT / 'run.py'), '--repo', str(repo),
'--source-commit', pin, '--profile', args.profile, '--command-profile', command_profile, '--output', str(out / name),
'--environment', str(out / 'environment.json'), *inventory_args, '--', *argv], stdout=stream, stderr=subprocess.STDOUT)
result_path = out / name / 'result.json'
result = read(result_path) if result_path.is_file() else None
steps.append(dict(name=name, argv=argv, command_profile=command_profile, runner_exit_code=proc.returncode,
orchestration_log=record(wrapper), result=record(result_path) if result else None))
if proc.returncode or not result or result['exit_code'] != 0 or result['failure'] or not result['inputs_unchanged']:
raise RuntimeError('Gate failed: ' + name)
if result['source_commit'] != pin or result['argv'] != argv or Path(result['cwd']) != repo or result['profile'] != args.profile or result['command_profile'] != command_profile or result['target'] != HOST:
raise RuntimeError('Command attribution differs')
for phase in ('inputs-before.json', 'inputs-after.json'):
if read(out / name / phase) != before:
raise RuntimeError('Source inventory differs in ' + name)
verify_environment(frozen)
if name == 'toolchain':
host_lines = [line for line in (out / name / 'output.log').read_text().splitlines() if line.startswith('host:')]
if host_lines != ['host: ' + HOST]:
raise RuntimeError('Actual rustc host differs')
if name == 'workspace-test-build':
tests = {}
source_by_repo_path = {item['repository_path']: item for item in before}
for line in (out / name / 'output.log').read_text(encoding='utf-8').splitlines():
artifact = json.loads(line)
if artifact.get('reason') != 'compiler-artifact' or artifact.get('profile', {}).get('test') is not True or not artifact.get('executable'):
continue
path = Path(artifact['executable']).resolve()
source = Path(artifact['target']['src_path']).resolve()
if not path.is_relative_to(target) or path.suffix.lower() != '.exe' or not source.is_relative_to(repo):
raise RuntimeError('Unexpected native test artifact/source')
package_manifest = Path(artifact['manifest_path']).resolve()
if not package_manifest.is_relative_to(repo) or not package_manifest.is_file():
raise RuntimeError('Test package manifest unavailable')
admitted_inputs = []
for input_path in (source, package_manifest):
relative_path = input_path.relative_to(repo).as_posix()
admitted = source_by_repo_path.get(relative_path)
if admitted is None or record(input_path) != {key: admitted[key] for key in ('path', 'bytes', 'sha256')}:
raise RuntimeError('Test source/manifest absent from or changed since frozen input map: ' + relative_path)
admitted_inputs.append(admitted)
cfg = manifest(package_manifest)
kind = artifact['target']['kind']
declarations = [cfg.get('lib', {})] if 'lib' in kind else [item for section in ('bin', 'test', 'example', 'bench') for item in cfg.get(section, []) if item.get('name') == artifact['target']['name']]
if any(item.get('harness') is False for item in declarations):
raise RuntimeError('Custom test harness requires separate reviewed listing contract')
item = dict(binary=record(path), cargo_artifact=artifact,
source_input=admitted_inputs[0], package_manifest=admitted_inputs[1])
if str(path) in tests and tests[str(path)] != item:
raise RuntimeError('Conflicting test executable attribution')
tests[str(path)] = item
if not tests:
raise RuntimeError('No native test harness artifacts identified')
save(out / 'test-artifact-inventory.json', dict(source_commit=pin, target=HOST,
build_result=record(result_path), artifacts=[tests[p] for p in sorted(tests)],
scope='Native test harnesses from the no-run build; rustdoc tests remain in the literal Cargo test log. Binaries are hashed, not copied.'))
if executable:
matches = []
for line in (out / name / 'output.log').read_text(encoding='utf-8').splitlines():
item = json.loads(line)
if item.get('reason') == 'compiler-artifact' and item.get('target', {}).get('name') == executable and item.get('executable'):
matches.append(item)
if len(matches) != 1:
raise RuntimeError('One exact Cargo executable artifact required')
artifact = matches[0]
path = Path(artifact['executable']).resolve()
if not path.is_relative_to(target) or path.suffix.lower() != '.exe' or artifact.get('profile', {}).get('test') is not False:
raise RuntimeError('Unexpected executable location/profile')
preserve(path, artifact)
preserve(RUNTIME)
except Exception as error:
failure = str(error)
finally:
errors = []
try:
after = inputs(repo, pin)
save(out / 'inputs-after.json', after)
unchanged = after == before
if not unchanged:
errors.append('Final source differs')
except Exception as error:
errors.append('Final source verification failed: ' + str(error))
try:
verify_environment(frozen)
except Exception as error:
errors.append('Final environment verification failed: ' + str(error))
for item in outputs:
try:
if record(item['path']) != item:
errors.append('Preserved artifact changed')
except Exception as error:
errors.append('Final artifact verification failed: ' + str(error))
failure = '; '.join(filter(None, [failure, *errors])) or None
save(out / 'artifact-relocation.json', dict(source_commit=pin, target=HOST, profile=args.profile, artifacts=relocations))
passed = failure is None and unchanged and len(steps) == len(commands) and len(outputs) == 3 + len(identified)
save(out / 'summary.json', dict(source_commit=pin, repo=str(repo), target=HOST, profile=args.profile,
profile_scope='selected build profile only', target_mode='implicit verified rustc host',
passed=passed, failure=failure, inputs_unchanged=unchanged, started_utc=started, finished_utc=now(),
steps=steps, outputs=outputs, source_manifest=record(out / 'inputs-before.json'),
environment=record(out / 'environment.json'), plan=record(out / 'plan.json'),
relocation=record(out / 'artifact-relocation.json'),
test_artifact_inventory=record(out / 'test-artifact-inventory.json') if (out / 'test-artifact-inventory.json').exists() else None,
test_name_inventory=record(out / 'workspace-tests/test-name-inventory.json') if (out / 'workspace-tests/test-name-inventory.json').exists() else None,
observed_ignores=record(out / 'workspace-tests/observed-ignores.json') if (out / 'workspace-tests/observed-ignores.json').exists() else None,
scope='Fresh literal workspace tests and strict all-target workspace lint; identified builds only. Existing ignored tests remain ignored; native/package/server-launch acceptance is separate.'))
print(json.dumps(dict(summary=str(out / 'summary.json'), passed=passed, failure=failure)))
if not passed:
raise SystemExit(1)
if __name__ == '__main__':
main()