#!/usr/bin/env python3 """Install and atomically activate one verified extension release.""" from __future__ import annotations import argparse import json import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[1] sys.path[:0] = [ str(ROOT / "packages" / "jwebot-sdk" / "src"), str(ROOT / "packages" / "jwebot-kernel" / "src"), ] from jwebot_kernel import ( # noqa: E402 ActivationError, ExtensionHostClient, ReleaseError, install_release, ) def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("artifact", type=Path) parser.add_argument("release", type=Path) parser.add_argument("--state-root", type=Path, required=True) parser.add_argument("--signature", type=Path) parser.add_argument("--allowed-signers", type=Path) parser.add_argument("--publisher") parser.add_argument("--allow-unsigned-local", action="store_true") parser.add_argument("--skip-health-check-local", action="store_true") args = parser.parse_args() if args.allow_unsigned_local: if any( value is not None for value in ( args.signature, args.allowed_signers, args.publisher, ) ): parser.error( "--allow-unsigned-local cannot include signature options" ) elif args.signature is None or args.allowed_signers is None: parser.error( "signed activation requires --signature and --allowed-signers" ) health_check = None if not args.allow_unsigned_local and not args.skip_health_check_local: client = ExtensionHostClient( allowed_signers=args.allowed_signers, expected_publisher=args.publisher, ) health_check = lambda release_dir: client.invoke( release_dir, {"op": "health"}, ) try: destination = install_release( args.artifact, args.release, args.state_root, signature=args.signature, allowed_signers=args.allowed_signers, expected_publisher=args.publisher, allow_unsigned=args.allow_unsigned_local, health_check=health_check, allow_unhealthy_local=args.skip_health_check_local, ) except (ActivationError, ReleaseError) as error: parser.error(str(error)) print( json.dumps( { "activated": True, "release_dir": str(destination), "current": str(args.state_root / "current"), }, ensure_ascii=False, sort_keys=True, ) ) return 0 if __name__ == "__main__": raise SystemExit(main())