fix(cli): protect root devDependencies from hermes update's scoped npm ci

Root package.json still owns devDependencies (the shared ESLint flat
config every workspace's eslint.config.mjs imports) even though
agent-browser and @streamdown/math were already removed from root
dependencies. The scoped `npm ci --workspace ui-tui --workspace web`
prunes them the same way it used to prune those; --include-workspace-root
protects them without reintroducing apps/desktop into the install.
This commit is contained in:
Zak B. Elep 2026-08-11 17:32:25 +08:00 committed by Teknium
parent 03cdc3b20c
commit 737e7aa562
2 changed files with 37 additions and 0 deletions

View File

@ -2178,6 +2178,13 @@ def _update_node_dependencies() -> list[str]:
install_args = [
"--no-fund", "--no-audit", "--prefer-offline", "--progress=false",
"--workspace", "ui-tui", "--workspace", "web",
# Root package.json's own devDependencies (the shared ESLint flat
# config every workspace's eslint.config.mjs imports) are otherwise
# pruned by this scoped install, same as agent-browser/@streamdown
# math used to be before they moved out of root entirely (#43564).
# Unlike those, root's devDependencies have nowhere else to live —
# this flag still excludes apps/desktop, which is never named above.
"--include-workspace-root",
]
from hermes_constants import with_hermes_node_path

View File

@ -879,6 +879,36 @@ class TestUpdateNodeDependencies:
f"no root-only deps remain to protect; --workspaces=false is unnecessary now; actual: {calls[0]}"
)
@patch("subprocess.Popen")
@patch("shutil.which", return_value="/usr/bin/npm")
def test_install_includes_workspace_root_to_protect_root_devdependencies(
self, _which, mock_popen, tmp_path, monkeypatch
):
"""Root package.json still owns devDependencies (the shared ESLint
flat config every workspace's own eslint.config.mjs imports) even
though agent-browser and @streamdown/math were removed from root
`dependencies` (#43564). --include-workspace-root keeps them from
being pruned by this scoped install, while --workspace ui-tui
--workspace web still excludes the unnamed apps/desktop workspace
(confirmed empirically against npm 10.9.8 and 11.9.0 in PR #44772
review)."""
from hermes_cli import main as hm
(tmp_path / "package.json").write_text("{}")
(tmp_path / "package-lock.json").write_text("{}")
monkeypatch.setattr(hm, "PROJECT_ROOT", tmp_path)
monkeypatch.setattr(hm, "_npm_lockfile_changed", lambda root: True)
popen_calls = []
mock_popen.side_effect = self._make_popen(popen_calls)
hm._update_node_dependencies()
calls = self._popen_npm_calls(popen_calls)
assert len(calls) == 1
joined = " ".join(str(a) for a in calls[0])
assert "--include-workspace-root" in joined
assert "desktop" not in joined
@patch("subprocess.Popen")
@patch("shutil.which", return_value="/usr/bin/npm")
def test_install_preserves_standard_flags(self, _which, mock_popen, tmp_path, monkeypatch):