"""Stage and compile the bridge, retaining a hash-checked rollback artifact.""" import hashlib import json import shutil import time from pathlib import Path from . import native def stage(): root = native.STATE_ROOT receipt_path = root/'deployment.json' old = json.loads(receipt_path.read_text()) target = Path(old['mod_path']) if native.legacy.manage.digest(target) != old['mod_sha256']: raise RuntimeError('Installed bridge changed outside the devkit') folder = root/'artifacts'/('build-'+str(time.time_ns())) folder.mkdir(parents=True) shutil.copy2(target,folder/'previous.ws') native.legacy.atomic_json(folder/'previous-receipt.json',old) source = native.legacy.bridge_source(native.ROOT) build = hashlib.sha256(source.encode()).hexdigest()[:16] target.write_text(source.replace('@@BUILD@@',build)) updated = dict(old,build=build,mod_sha256=native.legacy.manage.digest(target)) try: result = native.recompile() except (OSError,RuntimeError,ValueError) as error: result = {'ok':False,'phase':'compile_outcome_unknown','error':str(error)} if not result['ok']: shutil.copy2(target,folder/'failed.ws') shutil.copy2(folder/'previous.ws',target) try:rollback = native.recompile() except (OSError,RuntimeError,ValueError) as error: rollback = {'ok':False,'error':str(error),'runtime_build_unknown':True} result.update(rollback_compiled=rollback['ok'],rollback=rollback) if not rollback['ok']: try: session = native.state() session['uncertain'] = {'command':'stage','reason':'rollback compilation unverified','artifacts':str(folder)} native.legacy.atomic_json(native.STATE,session) except (OSError,RuntimeError):pass else: native.legacy.atomic_json(receipt_path,updated) try: session = native.state() except (OSError,RuntimeError): session = None if session: session['build'] = build native.legacy.atomic_json(native.STATE,session) result.update(build=build,restart_required_for_layout_changes=True) result['staging_artifacts']=str(folder) native.legacy.atomic_json(folder/'result.json',result) return result if __name__ == '__main__': import fcntl with (native.STATE_ROOT/'.lock').open('a') as lock: fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB) print(json.dumps(stage()))