Преглед на файлове

Select a minimal number of owners for a set of files

This CL adds a function which takes a set of files, and attempts to
select a single owner for all of them. If it cannot, it falls back to
the standard owner selection algorithm, which may result in more owners
being chosen than necessary, but guarantees that a valid set of owners
is always returned.

Bug: 389069356
Change-Id: I985804040f149a02bfb5b3c6b946602a81334e7c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/6321289
Reviewed-by: Josip Sokcevic <sokcevic@chromium.org>
Commit-Queue: Devon Loehr <dloehr@google.com>
Devon Loehr преди 5 месеца
родител
ревизия
c48f866fcf
променени са 2 файла, в които са добавени 62 реда и са изтрити 0 реда
  1. 35 0
      owners_client.py
  2. 27 0
      tests/owners_client_test.py

+ 35 - 0
owners_client.py

@@ -116,6 +116,41 @@ class OwnersClient(object):
 
         return selected
 
+    def SuggestMinimalOwners(self,
+                             paths: list[str],
+                             exclude: list[str] = None) -> list[str]:
+        """
+        Suggest a set of owners for the given paths. Never return an owner in
+        the |exclude| list.
+
+        Aims to provide only one, but will provide more if it's unable to
+        find a common owner.
+        """
+        exclude = exclude or []
+
+        owners_by_path = self.BatchListOwners(paths)
+        if not owners_by_path:
+            return []
+
+        common_owners = set(owners_by_path.popitem()[1]) - set(exclude)
+        for _, owners in owners_by_path.items():
+            common_owners = common_owners.intersection(set(owners))
+
+        if not common_owners:
+            # This likely means some of the files had `noparent` set.
+            # Fall back to the default suggestion algorithm, which accounts
+            # for noparent but is liable to return many different owners
+            return self.SuggestOwners(paths, exclude)
+
+        # Return an arbitrary common owner, preferring those with a good score
+        sorted_common_owners = [
+            owner for owner in self.ScoreOwners(paths, exclude=exclude)
+            if owner in common_owners
+        ]
+
+        # Return a singleton list so this function has a consistent return type
+        return sorted_common_owners[:1]
+
 
 class GerritClient(OwnersClient):
     """Implement OwnersClient using OWNERS REST API."""

+ 27 - 0
tests/owners_client_test.py

@@ -244,6 +244,33 @@ class OwnersClientTest(unittest.TestCase):
             self.client.BatchListOwners(
                 ['bar/everyone/foo.txt', 'bar/everyone/bar.txt', 'bar/foo/']))
 
+    def testSuggestMinimalOwners(self):
+        self.client.owners_by_path = {
+            'bar/everyone/foo.txt': [alice, bob, emily],
+            'bar/everyone/bar.txt': [bob, emily],
+            'bar/foo/': [bob, chris, emily],
+            'baz/baz/baz': [chris, dave, emily]
+        }
+
+        self.assertEqual([bob],
+                         self.client.SuggestMinimalOwners([
+                             'bar/everyone/foo.txt', 'bar/everyone/bar.txt',
+                             'bar/foo/'
+                         ]))
+
+        self.assertEqual([chris],
+                         self.client.SuggestMinimalOwners(
+                             ['bar/foo/', 'baz/baz/baz']))
+
+        # If no common owner exists, fallback to returning multiple owners
+        self.assertEqual([alice, bob, chris],
+                         self.client.SuggestMinimalOwners([
+                             'bar/everyone/foo.txt', 'bar/everyone/bar.txt',
+                             'bar/foo/', 'baz/baz/baz'
+                         ],
+                                                          exclude=[emily]))
+
+
 
 class GetCodeOwnersClientTest(unittest.TestCase):
     def setUp(self):