소스 검색

Add support for dulwich verify-tag command

Closes: #1833
Jelmer Vernooij 3 달 전
부모
커밋
494ffc4a10
3개의 변경된 파일95개의 추가작업 그리고 0개의 파일을 삭제
  1. 3 0
      NEWS
  2. 52 0
      dulwich/cli.py
  3. 40 0
      tests/test_cli.py

+ 3 - 0
NEWS

@@ -19,6 +19,9 @@
    after worktrees or the main repository have been moved.
    (Jelmer Vernooij, #1799)
 
+ * Add ``dulwich verify-tag`` command to check GPG signatures of tags.
+   (Jelmer Vernooij, #1833)
+
 0.24.2	2025-09-25
 
  * Added ``porcelain.shortlog`` function to summarize commits by author,

+ 52 - 0
dulwich/cli.py

@@ -1615,6 +1615,57 @@ class cmd_tag(Command):
         )
 
 
+class cmd_verify_tag(Command):
+    """Check the GPG signature of tags."""
+
+    def run(self, args: Sequence[str]) -> Optional[int]:
+        """Execute the verify-tag 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 tag 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("tags", nargs="+", help="Tags to verify")
+        parsed_args = parser.parse_args(args)
+
+        exit_code = None
+        for tag in parsed_args.tags:
+            try:
+                if parsed_args.verbose:
+                    # Show tag contents before verification
+                    porcelain.show(
+                        ".",
+                        objects=[tag],
+                        outstream=sys.stdout,
+                    )
+                porcelain.verify_tag(".", tag)
+                if not parsed_args.raw:
+                    print(f"gpg: Good signature from tag '{tag}'")
+            except Exception as e:
+                if not parsed_args.raw:
+                    print(f"error: {tag}: {e}", file=sys.stderr)
+                else:
+                    # In raw mode, let the exception propagate
+                    raise
+                exit_code = 1
+
+        return exit_code
+
+
 class cmd_repack(Command):
     """Pack unpacked objects in a repository."""
 
@@ -4488,6 +4539,7 @@ commands = {
     "unpack-objects": cmd_unpack_objects,
     "update-server-info": cmd_update_server_info,
     "upload-pack": cmd_upload_pack,
+    "verify-tag": cmd_verify_tag,
     "web-daemon": cmd_web_daemon,
     "worktree": cmd_worktree,
     "write-tree": cmd_write_tree,

+ 40 - 0
tests/test_cli.py

@@ -865,6 +865,46 @@ class TagCommandTest(DulwichCliTestCase):
         self.assertIn(b"refs/tags/v1.0", self.repo.refs.keys())
 
 
+class VerifyTagCommandTest(DulwichCliTestCase):
+    """Tests for verify-tag command."""
+
+    def test_verify_tag_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")
+
+        # Create an annotated tag
+        self._run_cli("tag", "--annotated", "v1.0")
+
+        # Mock the porcelain.verify_tag function since we don't have GPG setup
+        with patch("dulwich.cli.porcelain.verify_tag") as mock_verify:
+            _result, stdout, _stderr = self._run_cli("verify-tag", "v1.0")
+            mock_verify.assert_called_once_with(".", "v1.0")
+            self.assertIn("Good signature", stdout)
+
+    def test_verify_tag_multiple(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")
+
+        # Create multiple annotated tags
+        self._run_cli("tag", "--annotated", "v1.0")
+        self._run_cli("tag", "--annotated", "v2.0")
+
+        # Mock the porcelain.verify_tag function
+        with patch("dulwich.cli.porcelain.verify_tag") as mock_verify:
+            _result, stdout, _stderr = self._run_cli("verify-tag", "v1.0", "v2.0")
+            self.assertEqual(mock_verify.call_count, 2)
+            self.assertIn("v1.0", stdout)
+            self.assertIn("v2.0", stdout)
+
+
 class DiffCommandTest(DulwichCliTestCase):
     """Tests for diff command."""