Browse Source

search for CGit installations in PATH and registry

Peter Rowlands 4 years ago
parent
commit
0530d81853
2 changed files with 103 additions and 6 deletions
  1. 52 6
      dulwich/config.py
  2. 51 0
      dulwich/tests/test_config.py

+ 52 - 6
dulwich/config.py

@@ -498,6 +498,57 @@ def get_xdg_config_home_path(*path_segments):
     return os.path.join(xdg_config_home, *path_segments)
 
 
+def _find_git_in_win_path():
+    for exe in ("git.exe", "git.cmd"):
+        for path in os.environ.get("PATH", "").split(";"):
+            if os.path.exists(os.path.join(path, exe)):
+                # exe path is .../Git/bin/git.exe or .../Git/cmd/git.exe
+                git_dir, _bin_dir = os.path.split(path)
+                yield git_dir
+                break
+
+
+def _find_git_in_win_reg():
+    import platform
+    import winreg
+
+    if platform.machine() == "AMD64":
+        subkey = (
+            "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\"
+            "CurrentVersion\\Uninstall\\Git_is1"
+        )
+    else:
+        subkey = (
+            "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
+            "Uninstall\\Git_is1"
+        )
+
+    for key in (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE):
+        try:
+            with winreg.OpenKey(key, subkey) as k:
+                val, typ = winreg.QueryValueEx(k, "InstallLocation")
+                if typ == winreg.REG_SZ:
+                    yield val
+        except OSError:
+            pass
+
+
+# There is no set standard for system config dirs on windows. We try the
+# following:
+#   - %ProgramData%/Git/config - (deprecated) Windows config dir per CGit docs
+#   - %ProgramFiles%/Git/etc/gitconfig - Git for Windows (msysgit) config dir
+#     Used if CGit installation (Git/bin/git.exe) is found in PATH in the
+#     system registry
+def get_win_system_paths():
+    if "ProgramData" in os.environ:
+        yield os.path.join(os.environ["ProgramData"], "Git", "config")
+
+    for git_dir in _find_git_in_win_path():
+        yield os.path.join(git_dir, "etc", "gitconfig")
+    for git_dir in _find_git_in_win_reg():
+        yield os.path.join(git_dir, "etc", "gitconfig")
+
+
 class StackedConfig(Config):
     """Configuration which reads from multiple config files.."""
 
@@ -525,12 +576,7 @@ class StackedConfig(Config):
         if "GIT_CONFIG_NOSYSTEM" not in os.environ:
             paths.append("/etc/gitconfig")
             if sys.platform == "win32":
-                paths.extend(
-                    [
-                        "C:/Program Files/Git/etc/gitconfig",
-                        "C:/ProgramData/Git/config",
-                    ]
-                )
+                paths.extend(get_win_system_paths())
 
         backends = []
         for path in paths:

+ 51 - 0
dulwich/tests/test_config.py

@@ -20,7 +20,12 @@
 
 """Tests for reading and writing configuration files."""
 
+import os
+import sys
 from io import BytesIO
+from unittest import skipIf
+from unittest.mock import patch
+
 from dulwich.config import (
     ConfigDict,
     ConfigFile,
@@ -268,9 +273,55 @@ class ConfigDictTests(TestCase):
 
 
 class StackedConfigTests(TestCase):
+    def setUp(self):
+        super(StackedConfigTests, self).setUp()
+        self._old_path = os.environ.get("PATH")
+
+    def tearDown(self):
+        super(StackedConfigTests, self).tearDown()
+        os.environ["PATH"] = self._old_path
+
     def test_default_backends(self):
         StackedConfig.default_backends()
 
+    @skipIf(sys.platform != "win32", "Windows specfic config location.")
+    def test_windows_config_from_path(self):
+        from dulwich.config import get_win_system_paths
+
+        install_dir = os.path.join("C:", "foo", "Git")
+        os.environ["PATH"] = os.path.join(install_dir, "cmd")
+        with patch("os.path.exists", return_value=True):
+            paths = set(get_win_system_paths())
+        self.assertEqual(
+            {
+                os.path.join(os.environ.get("ProgramData"), "Git", "config"),
+                os.path.join(install_dir, "etc", "gitconfig"),
+            },
+            paths,
+        )
+
+    @skipIf(sys.platform != "win32", "Windows specfic config location.")
+    def test_windows_config_from_reg(self):
+        import winreg
+
+        from dulwich.config import get_win_system_paths
+
+        del os.environ["PATH"]
+        install_dir = os.path.join("C:", "foo", "Git")
+        with patch("winreg.OpenKey"):
+            with patch(
+                "winreg.QueryValueEx",
+                return_value=(install_dir, winreg.REG_SZ),
+            ):
+                paths = set(get_win_system_paths())
+        self.assertEqual(
+            {
+                os.path.join(os.environ.get("ProgramData"), "Git", "config"),
+                os.path.join(install_dir, "etc", "gitconfig"),
+            },
+            paths,
+        )
+
 
 class EscapeValueTests(TestCase):
     def test_nothing(self):