66 lines
3.1 KiB
Python
66 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Finish isolated Classic prerequisites. Never starts a realm or edits live data."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import pathlib
|
|
import shutil
|
|
import subprocess
|
|
import time
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--root', required=True)
|
|
args = parser.parse_args()
|
|
root = pathlib.Path(args.root).resolve()
|
|
|
|
def status(stage, **extra):
|
|
value = {'at': time.time(), 'stage': stage, 'cutover': False, **extra}
|
|
temporary = root / 'prerequisites-status.tmp'
|
|
temporary.write_text(json.dumps(value, indent=2))
|
|
temporary.replace(root / 'prerequisites-status.json')
|
|
print(stage, flush=True)
|
|
|
|
def run(*command):
|
|
return subprocess.check_output(command, text=True).strip()
|
|
|
|
try:
|
|
status('waiting for Classic compilation and client transfer')
|
|
deadline = time.monotonic() + 6 * 3600
|
|
while True:
|
|
state = json.loads(run('docker', 'inspect', 'azerothai-classic-build'))[0]['State']
|
|
if not state['Running']:
|
|
if state['ExitCode'] != 0:
|
|
raise RuntimeError('Classic build failed; inspect docker logs azerothai-classic-build')
|
|
break
|
|
if time.monotonic() > deadline:
|
|
raise RuntimeError('Classic build exceeded six-hour observation window')
|
|
time.sleep(30)
|
|
manifest = json.loads((root / 'client-manifest.json').read_text())
|
|
while not all((root / 'client' / f).exists() and (root / 'client' / f).stat().st_size == v['size'] for f, v in manifest.items()):
|
|
if time.monotonic() > deadline:
|
|
raise RuntimeError('Client transfer incomplete after six hours')
|
|
time.sleep(30)
|
|
status('verifying transferred client archives')
|
|
for filename, expected in manifest.items():
|
|
with (root / 'client' / filename).open('rb') as stream:
|
|
actual = hashlib.file_digest(stream, 'sha256').hexdigest()
|
|
if actual != expected['sha256']:
|
|
raise RuntimeError('Client archive checksum mismatch: ' + filename)
|
|
if shutil.disk_usage(root).free < 8 * 1024**3:
|
|
raise RuntimeError('Less than 8 GiB free for extracted Vanilla data')
|
|
output = root / 'extracted'; output.mkdir(exist_ok=True)
|
|
status('extracting Vanilla maps and navigation data; realm not switched')
|
|
with (root / 'data-extraction.log').open('w') as log:
|
|
subprocess.run([
|
|
'docker', 'run', '--name', 'azerothai-classic-extract', '--cpus', '2', '--memory', '4g',
|
|
'--user', '1000:1000', '-i', '-v', str(root) + ':/work', '-w', '/work/run/bin/tools',
|
|
'azerothai-classic-builder:local', 'bash', './ExtractResources.sh', '/work/client', '/work/extracted'
|
|
], input='y\n2\n\nn\nn\n\n', text=True, stdout=log, stderr=subprocess.STDOUT, check=True)
|
|
counts = {name: sum(1 for f in (output / name).rglob('*') if f.is_file()) for name in ('dbc', 'maps', 'vmaps', 'mmaps')}
|
|
if not all(counts.values()):
|
|
raise RuntimeError('An extracted data directory is empty: ' + str(counts))
|
|
status('prerequisites extracted; database, connector, roster and gameplay validation remain', files=counts)
|
|
except Exception as error:
|
|
status('blocked', error=str(error))
|
|
raise
|