blob: 39a7349cb16b45fa912dfdf11daa945e428a2c7c [file] [log] [blame]
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -07001# Copyright (C) 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the 'License');
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an 'AS IS' BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070014"""Find main reviewers for git push commands."""
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -070015
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070016import math
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -070017import random
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -070018from typing import List, Mapping, Set, Union
19
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070020# To randomly pick one of multiple reviewers, we put them in a List[str]
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -070021# to work with random.choice efficiently.
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070022# To pick all of multiple reviewers, we use a Set[str].
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -070023
24# A ProjMapping maps a project path string to
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070025# (1) a single reviewer email address as a string, or
26# (2) a List of multiple reviewers to be randomly picked, or
27# (3) a Set of multiple reviewers to be all added.
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -070028ProjMapping = Mapping[str, Union[str, List[str], Set[str]]]
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -070029
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070030# Rust crate owners (reviewers).
31RUST_CRATE_OWNERS: ProjMapping = {
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -070032 'rust/crates/anyhow': 'mmaurer@google.com',
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070033 # more rust crate owners could be added later
Joel Galenson6592d492021-07-26 10:31:54 -070034 # if so, consider modifying the quotas in RUST_REVIEWERS
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -070035}
36
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070037PROJ_REVIEWERS: ProjMapping = {
38 # define non-rust project reviewers here
39}
40
41# Combine all roject reviewers.
42PROJ_REVIEWERS.update(RUST_CRATE_OWNERS)
43
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -070044# Reviewers for external/rust/crates projects not found in PROJ_REVIEWER.
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070045# Each person has a quota, the number of projects to review.
Joel Galenson6592d492021-07-26 10:31:54 -070046# The sum of these quotas should ideally be at least the number of Rust
47# projects, but this only matters if we have many entries in RUST_CRATE_OWNERS,
48# as we subtract a person's owned crates from their quota.
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070049RUST_REVIEWERS: Mapping[str, int] = {
Chih-Hung Hsieh470cdc42021-01-11 15:06:55 -080050 'ivanlozano@google.com': 20,
51 'jeffv@google.com': 20,
Chih-Hung Hsieh470cdc42021-01-11 15:06:55 -080052 'mmaurer@google.com': 20,
53 'srhines@google.com': 20,
54 'tweek@google.com': 20,
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070055 # If a Rust reviewer needs to take a vacation, comment out the line,
56 # and distribute the quota to other reviewers.
57}
58
59
Thiébaud Weksteen4ac289b2020-09-28 15:23:29 +020060# pylint: disable=invalid-name
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070061def add_proj_count(projects: Mapping[str, float], reviewer: str, n: float):
62 """Add n to the number of projects owned by the reviewer."""
63 if reviewer in projects:
64 projects[reviewer] += n
65 else:
66 projects[reviewer] = n
67
68
69# Random Rust reviewers are selected from RUST_REVIEWER_LIST,
70# which is created from RUST_REVIEWERS and PROJ_REVIEWERS.
71# A person P in RUST_REVIEWERS will occur in the RUST_REVIEWER_LIST N times,
72# if N = RUST_REVIEWERS[P] - (number of projects owned by P in PROJ_REVIEWERS)
73# is greater than 0. N is rounded up by math.ceil.
74def create_rust_reviewer_list() -> List[str]:
75 """Create a list of duplicated reviewers for weighted random selection."""
76 # Count number of projects owned by each reviewer.
77 rust_reviewers = set(RUST_REVIEWERS.keys())
78 projects = {} # map from owner to number of owned projects
79 for value in PROJ_REVIEWERS.values():
80 if isinstance(value, str): # single reviewer for a project
81 add_proj_count(projects, value, 1)
82 continue
83 # multiple reviewers share one project, count only rust_reviewers
Thiébaud Weksteen4ac289b2020-09-28 15:23:29 +020084 # pylint: disable=bad-builtin
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -070085 reviewers = set(filter(lambda x: x in rust_reviewers, value))
86 if reviewers:
87 count = 1.0 / len(reviewers) # shared among all reviewers
88 for name in reviewers:
89 add_proj_count(projects, name, count)
90 result = []
91 for (x, n) in RUST_REVIEWERS.items():
92 if x in projects: # reduce x's quota by the number of assigned ones
93 n = n - projects[x]
94 if n > 0:
95 result.extend([x] * math.ceil(n))
96 if result:
97 return result
98 # Something was wrong or quotas were too small so that nobody
99 # was selected from the RUST_REVIEWERS. Select everyone!!
100 return list(RUST_REVIEWERS.keys())
101
102
103RUST_REVIEWER_LIST: List[str] = create_rust_reviewer_list()
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -0700104
105
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -0700106def find_reviewers(proj_path: str) -> str:
107 """Returns an empty string or a reviewer parameter(s) for git push."""
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -0700108 index = proj_path.find('/external/')
109 if index >= 0: # full path
110 proj_path = proj_path[(index + len('/external/')):]
111 elif proj_path.startswith('external/'): # relative path
112 proj_path = proj_path[len('external/'):]
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -0700113 if proj_path in PROJ_REVIEWERS:
114 reviewers = PROJ_REVIEWERS[proj_path]
Thiébaud Weksteen4ac289b2020-09-28 15:23:29 +0200115 # pylint: disable=isinstance-second-argument-not-valid-type
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -0700116 if isinstance(reviewers, List): # pick any one reviewer
117 return 'r=' + random.choice(reviewers)
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -0700118 if isinstance(reviewers, Set): # add all reviewers in sorted order
Thiébaud Weksteen4ac289b2020-09-28 15:23:29 +0200119 # pylint: disable=bad-builtin
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -0700120 return ','.join(map(lambda x: 'r=' + x, sorted(reviewers)))
Chih-Hung Hsieh4f2dd232020-08-19 18:52:53 -0700121 # reviewers must be a string
122 return 'r=' + reviewers
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -0700123 if proj_path.startswith('rust/crates/'):
Chih-Hung Hsiehaa485552020-08-20 15:45:09 -0700124 return 'r=' + random.choice(RUST_REVIEWER_LIST)
Chih-Hung Hsiehb298bcd2020-08-17 23:13:59 -0700125 return ''