import json import os from pathlib import Path import plistlib import selectors import shutil import subprocess import sys import tempfile import uuid app_source, library, fixtures = map(lambda path: Path(path).resolve(), sys.argv[1:]) with tempfile.TemporaryDirectory(prefix="terebi-startup-") as directory: root = Path(directory) app = root / "terebi.app" shutil.copytree(app_source, app) identifier = "local.personal.terebi.startup-check." + uuid.uuid4().hex info_path = app / "Contents/Info.plist" info = plistlib.loads(info_path.read_bytes()) info["CFBundleIdentifier"] = identifier info_path.write_bytes(plistlib.dumps(info)) shutil.copy2(library, app / "Contents/Frameworks/libterebi-player.dylib") pipe = root / "result.fifo" os.mkfifo(pipe) descriptor = os.open(pipe, os.O_RDONLY | os.O_NONBLOCK) launch = None try: with selectors.DefaultSelector() as selector: selector.register(descriptor, selectors.EVENT_READ) launch = subprocess.Popen( ["open", "-g", "-n", "-W", str(app), "--args", str(pipe), str(fixtures)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) if not selector.select(timeout=30): raise RuntimeError("packaged app did not report its startup state") result = json.loads(os.read(descriptor, 4096)) stdout, stderr = launch.communicate(timeout=10) if launch.returncode != 0 or not result["passed"] or result["bundleIdentifier"] != identifier: raise RuntimeError(f"packaged startup failed: {result}; {stdout}{stderr}") print("PASS packaged bootstrap: registered app PID/identity, in-process player load, actual playback and Now Playing") finally: os.close(descriptor) subprocess.run( ["xcrun", "swift", "-e", 'import AppKit; for app in NSWorkspace.shared.runningApplications { ' 'if app.bundleIdentifier == CommandLine.arguments[1] { app.forceTerminate() } }', identifier], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, ) if launch is not None and launch.poll() is None: launch.terminate() launch.communicate(timeout=5) subprocess.run( ["/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", "-u", str(app)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False, ) for domain in [identifier, identifier + ".history"]: subprocess.run(["defaults", "delete", domain], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False)