fix(update): locate PortableGit under the shared root, not profile home
Review feedback on #88136 (monerostar): a profile-scoped `hermes update` sets HERMES_HOME to <root>/profiles/<name>, but the Hermes-managed PortableGit tree lives under the SHARED root (<root>/git/...). The locator checked get_hermes_home() only, so a broken trampoline during a profile-scoped update was not swapped and fell through to ZIP. Extract _portable_git_candidates() (shared root first, profile home as fallback) and add a regression test for the profile layout.
This commit is contained in:
@@ -37,7 +37,7 @@ from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from hermes_cli.config import get_hermes_home
|
||||
from hermes_constants import venv_python_path
|
||||
from hermes_constants import get_default_hermes_root, venv_python_path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -7381,6 +7381,26 @@ def _git_is_trampoline(git_cmd: list) -> bool:
|
||||
return "fork bomb" in output
|
||||
|
||||
|
||||
def _portable_git_candidates() -> list:
|
||||
"""PortableGit candidate paths: shared root first, then profile home.
|
||||
|
||||
The Hermes-managed PortableGit tree lives under the SHARED root
|
||||
(``<root>/git/...``), not the profile-scoped HERMES_HOME
|
||||
(``<root>/profiles/<name>``), so a profile-scoped ``hermes update`` must
|
||||
look there (monerostar review, #87876). The profile-home candidate is
|
||||
kept as a fallback for custom layouts that place it there.
|
||||
"""
|
||||
candidates = []
|
||||
try:
|
||||
for root in (get_default_hermes_root(), Path(get_hermes_home())):
|
||||
candidates.append(
|
||||
root / "git" / "mingw64" / "libexec" / "git-core" / "git.exe"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return candidates
|
||||
|
||||
|
||||
def _locate_real_git() -> Optional[Path]:
|
||||
"""Find a real Git-for-Windows binary that is not a broken trampoline.
|
||||
|
||||
@@ -7396,18 +7416,7 @@ def _locate_real_git() -> Optional[Path]:
|
||||
candidates = [
|
||||
Path(r"C:\Program Files\Git\mingw64\libexec\git-core\git.exe"),
|
||||
Path(r"C:\Program Files (x86)\Git\mingw64\libexec\git-core\git.exe"),
|
||||
]
|
||||
try:
|
||||
candidates.append(
|
||||
Path(get_hermes_home())
|
||||
/ "git"
|
||||
/ "mingw64"
|
||||
/ "libexec"
|
||||
/ "git-core"
|
||||
/ "git.exe"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
] + _portable_git_candidates()
|
||||
for candidate in candidates:
|
||||
if not candidate.exists():
|
||||
continue
|
||||
|
||||
@@ -1509,3 +1509,24 @@ class TestGitTrampolineSelfHeal:
|
||||
result = update_cmd._ensure_non_trampoline_git(git_cmd)
|
||||
assert result == git_cmd
|
||||
run.assert_not_called()
|
||||
|
||||
def test_portable_git_candidates_check_shared_root_first(self, tmp_path, monkeypatch):
|
||||
# Profile-scoped layout: HERMES_HOME = <root>/profiles/foo, but the
|
||||
# PortableGit tree lives under the SHARED root (monerostar review on
|
||||
# #88136). The candidate list must check get_default_hermes_root()
|
||||
# before the profile home.
|
||||
from hermes_cli import update_cmd
|
||||
|
||||
root = tmp_path / "root"
|
||||
profile_home = root / "profiles" / "foo"
|
||||
|
||||
monkeypatch.setattr(update_cmd, "get_default_hermes_root", lambda: root)
|
||||
monkeypatch.setattr(update_cmd, "get_hermes_home", lambda: profile_home)
|
||||
|
||||
candidates = update_cmd._portable_git_candidates()
|
||||
assert candidates[0] == (
|
||||
root / "git" / "mingw64" / "libexec" / "git-core" / "git.exe"
|
||||
)
|
||||
assert candidates[1] == (
|
||||
profile_home / "git" / "mingw64" / "libexec" / "git-core" / "git.exe"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user