diff --git a/pkgs/clan-cli/clan_cli/webui/server.py b/pkgs/clan-cli/clan_cli/webui/server.py index f780f9b6..0799d5b9 100644 --- a/pkgs/clan-cli/clan_cli/webui/server.py +++ b/pkgs/clan-cli/clan_cli/webui/server.py @@ -2,32 +2,64 @@ import argparse import logging import multiprocessing as mp import os +import shutil +import signal import socket import subprocess import sys import syslog +import tempfile import time import urllib.request -import webbrowser from contextlib import ExitStack, contextmanager from pathlib import Path from threading import Thread from typing import Iterator # XXX: can we dynamically load this using nix develop? -from uvicorn import run +import uvicorn + +from clan_cli.errors import ClanError log = logging.getLogger(__name__) -def defer_open_browser(base_url: str) -> None: - for i in range(5): +def open_browser(base_url: str) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + for i in range(5): + try: + urllib.request.urlopen(base_url + "/health") + break + except OSError: + time.sleep(i) + proc = _open_browser(base_url, tmpdir) try: - urllib.request.urlopen(base_url + "/health") - break - except OSError: - time.sleep(i) - webbrowser.open(base_url) + proc.wait() + print("Browser closed") + os.kill(os.getpid(), signal.SIGINT) + finally: + proc.kill() + proc.wait() + + +def _open_browser(base_url: str, tmpdir: str) -> subprocess.Popen: + for browser in ("firefox", "iceweasel", "iceape", "seamonkey"): + if shutil.which(browser): + cmd = [ + browser, + "-kiosk", + "-private-window", + "--new-instance", + "--profile", + tmpdir, + base_url, + ] + print(" ".join(cmd)) + return subprocess.Popen(cmd) + for browser in ("chromium", "chromium-browser", "google-chrome", "chrome"): + if shutil.which(browser): + return subprocess.Popen([browser, f"--app={base_url}"]) + raise ClanError("No browser found") @contextmanager @@ -84,9 +116,9 @@ def start_server(args: argparse.Namespace) -> None: open_url = f"http://[{args.host}]:{args.port}" if not args.no_open: - Thread(target=defer_open_browser, args=(open_url,)).start() + Thread(target=open_browser, args=(open_url,)).start() - run( + uvicorn.run( "clan_cli.webui.app:app", host=args.host, port=args.port, @@ -161,7 +193,7 @@ def set_out_to_syslog() -> None: # type: ignore def _run_socketfile(socket_file: Path, debug: bool) -> None: set_out_to_syslog() - run( + uvicorn.run( "clan_cli.webui.app:app", uds=str(socket_file), access_log=debug, diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 35aec8e8..68cc8f98 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -10,6 +10,7 @@ , pytest-cov , pytest-subprocess , pytest-parallel +, pytest-timeout , python3 , runCommand , setuptools @@ -44,6 +45,7 @@ let pytest-cov pytest-subprocess pytest-parallel + pytest-timeout openssh git gnupg diff --git a/pkgs/clan-cli/tests/test_webui.py b/pkgs/clan-cli/tests/test_webui.py index 9c43a671..0ff3f8ca 100644 --- a/pkgs/clan-cli/tests/test_webui.py +++ b/pkgs/clan-cli/tests/test_webui.py @@ -5,15 +5,17 @@ import subprocess import sys from pathlib import Path +import pytest from ports import PortFunction +@pytest.mark.timeout(10) def test_start_server(unused_tcp_port: PortFunction, temporary_dir: Path) -> None: port = unused_tcp_port() fifo = temporary_dir / "fifo" os.mkfifo(fifo) - notify_script = temporary_dir / "notify.sh" + notify_script = temporary_dir / "firefox" bash = shutil.which("bash") assert bash is not None notify_script.write_text( @@ -25,7 +27,8 @@ echo "1" > {fifo} notify_script.chmod(0o700) env = os.environ.copy() - env["BROWSER"] = str(notify_script) + print(str(temporary_dir.absolute())) + env["PATH"] = ":".join([str(temporary_dir.absolute())] + env["PATH"].split(":")) with subprocess.Popen( [sys.executable, "-m", "clan_cli.webui", "--port", str(port)], env=env ) as p: