refactor(update): lift ZIP download/swap and deps reinstall out of _update_via_zip (327 -> 137 LOC)
This commit is contained in:
@@ -237,63 +237,18 @@ def _abort_zip_update_if_dirty_tree() -> None:
|
||||
_m().sys.exit(1)
|
||||
|
||||
|
||||
def _update_via_zip(args, *, had_desktop_app_before_update: bool = False) -> bool:
|
||||
"""Update Hermes Agent by downloading a ZIP archive.
|
||||
def _download_and_swap_zip(branch: str, zip_url: str) -> None:
|
||||
"""Download the source ZIP for *branch* and two-phase swap it into the checkout.
|
||||
|
||||
Used on Windows when git file I/O is broken (antivirus, NTFS filter
|
||||
drivers causing 'Invalid argument' errors on file creation).
|
||||
|
||||
Returns ``False`` when a Desktop rebuild ran and failed; ``True`` otherwise.
|
||||
Exits the process (``sys.exit(1)``) on any failure; the two-phase replace
|
||||
guarantees the install is either fully updated or fully rolled back.
|
||||
"""
|
||||
from hermes_cli.update_cmd import (
|
||||
_ensure_uv_for_termux,
|
||||
_ensure_venv_pip,
|
||||
_finish_dashboard_update_cleanup,
|
||||
_m,
|
||||
_print_bundled_skills_sync_report,
|
||||
_print_curator_first_run_notice,
|
||||
_print_curator_recent_run_notice,
|
||||
_print_update_summary,
|
||||
_read_project_version,
|
||||
_rebuild_desktop_after_update,
|
||||
_refuse_update_for_contended_shims,
|
||||
_shim_quarantine_error_type,
|
||||
_sweep_bytecode_after_update,
|
||||
_update_node_dependencies,
|
||||
_validate_critical_modules_import,
|
||||
_verify_and_restore_state_dbs_post_update,
|
||||
)
|
||||
active_tool_dependencies = _m()._capture_active_tool_dependencies()
|
||||
from hermes_cli.update_cmd import _m
|
||||
|
||||
import tempfile
|
||||
import zipfile
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
# Snapshot the pre-update version before files are replaced so the
|
||||
# completion line can report the transition (prime-agent#630 port).
|
||||
pre_update_version = _read_project_version()
|
||||
|
||||
# The static GitHub archive is fine for "main" but would silently ignore
|
||||
# --branch — the exact silent-divergence bug --branch was added to
|
||||
# prevent. Refuse rather than lie.
|
||||
branch = _m()._resolve_update_branch(args)
|
||||
if branch != "main":
|
||||
print(
|
||||
f"✗ --branch={branch} is not supported on the Windows ZIP-fallback "
|
||||
"update path."
|
||||
)
|
||||
print(
|
||||
" This path runs when git file I/O is broken on the system. "
|
||||
"Either resolve the git-side breakage (typically an antivirus "
|
||||
"or NTFS filter holding files open) and rerun `hermes update "
|
||||
f"--branch {branch}`, or update against main with `hermes update`."
|
||||
)
|
||||
_m().sys.exit(1)
|
||||
_abort_zip_update_if_dirty_tree()
|
||||
zip_url = (
|
||||
f"https://github.com/NousResearch/hermes-agent/archive/refs/heads/{branch}.zip"
|
||||
)
|
||||
|
||||
print("→ Downloading latest version...")
|
||||
tmp_dir = tempfile.mkdtemp(prefix="hermes-update-")
|
||||
try:
|
||||
@@ -434,15 +389,16 @@ def _update_via_zip(args, *, had_desktop_app_before_update: bool = False) -> boo
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
|
||||
_sweep_bytecode_after_update(branch)
|
||||
|
||||
# Reinstall Python deps: prefer .[all]; if one extra breaks, keep base
|
||||
# deps and retry the remaining extras individually so working
|
||||
# capabilities aren't silently stripped. Self-lock deferral (#86735): the
|
||||
# code swap is committed; defer only the dependency sync when this
|
||||
# process holds a native extension the sync must rewrite.
|
||||
_m()._abort_dependency_sync_if_self_locked()
|
||||
print("→ Updating Python dependencies...")
|
||||
def _reinstall_python_deps_after_zip(active_tool_dependencies) -> None:
|
||||
"""Reinstall Python deps (uv preferred, pip fallback) and re-arm active tool deps."""
|
||||
from hermes_cli.update_cmd import (
|
||||
_ensure_uv_for_termux,
|
||||
_ensure_venv_pip,
|
||||
_m,
|
||||
_refuse_update_for_contended_shims,
|
||||
_shim_quarantine_error_type,
|
||||
)
|
||||
|
||||
from hermes_cli.managed_uv import ensure_uv, update_managed_uv
|
||||
|
||||
@@ -490,6 +446,70 @@ def _update_via_zip(args, *, had_desktop_app_before_update: bool = False) -> boo
|
||||
# #70636).
|
||||
_m()._refresh_active_memory_provider_dependencies()
|
||||
|
||||
|
||||
def _update_via_zip(args, *, had_desktop_app_before_update: bool = False) -> bool:
|
||||
"""Update Hermes Agent by downloading a ZIP archive.
|
||||
|
||||
Used on Windows when git file I/O is broken (antivirus, NTFS filter
|
||||
drivers causing 'Invalid argument' errors on file creation).
|
||||
|
||||
Returns ``False`` when a Desktop rebuild ran and failed; ``True`` otherwise.
|
||||
"""
|
||||
from hermes_cli.update_cmd import (
|
||||
_finish_dashboard_update_cleanup,
|
||||
_m,
|
||||
_print_bundled_skills_sync_report,
|
||||
_print_curator_first_run_notice,
|
||||
_print_curator_recent_run_notice,
|
||||
_print_update_summary,
|
||||
_read_project_version,
|
||||
_rebuild_desktop_after_update,
|
||||
_sweep_bytecode_after_update,
|
||||
_update_node_dependencies,
|
||||
_validate_critical_modules_import,
|
||||
_verify_and_restore_state_dbs_post_update,
|
||||
)
|
||||
active_tool_dependencies = _m()._capture_active_tool_dependencies()
|
||||
|
||||
# Snapshot the pre-update version before files are replaced so the
|
||||
# completion line can report the transition (prime-agent#630 port).
|
||||
pre_update_version = _read_project_version()
|
||||
|
||||
# The static GitHub archive is fine for "main" but would silently ignore
|
||||
# --branch — the exact silent-divergence bug --branch was added to
|
||||
# prevent. Refuse rather than lie.
|
||||
branch = _m()._resolve_update_branch(args)
|
||||
if branch != "main":
|
||||
print(
|
||||
f"✗ --branch={branch} is not supported on the Windows ZIP-fallback "
|
||||
"update path."
|
||||
)
|
||||
print(
|
||||
" This path runs when git file I/O is broken on the system. "
|
||||
"Either resolve the git-side breakage (typically an antivirus "
|
||||
"or NTFS filter holding files open) and rerun `hermes update "
|
||||
f"--branch {branch}`, or update against main with `hermes update`."
|
||||
)
|
||||
_m().sys.exit(1)
|
||||
_abort_zip_update_if_dirty_tree()
|
||||
zip_url = (
|
||||
f"https://github.com/NousResearch/hermes-agent/archive/refs/heads/{branch}.zip"
|
||||
)
|
||||
|
||||
_download_and_swap_zip(branch, zip_url)
|
||||
|
||||
_sweep_bytecode_after_update(branch)
|
||||
|
||||
# Reinstall Python deps: prefer .[all]; if one extra breaks, keep base
|
||||
# deps and retry the remaining extras individually so working
|
||||
# capabilities aren't silently stripped. Self-lock deferral (#86735): the
|
||||
# code swap is committed; defer only the dependency sync when this
|
||||
# process holds a native extension the sync must rewrite.
|
||||
_m()._abort_dependency_sync_if_self_locked()
|
||||
print("→ Updating Python dependencies...")
|
||||
|
||||
_reinstall_python_deps_after_zip(active_tool_dependencies)
|
||||
|
||||
# Verify the tree actually imports (catches the parse-OK-but-skewed tree
|
||||
# an interrupted copy leaves). Placed *after* the dependency reinstall so
|
||||
# a genuinely-new third-party requirement isn't misreported as a partial
|
||||
|
||||
@@ -339,7 +339,10 @@ def test_swap_preserve_set_is_the_module_constant():
|
||||
truth for the preserved entries (no comment-synced duplicate)."""
|
||||
import inspect
|
||||
|
||||
src = inspect.getsource(update_cmd._update_via_zip)
|
||||
from hermes_cli import update_cmd_zip
|
||||
|
||||
# The swap loop lives in the download/swap collaborator the ZIP path calls.
|
||||
src = inspect.getsource(update_cmd_zip._download_and_swap_zip)
|
||||
assert "preserve = _ZIP_PRESERVED_TOP_LEVEL" in src
|
||||
|
||||
|
||||
|
||||
@@ -439,7 +439,10 @@ def test_update_via_zip_wires_discard_into_the_commit_failure_path():
|
||||
import inspect
|
||||
import textwrap
|
||||
|
||||
src = textwrap.dedent(inspect.getsource(update_cmd._update_via_zip))
|
||||
# The swap lives in the download/swap collaborator the ZIP path calls.
|
||||
from hermes_cli import update_cmd_zip
|
||||
|
||||
src = textwrap.dedent(inspect.getsource(update_cmd_zip._download_and_swap_zip))
|
||||
tree = ast.parse(src)
|
||||
|
||||
def _calls(node, name):
|
||||
|
||||
Reference in New Issue
Block a user