mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(install): guard Windows desktop installs against broken web_server
This commit is contained in:
parent
94205a1139
commit
18e840469f
4 changed files with 73 additions and 4 deletions
|
|
@ -4443,14 +4443,17 @@ def _clear_bytecode_cache(root: Path) -> int:
|
|||
return removed
|
||||
|
||||
|
||||
# Critical files that every ``hermes`` invocation imports at startup. If any
|
||||
# of these fail to parse after a pull, the CLI is bricked — the user can't
|
||||
# even run ``hermes update`` again to roll forward. The post-pull syntax
|
||||
# guard validates these and auto-rolls-back on failure.
|
||||
# Critical files that Hermes must be able to import immediately after an
|
||||
# update/install. Most are imported on every CLI startup; ``web_server.py``
|
||||
# is the desktop/dashboard backend path that a fresh Windows install launches
|
||||
# right away. If any of these fail to parse after a pull, the user can be
|
||||
# left with a bricked CLI or desktop backend. The post-pull syntax guard
|
||||
# validates these and auto-rolls-back on failure.
|
||||
_UPDATE_CRITICAL_FILES = (
|
||||
"hermes_cli/main.py",
|
||||
"hermes_cli/config.py",
|
||||
"hermes_cli/__init__.py",
|
||||
"hermes_cli/web_server.py",
|
||||
"cli.py",
|
||||
"run_agent.py",
|
||||
"model_tools.py",
|
||||
|
|
|
|||
|
|
@ -1995,6 +1995,7 @@ print(','.join(scripts))
|
|||
$pythonExe = if (-not $NoVenv) { "$InstallDir\venv\Scripts\python.exe" } else { (& $UvCmd python find $PythonVersion) }
|
||||
if (Test-Path $pythonExe) {
|
||||
$webOk = $false
|
||||
$webServerSyntaxOk = $false
|
||||
# Relax EAP=Stop while running the import probe; see the matching
|
||||
# comment on the baseline-imports check above. Python writes
|
||||
# deprecation warnings to stderr and we don't want those wrapped
|
||||
|
|
@ -2006,6 +2007,10 @@ print(','.join(scripts))
|
|||
& $pythonExe -c "import fastapi, uvicorn" 2>&1 | Out-Null
|
||||
if ($LASTEXITCODE -eq 0) { $webOk = $true }
|
||||
} catch { }
|
||||
try {
|
||||
& $pythonExe -m py_compile "$InstallDir\hermes_cli\web_server.py" 2>&1 | Out-Null
|
||||
if ($LASTEXITCODE -eq 0) { $webServerSyntaxOk = $true }
|
||||
} catch { }
|
||||
$ErrorActionPreference = $prevEAP
|
||||
if (-not $webOk) {
|
||||
Write-Warn "fastapi/uvicorn not importable -- `hermes dashboard` will not work."
|
||||
|
|
@ -2017,6 +2022,9 @@ print(','.join(scripts))
|
|||
Write-Warn "Could not install [web] extra. Run manually: uv pip install --python `"$pythonExe`" `"fastapi>=0.104,<1`" `"uvicorn[standard]>=0.24,<1`""
|
||||
}
|
||||
}
|
||||
if (-not $webServerSyntaxOk) {
|
||||
throw "dashboard backend source failed syntax check: hermes_cli/web_server.py"
|
||||
}
|
||||
}
|
||||
|
||||
Pop-Location
|
||||
|
|
|
|||
|
|
@ -116,6 +116,15 @@ def test_validate_critical_files_syntax_detects_break_in_main_py(tmp_path):
|
|||
assert failing_path is not None and failing_path.endswith("hermes_cli/main.py")
|
||||
|
||||
|
||||
def test_validate_critical_files_syntax_detects_break_in_web_server(tmp_path):
|
||||
_populate_critical_tree(tmp_path, broken_file="hermes_cli/web_server.py")
|
||||
|
||||
ok, failing_path, _ = hermes_main._validate_critical_files_syntax(tmp_path)
|
||||
|
||||
assert ok is False
|
||||
assert failing_path is not None and failing_path.endswith("hermes_cli/web_server.py")
|
||||
|
||||
|
||||
def test_validate_critical_files_syntax_tolerates_missing_files(tmp_path):
|
||||
"""A refactor may legitimately remove one of the critical files — the
|
||||
guard should skip missing files, not falsely flag the install as broken."""
|
||||
|
|
|
|||
49
tests/test_install_ps1_web_server_syntax_probe.py
Normal file
49
tests/test_install_ps1_web_server_syntax_probe.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
"""Regression: install.ps1 must syntax-check the dashboard backend source.
|
||||
|
||||
Issue #59004 reported a fresh Windows desktop install crashing on launch
|
||||
because ``hermes_cli/web_server.py`` inside the installed checkout still
|
||||
contained merge-conflict markers. Import-only dependency probes (fastapi /
|
||||
uvicorn) do not catch that: the packages can be present while the backend
|
||||
source itself is unparsable.
|
||||
|
||||
This test is source-level because Linux CI cannot execute the PowerShell
|
||||
installer. It locks the contract that install.ps1 runs ``py_compile`` against
|
||||
``hermes_cli/web_server.py`` and fails the stage when that syntax probe fails.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
INSTALL_PS1 = REPO_ROOT / "scripts" / "install.ps1"
|
||||
|
||||
|
||||
def test_install_ps1_compiles_web_server_source_after_web_deps_probe() -> None:
|
||||
text = INSTALL_PS1.read_text(encoding="utf-8")
|
||||
|
||||
probe = re.search(
|
||||
r'import fastapi, uvicorn[\s\S]{0,1200}?-m py_compile "\$InstallDir\\hermes_cli\\web_server\.py"',
|
||||
text,
|
||||
)
|
||||
assert probe is not None, (
|
||||
"install.ps1 must syntax-check hermes_cli/web_server.py after the "
|
||||
"dashboard dependency probe so a fresh desktop install fails early on "
|
||||
"merge-conflict markers or other SyntaxErrors."
|
||||
)
|
||||
|
||||
|
||||
def test_install_ps1_fails_stage_when_web_server_syntax_probe_fails() -> None:
|
||||
text = INSTALL_PS1.read_text(encoding="utf-8")
|
||||
|
||||
assert "if ($LASTEXITCODE -eq 0) { $webServerSyntaxOk = $true }" in text
|
||||
assert "if (-not $webServerSyntaxOk) {" in text
|
||||
assert (
|
||||
'throw "dashboard backend source failed syntax check: hermes_cli/web_server.py"'
|
||||
in text
|
||||
), (
|
||||
"install.ps1 must fail the install stage when hermes_cli/web_server.py "
|
||||
"does not compile, instead of writing a broken desktop/backend install."
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue