fix(update): don't roll back a good update over uninstalled deps
Phase 2 review caught a false-rollback I introduced: on the git path the import guard runs at the post-pull syntax check, which is BEFORE the dependency sync. A release that adds a new third-party requirement would fail the probe and trigger `git reset --hard` on a perfectly good update. Rather than reorder the git path (the guard belongs with the rollback it feeds), make the probe ignore a missing module that isn't ours. A missing third-party package means deps aren't installed yet; a missing first-party module means the update dropped a file, which IS the skew we're hunting. This also makes the ZIP path's ordering non-load-bearing. Verified: third-party absent -> (True, None, None); first-party absent -> flagged; and the original TODO_INJECTION_HEADER skew is still caught.
This commit is contained in:
parent
aa5d4fd6ee
commit
822571fa8e
|
|
@ -200,6 +200,16 @@ def _validate_critical_modules_import(root) -> tuple[bool, str | None, str | Non
|
|||
"for name in %r:\n"
|
||||
" try:\n"
|
||||
" importlib.import_module(name)\n"
|
||||
" except ModuleNotFoundError as exc:\n"
|
||||
# A missing *third-party* module means dependencies aren't installed
|
||||
# yet (this guard can run before the dependency sync on the git path),
|
||||
# not a skewed checkout. Only treat it as breakage when the missing
|
||||
# module is one of ours.
|
||||
" missing = (getattr(exc, 'name', '') or '').split('.')[0]\n"
|
||||
" if missing in ('tools', 'agent', 'gateway', 'plugins', 'providers') \\\n"
|
||||
" or missing.startswith('hermes'):\n"
|
||||
" sys.stdout.write(name + '\\n' + str(exc))\n"
|
||||
" raise SystemExit(3)\n"
|
||||
" except ImportError as exc:\n"
|
||||
" sys.stdout.write(name + '\\n' + str(exc))\n"
|
||||
" raise SystemExit(3)\n"
|
||||
|
|
|
|||
|
|
@ -147,3 +147,27 @@ def test_import_guard_prefers_the_project_venv_interpreter(monkeypatch, tmp_path
|
|||
update_cmd._validate_critical_modules_import(tmp_path)
|
||||
|
||||
assert seen["interpreter"] == str(venv_python)
|
||||
|
||||
|
||||
def test_import_guard_ignores_missing_third_party_dependency(monkeypatch, tmp_path):
|
||||
"""A new third-party requirement is not a partially-updated tree.
|
||||
|
||||
On the git path this guard runs BEFORE the dependency sync, so a release
|
||||
that adds a dependency would otherwise look like breakage and trigger a
|
||||
spurious `git reset --hard` rollback of a perfectly good update.
|
||||
"""
|
||||
(tmp_path / "consumer.py").write_text("import totally_not_installed_pkg\n")
|
||||
monkeypatch.setattr(update_cmd, "_UPDATE_CRITICAL_MODULES", ("consumer",))
|
||||
|
||||
assert update_cmd._validate_critical_modules_import(tmp_path) == (True, None, None)
|
||||
|
||||
|
||||
def test_import_guard_flags_missing_first_party_module(monkeypatch, tmp_path):
|
||||
"""A missing *first-party* module IS skew — the update dropped a file."""
|
||||
(tmp_path / "consumer.py").write_text("import tools.nonexistent_module\n")
|
||||
monkeypatch.setattr(update_cmd, "_UPDATE_CRITICAL_MODULES", ("consumer",))
|
||||
|
||||
ok, module, error = update_cmd._validate_critical_modules_import(tmp_path)
|
||||
assert ok is False
|
||||
assert module == "consumer"
|
||||
assert error is not None and "tools.nonexistent_module" in error
|
||||
|
|
|
|||
Loading…
Reference in New Issue