Explorar el Código

Add support for dulwich verify-commit command (#1915)

Fixes #1832
Jelmer Vernooij hace 3 meses
padre
commit
d912eaaffd
Se han modificado 3 ficheros con 113 adiciones y 0 borrados
  1. 3 0
      NEWS
  2. 57 0
      dulwich/cli.py
  3. 53 0
      tests/test_cli.py

+ 3 - 0
NEWS

@@ -27,6 +27,9 @@
  * Add ``dulwich verify-tag`` command to check GPG signatures of tags.
    (Jelmer Vernooij, #1833)
 
+ * Add ``dulwich verify-commit`` command to check GPG signatures of tags.
+   (Jelmer Vernooij, #1832)
+
 0.24.2	2025-09-25
 
  * Added ``porcelain.shortlog`` function to summarize commits by author,

+ 57 - 0
dulwich/cli.py

@@ -1660,6 +1660,62 @@ class cmd_tag(Command):
         )
 
 
+class cmd_verify_commit(Command):
+    """Check the GPG signature of commits."""
+
+    def run(self, args: Sequence[str]) -> Optional[int]:
+        """Execute the verify-commit command.
+
+        Args:
+            args: Command line arguments
+
+        Returns:
+            Exit code (1 on error, None on success)
+        """
+        parser = argparse.ArgumentParser()
+        parser.add_argument(
+            "-v",
+            "--verbose",
+            help="Print the contents of the commit object before validating it.",
+            action="store_true",
+        )
+        parser.add_argument(
+            "--raw",
+            help="Print the raw gpg status output to standard error.",
+            action="store_true",
+        )
+        parser.add_argument(
+            "commits",
+            nargs="*",
+            default=["HEAD"],
+            help="Commits to verify (defaults to HEAD)",
+        )
+        parsed_args = parser.parse_args(args)
+
+        exit_code = None
+        for commit in parsed_args.commits:
+            try:
+                if parsed_args.verbose:
+                    # Show commit contents before verification
+                    porcelain.show(
+                        ".",
+                        objects=[commit],
+                        outstream=sys.stdout,
+                    )
+                porcelain.verify_commit(".", commit)
+                if not parsed_args.raw:
+                    print(f"gpg: Good signature from commit '{commit}'")
+            except Exception as e:
+                if not parsed_args.raw:
+                    print(f"error: {commit}: {e}", file=sys.stderr)
+                else:
+                    # In raw mode, let the exception propagate
+                    raise
+                exit_code = 1
+
+        return exit_code
+
+
 class cmd_verify_tag(Command):
     """Check the GPG signature of tags."""
 
@@ -4585,6 +4641,7 @@ commands = {
     "update-server-info": cmd_update_server_info,
     "upload-pack": cmd_upload_pack,
     "var": cmd_var,
+    "verify-commit": cmd_verify_commit,
     "verify-tag": cmd_verify_tag,
     "web-daemon": cmd_web_daemon,
     "worktree": cmd_worktree,

+ 53 - 0
tests/test_cli.py

@@ -865,6 +865,59 @@ class TagCommandTest(DulwichCliTestCase):
         self.assertIn(b"refs/tags/v1.0", self.repo.refs.keys())
 
 
+class VerifyCommitCommandTest(DulwichCliTestCase):
+    """Tests for verify-commit command."""
+
+    def test_verify_commit_basic(self):
+        # Create initial commit
+        test_file = os.path.join(self.repo_path, "test.txt")
+        with open(test_file, "w") as f:
+            f.write("test")
+        self._run_cli("add", "test.txt")
+        self._run_cli("commit", "--message=Initial")
+
+        # Mock the porcelain.verify_commit function since we don't have GPG setup
+        with patch("dulwich.cli.porcelain.verify_commit") as mock_verify:
+            _result, stdout, _stderr = self._run_cli("verify-commit", "HEAD")
+            mock_verify.assert_called_once_with(".", "HEAD")
+            self.assertIn("Good signature", stdout)
+
+    def test_verify_commit_multiple(self):
+        # Create multiple commits
+        test_file = os.path.join(self.repo_path, "test.txt")
+        with open(test_file, "w") as f:
+            f.write("test1")
+        self._run_cli("add", "test.txt")
+        self._run_cli("commit", "--message=First")
+
+        with open(test_file, "w") as f:
+            f.write("test2")
+        self._run_cli("add", "test.txt")
+        self._run_cli("commit", "--message=Second")
+
+        # Mock the porcelain.verify_commit function
+        with patch("dulwich.cli.porcelain.verify_commit") as mock_verify:
+            _result, stdout, _stderr = self._run_cli("verify-commit", "HEAD", "HEAD~1")
+            self.assertEqual(mock_verify.call_count, 2)
+            self.assertIn("HEAD", stdout)
+            self.assertIn("HEAD~1", stdout)
+
+    def test_verify_commit_default_head(self):
+        # Create initial commit
+        test_file = os.path.join(self.repo_path, "test.txt")
+        with open(test_file, "w") as f:
+            f.write("test")
+        self._run_cli("add", "test.txt")
+        self._run_cli("commit", "--message=Initial")
+
+        # Mock the porcelain.verify_commit function
+        with patch("dulwich.cli.porcelain.verify_commit") as mock_verify:
+            # Test that verify-commit without arguments defaults to HEAD
+            _result, stdout, _stderr = self._run_cli("verify-commit")
+            mock_verify.assert_called_once_with(".", "HEAD")
+            self.assertIn("Good signature", stdout)
+
+
 class VerifyTagCommandTest(DulwichCliTestCase):
     """Tests for verify-tag command."""