Release v0.51.236 — Release HD (stage-q7) (#3491)
Some checks failed
Release & Docker / release (push) Has been cancelled
Some checks failed
Release & Docker / release (push) Has been cancelled
## Release v0.51.236 — Release HD (stage-q7) First Phase 3 (deep-review) release — picked by the 3-factor framework (contributor × impact × mitigated-risk): high-impact (#1952 native Windows support), backend-only (no screenshots), well-mitigated risk (POSIX path provably unchanged), from a contributor active this session (@rodboev, #3446/#3486 shipped earlier today). ### Added | PR | Author | Fix | |----|--------|-----| | #1952 | @rodboev | Native Windows support for `bootstrap.py` + the embedded terminal: POSIX-only `fcntl`/`termios`/`select` guarded behind `_TERMINAL_SUPPORTED`; terminal entry points raise `NotImplementedError`/no-op on Windows; bootstrap Windows block → warning; auto-install errors clearly on native Windows (WSL unaffected); foreground uses `Popen`+exit on Windows instead of `os.execv`. **POSIX behavior unchanged on every path.** | ### Absorbed on the way in (fix-it-ourselves, reviewed fresh) - `subprocess.CREATE_NEW_PROCESS_GROUP` → `getattr(subprocess, ..., 0)` — the constant is Windows-only, so a win32-simulating test `AttributeError`'d on Linux. Mirrors the `SO_EXCLUSIVEADDRUSE` getattr guard. - Fixed 2 over-reaching tests in `test_windows_native_support.py` — one was launching a **real installer subprocess** via an unstubbed `subprocess.run` (now stubbed; harness 2.8s vs 80s); removed unused imports. - Updated `test_onboarding_static.py` — it asserted the OLD "Native Windows is not supported" hard-block string this PR intentionally replaces; now asserts the new experimental-warning + auto-install guard. - Help-text accuracy: `--foreground` help now describes the Windows Popen path (Opus nit). ### Gate results - **Full pytest suite**: 7478 passed, 9 skipped, 3 xpassed, **0 failed** - **ruff forward gate**: CLEAN - **browser-smoke gate**: CLEAN (gate hardened mid-release to auto-detect the cached chromium revision) - **Codex (regression)**: SAFE TO SHIP (simulated `sys.platform=win32`, verified POSIX modules not imported + all terminal guards complete + POSIX foreground still uses execv) - **Opus (correctness)**: SAFE TO SHIP (POSIX path provably unchanged, all fcntl/termios/select guarded, Popen+exit correct; noted inherent-Windows trade-offs that aren't PR bugs) Note: the Windows *runtime* path can't be executed on the Linux CI box; it was reviewed statically by both reviewers + the contributor's 209-line test (win32 simulated via monkeypatch). Linux/POSIX no-regression is fully verified. Closes #1952. Co-authored-by: rodboev <rodboev@users.noreply.github.com>
This commit is contained in:
32
bootstrap.py
32
bootstrap.py
@@ -92,9 +92,9 @@ def is_wsl() -> bool:
|
||||
|
||||
def ensure_supported_platform() -> None:
|
||||
if platform.system() == "Windows" and not is_wsl():
|
||||
raise RuntimeError(
|
||||
"Native Windows is not supported for this bootstrap yet. "
|
||||
"Please run it from Linux, macOS, or inside WSL2."
|
||||
info(
|
||||
"Warning: Native Windows bootstrap is experimental. "
|
||||
"Embedded terminal and auto-install are not supported."
|
||||
)
|
||||
|
||||
|
||||
@@ -270,6 +270,11 @@ def hermes_command_exists() -> bool:
|
||||
|
||||
|
||||
def install_hermes_agent() -> None:
|
||||
if platform.system() == "Windows" and not is_wsl():
|
||||
raise RuntimeError(
|
||||
"Auto-install is not supported on native Windows. "
|
||||
"Install hermes-agent manually first."
|
||||
)
|
||||
info(f"Hermes Agent not found. Attempting install via {INSTALLER_URL}")
|
||||
subprocess.run(
|
||||
["/bin/bash", "-lc", f"curl -fsSL {INSTALLER_URL} | bash"], check=True
|
||||
@@ -316,8 +321,10 @@ def parse_args() -> argparse.Namespace:
|
||||
"--foreground",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Run server.py in this process (via os.execv) instead of spawning a "
|
||||
"child. Use this under launchd / systemd / supervisord so the "
|
||||
"Run server.py in this process (via os.execv on POSIX; via a "
|
||||
"Popen child + exit on Windows, where execv can't replace the "
|
||||
"process image) instead of spawning a detached child. Use this "
|
||||
"under launchd / systemd / supervisord so the "
|
||||
"supervisor sees the long-lived server as the original child. "
|
||||
"Implies --no-browser. Skips the post-launch health probe — the "
|
||||
"supervisor's own KeepAlive / Restart=on-failure handles liveness."
|
||||
@@ -444,8 +451,19 @@ def main() -> int:
|
||||
f"Set HERMES_WEBUI_PYTHON to a working interpreter or fix "
|
||||
f"the agent venv at {agent_dir}."
|
||||
)
|
||||
# os.execv replaces the current process image. Anything after this line
|
||||
# only runs if execv itself fails (it raises OSError on failure).
|
||||
# os.execv replaces the current process image. On Windows, execv
|
||||
# spawns a new process instead of replacing (Python calls CreateProcess),
|
||||
# orphaning it from any supervisor. Use Popen + exit there instead.
|
||||
if sys.platform == "win32":
|
||||
# CREATE_NEW_PROCESS_GROUP only exists in the subprocess module on
|
||||
# Windows; resolve it defensively (0 = no extra flags) so this line
|
||||
# can't AttributeError if reached on a non-Windows interpreter
|
||||
# (e.g. a win32-simulating test) — mirrors the getattr() guard used
|
||||
# for SO_EXCLUSIVEADDRUSE.
|
||||
_CREATE_NEW_PROCESS_GROUP = getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
||||
subprocess.Popen([python_exe, server_path],
|
||||
creationflags=_CREATE_NEW_PROCESS_GROUP)
|
||||
sys.exit(0)
|
||||
os.execv(python_exe, [python_exe, server_path])
|
||||
# Unreachable — execv either replaces the process or raises.
|
||||
raise RuntimeError("os.execv returned unexpectedly")
|
||||
|
||||
Reference in New Issue
Block a user