Files
ARCADIA-Studio/harness/experiments/navigation-prerelease-20260911/initial-failed/readback.py
T

106 lines
6.2 KiB
Python

"""Read back the produced ZIP/stage/source without launching either executable."""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import stat
import sys
import zipfile
from prepare import BASE, SOURCE, VERSION, TARGET, COMMIT, SERVER_SHA, DLL_SHA, ordinary, sha, load, record, git, relative, historical_inputs, HISTORY
def main():
assert not (BASE / 'package-readback.json').exists()
preparation = load(BASE / 'preparation.json')
terminal = load(BASE / 'package2-result.json')
assert terminal['success'] is True and terminal['metadata_calls'] == 1
assert preparation['base_commit'] == COMMIT and preparation['version'] == VERSION and preparation['target'] == TARGET
name = f'ArcadiaStudio-{VERSION}-{TARGET}'
archive_path = BASE / 'packages' / (name + '.zip')
stage = BASE / 'packages' / name
assert Path(terminal['archive']).resolve() == archive_path.resolve()
assert terminal['archive_sha256'] == sha(archive_path) and terminal['archive_bytes'] == archive_path.stat().st_size
actual = {}
with zipfile.ZipFile(archive_path) as archive:
entries = archive.infolist()
assert entries and len({e.filename.casefold() for e in entries}) == len(entries)
for entry in entries:
assert entry.filename.startswith(name + '/') and not entry.is_dir()
path = relative(entry.filename[len(name) + 1:])
assert not (entry.flag_bits & 1) and not stat.S_ISLNK(entry.external_attr >> 16)
assert entry.date_time == (2000, 1, 1, 0, 0, 0)
with archive.open(entry) as stream:
checksum = hashlib.file_digest(stream, 'sha256').hexdigest()
assert sha(stage / path) == checksum and (stage / path).stat().st_size == entry.file_size
actual[path] = {'sha256': checksum, 'bytes': entry.file_size}
files = {ordinary(p).relative_to(stage).as_posix() for p in stage.rglob('*') if p.is_file()}
assert set(actual) == files
sums = {}
for line in (stage / 'SHA256SUMS.txt').read_text(encoding='utf-8').splitlines():
checksum, path = line.split(' ', 1)
assert relative(path) not in sums
sums[path] = checksum
assert set(sums) == set(actual) - {'SHA256SUMS.txt'}
assert all(actual[p]['sha256'] == h for p, h in sums.items())
companion = (archive_path.with_suffix('.zip.sha256')).read_text(encoding='utf-8').strip().split()
assert companion == [terminal['archive_sha256'], archive_path.name]
for item in preparation['artifacts']:
p = Path(item['destination']).name
assert actual[p]['sha256'] == sha(BASE / item['destination']) == item['sha256']
assert actual['arcadia-server.exe']['sha256'] == SERVER_SHA and actual['libunwind.dll']['sha256'] == DLL_SHA
assert git(SOURCE, 'rev-parse', 'HEAD').decode().strip() == preparation['reconstruction_commit']
assert not git(SOURCE, 'status', '--porcelain').strip()
for mapping in (preparation['gui_inputs'], preparation['shared_non_cargo_inputs']):
for path, checksum in mapping.items():
assert sha(SOURCE / path) == checksum
# This comparison deliberately uses the old server tar, never GUI source.
assert historical_inputs() == preparation['historical_server_inputs']
for item in load(BASE / 'evidence-manifest.json'):
assert sha(BASE / item['destination']) == item['sha256'] and (BASE / item['destination']).stat().st_size == item['bytes']
package = load(stage / 'package.json')
assert package['version'] == VERSION and package['target'] == TARGET
assert package['build_evidence']['evidence'] == load(BASE / 'build-evidence.json')
for path, checksum in package['input_sha256'].items():
assert actual[path]['sha256'] == checksum
notices = load(stage / 'dependency-notices.json')
previous = load(HISTORY / 'prior-dependency-notices.json')
def registry_key(item):
return item['name'], item['version'], item['source'], item['registry_checksum']
collected = {registry_key(x): x for x in notices['packages'] if x['source']}
covered = 0
for old in previous['packages']:
if not old['source']:
continue
current = collected[registry_key(old)]
assert old['license'] == current['license']
assert set(old['features']).issubset(current['features'])
# Exact recorded notice files must remain in the actual ZIP. Current
# metadata does not substitute for the historical server's provenance.
new_notices = {(n['path'], n['sha256']) for n in current['notices']}
for item in old['notices']:
assert (item['path'], item['sha256']) in new_notices
assert actual[item['path']]['sha256'] == item['sha256']
assert old['embedded_fonts'] == current['embedded_fonts']
for font in old['embedded_fonts']:
for item in font['notices']:
assert actual[item['path']]['sha256'] == item['sha256']
covered += 1
assert covered == preparation['historical_registry_count']
docs = load(stage / 'documentation-check.json')
result = {'atUtc': datetime.now(timezone.utc).isoformat(), 'status': 'PASS', 'archive_sha256': sha(archive_path),
'archive_bytes': archive_path.stat().st_size, 'file_count': len(actual), 'files': actual,
'manifest_entries': len(sums), 'source_commit': preparation['reconstruction_commit'],
'gui_inputs_verified': len(preparation['gui_inputs']), 'historical_server_inputs_verified': len(preparation['historical_server_inputs']),
'historical_server_comparison': preparation['historical_server_comparison'],
'historical_registry_packages_covered': covered, 'notice_roots': notices['root_packages'], 'notice_package_count': len(notices['packages']),
'documentation_check': docs, 'binary_imports': package['imports'],
'scope': 'Producer packaging readback only; no application launch or independent/native acceptance.'}
record(BASE / 'package-readback.json', result)
print(json.dumps({k: v for k, v in result.items() if k != 'files'}, ensure_ascii=False, indent=2))
if __name__ == '__main__':
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
main()