"""Capture exact working inputs and run one serial Windows acceptance attempt.""" import datetime import hashlib import json import os from pathlib import Path import re import shutil import subprocess import sys import time import zipfile ROOT = Path(__file__).resolve().parents[2] BASE = Path(__file__).resolve().parent label = sys.argv[1] if not re.fullmatch(r'attempt-[0-9]+', label): raise RuntimeError('Invalid attempt label') DEST = BASE / label DEST.mkdir() if os.environ.get('CARGO_BUILD_JOBS') != '1': raise RuntimeError('One Windows compiler job required') SCOPES = ['Cargo.toml', 'Cargo.lock', 'crates', 'build', '.cargo', 'docs', 'examples', 'harness/fixtures'] def now(): return datetime.datetime.now(datetime.timezone.utc).isoformat() def sha(path): return hashlib.sha256(path.read_bytes()).hexdigest() def names(): data = subprocess.check_output(['git', 'ls-files', '-z', '--cached', '--others', '--exclude-standard', '--', *SCOPES], cwd=ROOT) return sorted(set(p.decode() for p in data.split(b'\0') if p)) def write(name, value): with (DEST / name).open('x', encoding='utf-8', newline='\n') as output: json.dump(value, output, indent=2) output.write('\n') inputs = [] with zipfile.ZipFile(DEST / 'inputs.zip', 'x', zipfile.ZIP_DEFLATED) as archive: for name in names(): raw = (ROOT / name).read_bytes() archive.writestr(name, raw) inputs.append({'path': name, 'bytes': len(raw), 'sha256': hashlib.sha256(raw).hexdigest()}) def verify(): return names() == [row['path'] for row in inputs] and all(sha(ROOT / row['path']) == row['sha256'] for row in inputs) write('inputs.json', {'atUtc': now(), 'baseCommit': subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=ROOT).decode().strip(), 'archiveSha256': sha(DEST / 'inputs.zip'), 'files': inputs}) write('environment.json', {'atUtc': now(), 'versions': {name: subprocess.check_output(command, cwd=ROOT).decode() for name, command in [('rustc', ['rustc', '-vV']), ('cargo', ['cargo', '-V'])]}, 'variables': {key: os.environ.get(key) for key in ['RUSTUP_HOME', 'CARGO_HOME', 'RUSTUP_TOOLCHAIN', 'CARGO_TARGET_DIR', 'CARGO_BUILD_JOBS', 'CARGO_TARGET_X86_64_PC_WINDOWS_GNULLVM_LINKER']}}) gates = [ ('clippy', ['cargo', 'clippy', '--locked', '--offline', '-p', 'oa-desktop', '--all-targets', '--', '-D', 'warnings']), ('membership', ['cargo', 'test', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--', '--list']), ('focused', ['cargo', 'test', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', 'text_workspace', '--', '--nocapture', '--test-threads=1']), ('tests', ['cargo', 'test', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--', '--test-threads=1']), ('timing', ['cargo', 'test', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', 'app::source_rename::timing_tests::accepted_release1_prepare_commit_sync_and_confirm_timing', '--', '--ignored', '--nocapture', '--test-threads=1']), ('gui', ['cargo', 'build', '--locked', '--offline', '-p', 'oa-desktop', '--bin', 'arcadia-studio', '--message-format=json-render-diagnostics']), ] for name, command in gates: if not verify(): raise RuntimeError('Source inputs changed before gate') print(json.dumps({'starting': name, 'command': command}), flush=True) start, clock = now(), time.perf_counter() with (DEST / (name + '-stdout.log')).open('xb') as out, (DEST / (name + '-stderr.log')).open('xb') as err: result = subprocess.run(command, cwd=ROOT, stdout=out, stderr=err) receipt = {'command': command, 'started': start, 'finished': now(), 'seconds': time.perf_counter() - clock, 'exitCode': result.returncode, 'inputsUnchanged': verify(), 'stdoutSha256': sha(DEST / (name + '-stdout.log')), 'stderrSha256': sha(DEST / (name + '-stderr.log'))} if name == 'membership' and result.returncode == 0: listed = {line.removesuffix(': test') for line in (DEST / 'membership-stdout.log').read_text(encoding='utf-8').splitlines() if line.endswith(': test')} required = { 'app::text_workspace::tests::document_reset_preserves_popup_token_sequence_and_drops_old_menu_authority', 'app::text_workspace::tests::stale_retained_group_can_expand_without_navigating_or_applying_drafts', 'app::text_workspace::lifecycle_tests::saved_new_destination_republishes_current_warning_and_refuses_old_primary_and_related', 'app::text_workspace::lifecycle_tests::invalid_repair_open_clears_prior_presentation_and_publishes_current_problems_preserving_worker', 'views::text_workspace::tests::highlighted_sensor_editor_retains_real_scrolled_buffer_across_panel_rebuilds', 'views::text_workspace::tests::drawer_clamps_to_actual_center_allocation_including_oversize_requests', 'diagnostic_input::tests::same_publication_new_visit_cancels_armed_rows_without_a_hidden_tree_frame', 'app::source_symbols::tests::retained_choices_cannot_replay_after_hide_or_references_problems_references', 'app::text_workspace::tests::queued_group_toggle_cannot_mutate_a_reopened_or_rotated_visit', 'views::text_workspace::summary_visibility_tests::all_reference_exceptions_fit_allocated_width_with_wide_alias_and_large_counts', } receipt['testMembership'] = {'listed': len(listed), 'required': sorted(required), 'missing': sorted(required - listed), 'diagnosticGuardCount': sum(name.startswith('diagnostic_input::tests::') for name in listed)} if required - listed or receipt['testMembership']['diagnosticGuardCount'] != 20: write(name + '-result.json', receipt) raise RuntimeError('New Text workspace test membership differs; shared-target cache output is not accepted') if name == 'gui' and result.returncode == 0: records = [json.loads(line) for line in (DEST / 'gui-stdout.log').read_text(encoding='utf-8').splitlines() if line.startswith('{')] artifacts = [record for record in records if record.get('reason') == 'compiler-artifact' and record.get('target', {}).get('name') == 'arcadia-studio' and record.get('executable')] if len(artifacts) != 1: raise RuntimeError('Expected exactly one desktop artifact') binary = Path(artifacts[0]['executable']) shutil.copyfile(binary, DEST / 'arcadia-studio.exe') if sha(binary) != sha(DEST / 'arcadia-studio.exe'): raise RuntimeError('Desktop preservation differs') receipt['binary'] = {'path': str(binary), 'preserved': str(DEST / 'arcadia-studio.exe'), 'sha256': sha(binary), 'bytes': binary.stat().st_size, 'artifact': artifacts[0]} write(name + '-result.json', receipt) print(json.dumps(receipt), flush=True) if result.returncode or not receipt['inputsUnchanged']: raise SystemExit(result.returncode or 1) write('complete.json', {'atUtc': now(), 'inputsUnchanged': verify(), 'gates': [name for name, _ in gates], 'scope': 'Windows headless tests/lint/timing/build; no native acceptance'})