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 subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from sys import stderr
|
||||
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")
|
||||
return None
|
||||
|
||||
# Wait briefly to check if Caddy exits immediately (e.g., port binding failure)
|
||||
time.sleep(0.2)
|
||||
exit_code = caddy_process.poll()
|
||||
if exit_code is not None:
|
||||
# Read and display stderr output, filtering and formatting JSON log lines
|
||||
caddy_stderr = caddy_process.stderr.read().decode() if caddy_process.stderr else ""
|
||||
stderr.write(f"┃ ⚠️ Caddy startup failed\n")
|
||||
if caddy_stderr:
|
||||
for line in caddy_stderr.splitlines():
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
log = json.loads(line)
|
||||
# Filter out info-level messages
|
||||
if log.get("level") == "info":
|
||||
continue
|
||||
# Filter out expected "admin endpoint disabled" warning
|
||||
if log.get("logger") == "admin" and "admin endpoint disabled" in log.get("msg", ""):
|
||||
continue
|
||||
# Format: [level] logger: msg
|
||||
level = log.get("level", "?").upper()
|
||||
logger = log.get("logger", "")
|
||||
# Read stderr line by line until Caddy signals it's ready or exits
|
||||
# Caddy outputs JSON logs; "serving initial configuration" means it's ready
|
||||
ready = False
|
||||
while True:
|
||||
exit_code = caddy_process.poll()
|
||||
if exit_code is not None:
|
||||
# Process exited - read remaining stderr and report failure
|
||||
remaining = caddy_process.stderr.read().decode() if caddy_process.stderr else ""
|
||||
stderr.write(f"┃ ⚠️ Caddy startup failed (exit code {exit_code})\n")
|
||||
if remaining:
|
||||
for line in remaining.splitlines():
|
||||
if line:
|
||||
stderr.write(f"┃ {line}\n")
|
||||
return None
|
||||
|
||||
# Read one line from stderr (blocks until data available)
|
||||
line = caddy_process.stderr.readline().decode()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Try to parse as JSON log
|
||||
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", "")
|
||||
logger = log.get("logger", "")
|
||||
# Skip expected warnings
|
||||
if logger == "admin" and "admin endpoint disabled" in msg:
|
||||
continue
|
||||
if logger:
|
||||
stderr.write(f"┃ [{level}] {logger}: {msg}\n")
|
||||
stderr.write(f"┃ [{level.upper()}] {logger}: {msg}\n")
|
||||
else:
|
||||
stderr.write(f"┃ [{level}] {msg}\n")
|
||||
except json.JSONDecodeError:
|
||||
# Not JSON, print as-is (e.g., "Error: ..." lines)
|
||||
stderr.write(f"┃ {line}\n")
|
||||
return None
|
||||
stderr.write(f"┃ [{level.upper()}] {msg}\n")
|
||||
except json.JSONDecodeError:
|
||||
stderr.write(line)
|
||||
|
||||
drain_thread = Thread(target=drain_stderr, daemon=True)
|
||||
drain_thread.start()
|
||||
|
||||
def cleanup():
|
||||
if caddy_process:
|
||||
|
||||
Reference in New Issue
Block a user