"""Build a local VM upgrade kit. The core image and published app release are unchanged.""" import argparse,hashlib,pathlib,shutil,tarfile,tempfile ROOT=pathlib.Path(__file__).resolve().parent.parent parser=argparse.ArgumentParser() parser.add_argument('--scripts',type=pathlib.Path,default=ROOT.parent/'skeletonworks-scripts') parser.add_argument('--legacy-app-release',type=pathlib.Path,default=ROOT.parent/'social-scheduler/release/social-scheduler-0.5.0.tar.gz') parser.add_argument('--migration-recipe',type=pathlib.Path,default=ROOT.parent/'social-scheduler/integration/conductor-migration.json') args=parser.parse_args() expected='e5a9e230a53c750c63aefeed6ec91fb6ca04e678a7cb75418e921b48ac27fdbd' if hashlib.sha256(args.legacy_app_release.read_bytes()).hexdigest()!=expected:raise SystemExit('The legacy frontend migration requires the verified, published Social Scheduler 0.5.0 kit.') out=ROOT/'release';out.mkdir(exist_ok=True) with tempfile.TemporaryDirectory(prefix='conductor-upgrade-') as temp: kit=pathlib.Path(temp)/'conductor-upgrade';kit.mkdir() for name in ['update-conductor.sh','setup-conductor.sh','backup-conductor.sh','restore-conductor.sh']: shutil.copy2(args.scripts/name,kit/name) guide=ROOT/'docs/CONDUCTOR_UPGRADE.md' if guide.exists():shutil.copy2(guide,kit/'INSTALL.md') packages=kit/'conductor-app-packages';packages.mkdir() # Only the app-owned browser package and declarative connection mapping are bundled. with tarfile.open(args.legacy_app_release) as archive: prefix='social-scheduler/dist/conductor-app/' for member in archive: if not member.name.startswith(prefix) or member.isdir():continue relative=pathlib.PurePosixPath(member.name[len(prefix):]) if not member.isfile() or relative.is_absolute() or '..' in relative.parts:raise SystemExit('Unsafe release package asset.') target=packages/'social-scheduler'/str(relative);target.parent.mkdir(parents=True,exist_ok=True) target.write_bytes(archive.extractfile(member).read()) shutil.copy2(args.migration_recipe,packages/'social-scheduler/conductor-migration.json') files=sorted(p for p in kit.rglob('*') if p.is_file()) (kit/'SHA256SUMS').write_text(''.join(hashlib.sha256(p.read_bytes()).hexdigest()+' '+str(p.relative_to(kit))+'\n' for p in files)) output=out/'conductor-upgrade-1.1.0.tar.gz' with tarfile.open(output.with_suffix('.gz.new'),'w:gz') as archive:archive.add(kit,arcname=kit.name) output.with_suffix('.gz.new').replace(output) (out/(output.name+'.sha256')).write_text(hashlib.sha256(output.read_bytes()).hexdigest()+' '+output.name+'\n') print(output)