from pathlib import Path, PurePosixPath
import hashlib,json,tarfile,shutil,os
home=Path('/home/justrep-ssh');web=home/'www'
state=json.loads((home/'journal-pdf-deployment-state.json').read_text())
name=state['name']; archive=home/(name+'.tar.gz');stage=home/(name+'-stage');backup=home/(name+'-backup')
assert hashlib.sha256(archive.read_bytes()).hexdigest()==state['archive_sha256'],'Archive mismatch'
assert hashlib.sha256((web/'index.html').read_bytes()).hexdigest()==state['live_index_sha256'],'The live frontend has changed; rebuild against its latest version'
assert not stage.exists() and not backup.exists(),'Deployment already exists'
stage.mkdir(mode=0o700);backup.mkdir(mode=0o700)
with tarfile.open(archive) as tf:
 for m in tf.getmembers():
  p=PurePosixPath(m.name)
  assert m.isfile() and not p.is_absolute() and '..' not in p.parts,'Unsafe archive entry'
 tf.extractall(stage)
manifest=json.loads((stage/'.deployment-manifest.json').read_text())
for rel,digest in manifest.items():assert hashlib.sha256((stage/rel).read_bytes()).hexdigest()==digest,'Staging mismatch'
paths=sorted(manifest,key=lambda p:(2 if p=='index.html' else 1 if p.endswith('.html') else 0,p))
assert not any(p.startswith(('backend/','potree')) or p=='.htaccess' for p in paths),'Unexpected deployment scope'
replaced=[]
try:
 for rel in paths:
  target=web/rel
  if target.exists():
   saved=backup/rel;saved.parent.mkdir(parents=True,exist_ok=True);shutil.copy2(target,saved)
 for rel in paths:
  target=web/rel;target.parent.mkdir(parents=True,exist_ok=True)
  temporary=target.with_name(target.name+'.journal-pdf-next');shutil.copyfile(stage/rel,temporary);temporary.chmod(0o644);os.replace(temporary,target);replaced.append(rel)
 for rel,digest in manifest.items():assert hashlib.sha256((web/rel).read_bytes()).hexdigest()==digest,'Published file mismatch'
except Exception:
 for rel in reversed(replaced):
  saved=backup/rel;target=web/rel
  if saved.exists():shutil.copy2(saved,target)
  else:target.unlink()
 raise
print('Published',len(paths),'frontend files; backup:',backup)
