From 53cbfe23c65beaae477d0911a19c34c0129e2b7a Mon Sep 17 00:00:00 2001 From: allenliang2022 Date: Sat, 4 Jul 2026 15:08:31 +0800 Subject: [PATCH] fix(webui): resolve git.exe via Windows registry/known paths so WEBUI_VERSION doesn't degrade to 'unknown' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the WebUI server is launched from a venv python whose PATH does not include git (e.g. hermes-agent venv on Windows), shutil.which('git') returns None and WEBUI_VERSION falls back to 'unknown'. That freezes the ?v= static-asset cache key, so browsers keep serving stale cached ui.js/messages.js even after the server is restarted with fixed code — frontend fixes silently never reach clients (recurring scroll jump-back). Add a Windows fallback to _resolve_git_executable(): read the Git-for-Windows registry InstallPath (HKLM + WOW6432Node + HKCU), then probe common install dirs. Mirrors the existing darwin /usr/bin/git fallback. No behavior change on posix or when git is already on PATH. --- api/updates.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/api/updates.py b/api/updates.py index a44bed59..d9c91d69 100644 --- a/api/updates.py +++ b/api/updates.py @@ -214,12 +214,60 @@ def _run_git(args, cwd, timeout=10): return f'git failed to start: {exc}', False +def _windows_git_from_registry(): + """Best-effort resolve git.exe from the Git-for-Windows registry key. + + Git for Windows records its install root at + ``HKLM\\SOFTWARE\\GitForWindows\\InstallPath`` (and the WOW6432Node mirror + for a 32-bit install on 64-bit Windows). ``git.exe`` lives under + ``\\cmd\\git.exe``. This is the reliable way to find git when + it is installed but NOT on the launching process's PATH — e.g. the WebUI + server started from a venv python whose environment does not inherit the + interactive shell PATH, which otherwise degrades WEBUI_VERSION to + ``'unknown'`` and freezes the ``?v=`` static-asset cache-busting stamp. + """ + try: + import winreg + except ImportError: + return None + for hive, flag in ( + (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_64KEY), + (winreg.HKEY_LOCAL_MACHINE, winreg.KEY_WOW64_32KEY), + (winreg.HKEY_CURRENT_USER, 0), + ): + try: + with winreg.OpenKey( + hive, r'SOFTWARE\GitForWindows', 0, + winreg.KEY_READ | flag, + ) as key: + install_path, _ = winreg.QueryValueEx(key, 'InstallPath') + except OSError: + continue + if not install_path: + continue + candidate = os.path.join(install_path, 'cmd', 'git.exe') + if os.path.exists(candidate): + return candidate + return None + + def _resolve_git_executable(): git_executable = shutil.which('git') if git_executable: return git_executable if sys.platform == 'darwin' and os.path.exists('/usr/bin/git'): return '/usr/bin/git' + if sys.platform == 'win32': + from_registry = _windows_git_from_registry() + if from_registry: + return from_registry + for candidate in ( + os.path.expandvars(r'%ProgramFiles%\Git\cmd\git.exe'), + os.path.expandvars(r'%ProgramFiles(x86)%\Git\cmd\git.exe'), + os.path.expandvars(r'%LocalAppData%\Programs\Git\cmd\git.exe'), + ): + if candidate and os.path.exists(candidate): + return candidate return None