diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a28cdd2..3a6f7101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,19 @@ Instructions: Add a subsection under `[Unreleased]` for additions, fixes, change ## [Unreleased] +### Fixed + +- Improved codespace view command (now respects already running servers). +- CSS fixes for logo and references from core. + ## [2.11.1] - 2024-12-25 Includes updates to core through commit: [e4edfd0](https://github.com/PreTeXtBook/pretext/commit/e4edfd0fe052d9dd91404667a42ff1c0932d114b) +### Fixed + +- `pretext view` bug where a process is terminated abnormally + ## [2.11.0] - 2024-12-24 Includes updates to core through commit: [e4edfd0](https://github.com/PreTeXtBook/pretext/commit/e4edfd0fe052d9dd91404667a42ff1c0932d114b) diff --git a/pretext/__init__.py b/pretext/__init__.py index 12c3a6d5..ba243a72 100644 --- a/pretext/__init__.py +++ b/pretext/__init__.py @@ -21,7 +21,7 @@ VERSION = get_version("pretext", Path(__file__).parent.parent) -CORE_COMMIT = "e4edfd0fe052d9dd91404667a42ff1c0932d114b" +CORE_COMMIT = "3c662c9fc70c6fa49c031c44ffaebcfbf61ce2f1" def activate() -> None: diff --git a/pretext/cli.py b/pretext/cli.py index 99e42c7f..7d5b1098 100644 --- a/pretext/cli.py +++ b/pretext/cli.py @@ -1,8 +1,6 @@ import logging import logging.handlers -import random import sys -import time import click import click_log import shutil @@ -805,11 +803,10 @@ def view( if utils.cannot_find_project(task="view the output for"): return project = Project.parse() - + project_hash = utils.hash_path(project.abspath()) + current_server = server.active_server_for_path_hash(project_hash) if stop_server: try: - project_hash = utils.hash_path(project.abspath()) - current_server = server.active_server_for_path_hash(project_hash) log.info("\nStopping server.") if current_server: current_server.terminate() @@ -852,28 +849,8 @@ def view( in_codespace = os.environ.get("CODESPACES") - if in_codespace and not default_server: - log.info( - "Running in a codespace, so using the codespace server instead of the standard python server." - ) - if port == 8128: - port = random.randint(8129, 8999) - # set the url - url_base = utils.url_for_access(access=access, port=port) - url = url_base + url_path - log.info(f"Server will soon be available at {url_base}") - utils.start_codespace_server(port=port, access=access) - if no_launch: - log.info(f"The {target_name} will be available at {url}") - else: - seconds = 2 - log.info(f"Opening browser for {target_name} at {url} in {seconds} seconds") - time.sleep(seconds) - webbrowser.open(url) - return # Start server if there isn't one running already: - project_hash = utils.hash_path(project.abspath()) - current_server = server.active_server_for_path_hash(project_hash) + if restart_server and current_server is not None: log.info( f"Terminating existing server {current_server.pid} on port {current_server.port}" @@ -915,7 +892,11 @@ def callback(actual_port: int) -> None: webbrowser.open(url) log.info("starting server ...") - server.start_server(project.abspath(), access, port, callback) + if in_codespace and not default_server: + log.info("Running in a codespace, so using a subprocess server.") + server.start_codespace_server(project.abspath(), access, port, callback) + else: + server.start_server(project.abspath(), access, port, callback) # pretext deploy diff --git a/pretext/server.py b/pretext/server.py index 5bc83ecd..df8b4513 100644 --- a/pretext/server.py +++ b/pretext/server.py @@ -5,6 +5,8 @@ import os from pathlib import Path import socketserver +import subprocess +import sys import typing as t import psutil @@ -73,6 +75,13 @@ def url(self) -> str: return f"{self.binding}:{self.port}" +def is_port_in_use(port: int) -> bool: + import socket + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + return s.connect_ex(("localhost", port)) == 0 + + def get_running_servers() -> t.List[RunningServerInfo]: """ Processes the ~/.ptx/running_servers file to retrieve a list @@ -208,3 +217,29 @@ class TCPServer(socketserver.TCPServer): log.info("Stopping server.") remove_server_entry(path_hash) return + + +def start_codespace_server( + base_dir: Path, + access: t.Literal["public", "private"] = "private", + port: int = 8128, + callback: t.Callable[[int], None] | None = None, +) -> None: + while is_port_in_use(port): + log.debug(f"Port {port} is in use.") + port = port + 1 + log.debug(f"Trying port {port} instead.") + path_hash = hash_path(base_dir) + binding = binding_for_access(access) + log.debug(f"values set: {path_hash}, {binding}, {port}") + + log.info("Starting the server") + server_process = subprocess.Popen( + [sys.executable, "-m", "http.server", str(port)], cwd=base_dir + ) + log.debug(f"Server process pid: {server_process.pid}") + log.debug("adding server entry") + add_server_entry(path_hash, server_process.pid, port, binding) + if callback is not None: + callback(port) + return