60 lines
2.7 KiB
Python
60 lines
2.7 KiB
Python
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)
|