Wait until Caddy startup is actually complete, keep reading stderr in the background.
This commit is contained in:
+69
-29
@@ -28,7 +28,6 @@ import shutil
|
|||||||
import signal
|
import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from sys import stderr
|
from sys import stderr
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@@ -241,37 +240,78 @@ def run_caddy(origins: list[str], vite_port: int) -> subprocess.Popen | None:
|
|||||||
stderr.write(f"┃ ⚠️ Caddy couldn't start: {e}\n")
|
stderr.write(f"┃ ⚠️ Caddy couldn't start: {e}\n")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Wait briefly to check if Caddy exits immediately (e.g., port binding failure)
|
# Read stderr line by line until Caddy signals it's ready or exits
|
||||||
time.sleep(0.2)
|
# Caddy outputs JSON logs; "serving initial configuration" means it's ready
|
||||||
exit_code = caddy_process.poll()
|
ready = False
|
||||||
if exit_code is not None:
|
while True:
|
||||||
# Read and display stderr output, filtering and formatting JSON log lines
|
exit_code = caddy_process.poll()
|
||||||
caddy_stderr = caddy_process.stderr.read().decode() if caddy_process.stderr else ""
|
if exit_code is not None:
|
||||||
stderr.write(f"┃ ⚠️ Caddy startup failed\n")
|
# Process exited - read remaining stderr and report failure
|
||||||
if caddy_stderr:
|
remaining = caddy_process.stderr.read().decode() if caddy_process.stderr else ""
|
||||||
for line in caddy_stderr.splitlines():
|
stderr.write(f"┃ ⚠️ Caddy startup failed (exit code {exit_code})\n")
|
||||||
if not line:
|
if remaining:
|
||||||
continue
|
for line in remaining.splitlines():
|
||||||
try:
|
if line:
|
||||||
log = json.loads(line)
|
stderr.write(f"┃ {line}\n")
|
||||||
# Filter out info-level messages
|
return None
|
||||||
if log.get("level") == "info":
|
|
||||||
continue
|
# Read one line from stderr (blocks until data available)
|
||||||
# Filter out expected "admin endpoint disabled" warning
|
line = caddy_process.stderr.readline().decode()
|
||||||
if log.get("logger") == "admin" and "admin endpoint disabled" in log.get("msg", ""):
|
if not line:
|
||||||
continue
|
continue
|
||||||
# Format: [level] logger: msg
|
|
||||||
level = log.get("level", "?").upper()
|
# Try to parse as JSON log
|
||||||
logger = log.get("logger", "")
|
try:
|
||||||
|
log = json.loads(line)
|
||||||
|
msg = log.get("msg", "")
|
||||||
|
level = log.get("level", "")
|
||||||
|
|
||||||
|
# Check for ready signal
|
||||||
|
if "serving initial configuration" in msg:
|
||||||
|
ready = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# Show errors/warnings to user
|
||||||
|
if level in ("error", "fatal"):
|
||||||
|
logger = log.get("logger", "")
|
||||||
|
if logger:
|
||||||
|
stderr.write(f"┃ [{level.upper()}] {logger}: {msg}\n")
|
||||||
|
else:
|
||||||
|
stderr.write(f"┃ [{level.upper()}] {msg}\n")
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
# Not JSON - might be sudo password prompt or other output
|
||||||
|
# Pass through to user
|
||||||
|
stderr.write(line)
|
||||||
|
stderr.flush()
|
||||||
|
|
||||||
|
if ready:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Start a background thread to drain stderr and show errors
|
||||||
|
def drain_stderr():
|
||||||
|
while True:
|
||||||
|
line = caddy_process.stderr.readline().decode()
|
||||||
|
if not line:
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
log = json.loads(line)
|
||||||
|
level = log.get("level", "")
|
||||||
|
if level in ("error", "fatal", "warn"):
|
||||||
msg = log.get("msg", "")
|
msg = log.get("msg", "")
|
||||||
|
logger = log.get("logger", "")
|
||||||
|
# Skip expected warnings
|
||||||
|
if logger == "admin" and "admin endpoint disabled" in msg:
|
||||||
|
continue
|
||||||
if logger:
|
if logger:
|
||||||
stderr.write(f"┃ [{level}] {logger}: {msg}\n")
|
stderr.write(f"┃ [{level.upper()}] {logger}: {msg}\n")
|
||||||
else:
|
else:
|
||||||
stderr.write(f"┃ [{level}] {msg}\n")
|
stderr.write(f"┃ [{level.upper()}] {msg}\n")
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
# Not JSON, print as-is (e.g., "Error: ..." lines)
|
stderr.write(line)
|
||||||
stderr.write(f"┃ {line}\n")
|
|
||||||
return None
|
drain_thread = Thread(target=drain_stderr, daemon=True)
|
||||||
|
drain_thread.start()
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
if caddy_process:
|
if caddy_process:
|
||||||
|
|||||||
Reference in New Issue
Block a user