Przeglądaj źródła

Support core.sshCommand (#1550)

Jelmer Vernooij 2 tygodni temu
rodzic
commit
3b887ee569
4 zmienionych plików z 68 dodań i 8 usunięć
  1. 4 1
      NEWS
  2. 17 5
      dulwich/client.py
  3. 2 2
      tests/__init__.py
  4. 45 0
      tests/test_client.py

+ 4 - 1
NEWS

@@ -10,7 +10,10 @@
  * ``Config.set`` replaces values by default, ``Config.add``
    appends them. (Jelmer Vernooij, #1545)
 
-* Bump PyO3 to 0.25. (Jelmer Vernooij)
+ * Support ``core.sshCommand`` setting.
+   (Jelmer Vernooij, #1548)
+
+ * Bump PyO3 to 0.25. (Jelmer Vernooij)
 
 0.22.8	2025-03-02
 

+ 17 - 5
dulwich/client.py

@@ -2125,9 +2125,21 @@ class SSHGitClient(TraditionalGitClient):
         self.username = username
         self.password = password
         self.key_filename = key_filename
-        self.ssh_command = ssh_command or os.environ.get(
-            "GIT_SSH_COMMAND", os.environ.get("GIT_SSH")
-        )
+        # Priority: ssh_command parameter, then env vars, then core.sshCommand config
+        if ssh_command:
+            self.ssh_command = ssh_command
+        else:
+            # Check environment variables first
+            self.ssh_command = os.environ.get(
+                "GIT_SSH_COMMAND", os.environ.get("GIT_SSH")
+            )
+
+            # Fall back to config if no environment variable set
+            if not self.ssh_command and config is not None:
+                config_ssh_command = config.get((b"core",), b"sshCommand")
+                self.ssh_command = (
+                    config_ssh_command.decode() if config_ssh_command else None
+                )
         super().__init__(**kwargs)
         self.alternative_paths: dict[bytes, bytes] = {}
         if vendor is not None:
@@ -2920,7 +2932,7 @@ def _get_transport_and_path_from_url(url, config, operation, **kwargs):
     if parsed.scheme == "git":
         return (TCPGitClient.from_parsedurl(parsed, **kwargs), parsed.path)
     elif parsed.scheme in ("git+ssh", "ssh"):
-        return SSHGitClient.from_parsedurl(parsed, **kwargs), parsed.path
+        return SSHGitClient.from_parsedurl(parsed, config=config, **kwargs), parsed.path
     elif parsed.scheme in ("http", "https"):
         return (
             HttpGitClient.from_parsedurl(parsed, config=config, **kwargs),
@@ -2997,7 +3009,7 @@ def get_transport_and_path(
         # Otherwise, assume it's a local path.
         return default_local_git_client_cls(**kwargs), location
     else:
-        return SSHGitClient(hostname, username=username, **kwargs), path
+        return SSHGitClient(hostname, username=username, config=config, **kwargs), path
 
 
 DEFAULT_GIT_CREDENTIALS_PATHS = [

+ 2 - 2
tests/__init__.py

@@ -53,13 +53,13 @@ class TestCase(_TestCase):
         def restore() -> None:
             if oldval is not None:
                 os.environ[name] = oldval
-            else:
+            elif name in os.environ:
                 del os.environ[name]
 
         oldval = os.environ.get(name)
         if value is not None:
             os.environ[name] = value
-        else:
+        elif name in os.environ:
             del os.environ[name]
         self.addCleanup(restore)
 

+ 45 - 0
tests/test_client.py

@@ -527,6 +527,24 @@ class TestGetTransportAndPath(TestCase):
         self.assertIsInstance(c, LocalGitClient)
         self.assertEqual("foo.bar/baz", path)
 
+    def test_ssh_with_config(self) -> None:
+        # Test that core.sshCommand from config is passed to SSHGitClient
+        from dulwich.config import ConfigDict
+
+        config = ConfigDict()
+        config.set((b"core",), b"sshCommand", b"custom-ssh -o CustomOption=yes")
+
+        c, path = get_transport_and_path(
+            "ssh://git@github.com/user/repo.git", config=config
+        )
+        self.assertIsInstance(c, SSHGitClient)
+        self.assertEqual("custom-ssh -o CustomOption=yes", c.ssh_command)
+
+        # Test rsync-style URL also gets the config
+        c, path = get_transport_and_path("git@github.com:user/repo.git", config=config)
+        self.assertIsInstance(c, SSHGitClient)
+        self.assertEqual("custom-ssh -o CustomOption=yes", c.ssh_command)
+
     @skipIf(sys.platform != "win32", "Behaviour only happens on windows.")
     def test_local_abs_windows_path(self) -> None:
         c, path = get_transport_and_path("C:\\foo.bar\\baz")
@@ -818,6 +836,33 @@ class SSHGitClientTests(TestCase):
         test_client = SSHGitClient("git.samba.org", ssh_command="ssh -o Option1=Value1")
         self.assertEqual(test_client.ssh_command, "ssh -o Option1=Value1")
 
+    def test_ssh_command_config(self) -> None:
+        # Test core.sshCommand config setting
+        from dulwich.config import ConfigDict
+
+        # No config, no environment - should be None
+        self.overrideEnv("GIT_SSH", None)
+        self.overrideEnv("GIT_SSH_COMMAND", None)
+        test_client = SSHGitClient("git.samba.org")
+        self.assertIsNone(test_client.ssh_command)
+
+        # Config with core.sshCommand
+        config = ConfigDict()
+        config.set((b"core",), b"sshCommand", b"ssh -o StrictHostKeyChecking=no")
+        test_client = SSHGitClient("git.samba.org", config=config)
+        self.assertEqual(test_client.ssh_command, "ssh -o StrictHostKeyChecking=no")
+
+        # ssh_command parameter takes precedence over config
+        test_client = SSHGitClient(
+            "git.samba.org", config=config, ssh_command="custom-ssh"
+        )
+        self.assertEqual(test_client.ssh_command, "custom-ssh")
+
+        # Environment variables take precedence over config when no ssh_command parameter
+        self.overrideEnv("GIT_SSH_COMMAND", "/usr/bin/ssh -v")
+        test_client = SSHGitClient("git.samba.org", config=config)
+        self.assertEqual(test_client.ssh_command, "/usr/bin/ssh -v")
+
 
 class ReportStatusParserTests(TestCase):
     def test_invalid_pack(self) -> None: