import base64
import gzip
import io
import json
from pathlib import Path
import tarfile
import remote

ROOT=Path(__file__).resolve().parent
manifest=json.loads((ROOT/'candidate-manifest.json').read_text())
buf=io.BytesIO()
with tarfile.open(fileobj=buf,mode='w:gz') as archive:
    for p in sorted((ROOT/'build-context').rglob('*')):
        if p.is_file():archive.add(p,arcname=str(p.relative_to(ROOT/'build-context')),recursive=False)
payload=base64.b64encode(buf.getvalue()).decode()
code='''
import base64, hashlib, io, json, os, shutil, subprocess, sys, tarfile
from pathlib import Path
sys.path.insert(0,'/usr/local/lib/contextra-maintenance/portable-20261006T192646')
import compose_backend as cb, server_context as server
server.verify_tools();server.guard_disks()
a,site,cp,approved,live,source=cb.inspect()
assert a['transaction']=='storage-20261007T112302'
assert approved==live
assert live['services']['analytics']['image']==MANIFEST['base_image']
assert shutil.disk_usage('/').free>17*1024**3
mem=dict(line.split(':',1) for line in Path('/proc/meminfo').read_text().splitlines())
assert int(mem['MemAvailable'].split()[0])*1024>8*1024**3
root=Path('/storage/contextra/deploy/event-time-fallback-20261013-r01')
if root.exists():
    previous=json.loads((root/'candidate-manifest.json').read_text())
    assert previous['files']==MANIFEST['files'] and previous['base_image']==MANIFEST['base_image']
else:root.mkdir(mode=0o700,parents=True)
with tarfile.open(fileobj=io.BytesIO(base64.b64decode(PAYLOAD)),mode='r:gz') as t:
    for m in t.getmembers():
        p=Path(m.name)
        assert m.isfile() and not p.is_absolute() and '..' not in p.parts
        dest=root/p;dest.parent.mkdir(parents=True,exist_ok=True)
        dest.write_bytes(t.extractfile(m).read())
(root/'candidate-manifest.json').write_text(json.dumps(MANIFEST,indent=2))
for name,info in MANIFEST['files'].items():
    assert hashlib.sha256((root/'hotfix'/name).read_bytes()).hexdigest()==info['candidate_sha256']
base_tag='contextra-hotfix-base:ai2-4648079cdd03'
subprocess.run(['docker','tag',MANIFEST['base_image'],base_tag],check=True,timeout=30)
assert json.loads(subprocess.check_output(['docker','image','inspect',base_tag]))[0]['Id']==MANIFEST['base_image']
p=root/'Dockerfile';p.write_text(p.read_text().replace(MANIFEST['base_image'],base_tag))
env=dict(os.environ,DOCKER_BUILDKIT='0')
attempt=len(list(root.glob('build-*.log')))+1
log_path=root/('build-%s.log'%attempt)
with log_path.open('xb') as log:
    result=subprocess.run(['docker','build','--pull=false','--network=none','--memory','4g',
        '--cpu-period','100000','--cpu-quota','200000','-t',MANIFEST['tag'],str(root)],
        stdout=log,stderr=subprocess.STDOUT,timeout=600,env=env)
print(json.dumps({'returncode':result.returncode,'root':str(root),
    'log_tail':log_path.read_text(errors='replace')[-7000:]}))
if result.returncode==0:
    row=json.loads(subprocess.check_output(['docker','image','inspect',MANIFEST['tag']]))[0]
    (root/'image.json').write_text(json.dumps({'Id':row['Id'],'tag':MANIFEST['tag']},indent=2))
    print(json.dumps({'image_id':row['Id'],'tag':MANIFEST['tag']}))
'''
code='MANIFEST='+repr(manifest)+'\nPAYLOAD='+repr(payload)+'\n'+code
out=remote.run(code,root=True,timeout=660)
remote.save('build-result-%s.txt'%(len(list(ROOT.glob('build-result*.txt')))+1),out)
print(out.decode())
