8
8
# "path": "api/",
9
9
# "label": "api",
10
10
# "allow_global_approval": True,
11
- # "github_status_label" = "any API change",
11
+ # "github_status_label": "any API change",
12
+ # "auto_assign": True,
12
13
# },
13
14
# ],
14
15
# )
27
28
#
28
29
# 'label' refers to a GitHub label applied to any matching PR. The GitHub check status
29
30
# can be customized with `github_status_label`.
31
+ #
32
+ # If 'auto_assign' is set True, a randomly selected reviwer from the owner team will
33
+ # be selected and set as a reviewer on the PR if there is not already a member of the
34
+ # owner team set as reviewer or assignee for the PR.
30
35
31
36
load ("text" , "match" )
37
+ load ("time" , "now" )
32
38
load ("github.com/repokitteh/modules/lib/utils.star" , "react" )
33
39
34
40
def _store_partial_approval (who , files ):
@@ -64,7 +70,8 @@ def _get_relevant_specs(specs, changed_files):
64
70
label = spec .get ("label" , None ),
65
71
path_match = path_match ,
66
72
allow_global_approval = allow_global_approval ,
67
- status_label = status_label ))
73
+ status_label = status_label ,
74
+ auto_assign = spec .get ("auto_assign" , False )))
68
75
69
76
print ("specs: %s" % relevant )
70
77
@@ -152,20 +159,19 @@ def _reconcile(config, specs=None):
152
159
return results
153
160
154
161
155
- def _comment (config , results , force = False ):
162
+ def _comment (config , results , assignees , sender , force = False ):
156
163
lines = []
157
164
158
165
for spec , approved in results :
159
166
if approved :
160
167
continue
161
168
162
- mention = spec .owner
169
+ owner = spec .owner
163
170
164
- if mention [ 0 ] != '@ ' :
165
- mention = '@' + mention
171
+ if owner [ - 1 ] == '! ' :
172
+ owner = owner [: - 1 ]
166
173
167
- if mention [- 1 ] == '!' :
168
- mention = mention [:- 1 ]
174
+ mention = '@' + owner
169
175
170
176
match_description = spec .path_match
171
177
if match_description :
@@ -185,21 +191,40 @@ def _comment(config, results, force=False):
185
191
elif mode == 'fyi' :
186
192
lines .append ('CC %s: FYI only%s.' % (mention , match_description ))
187
193
194
+ if mode != 'skip' and spec .auto_assign :
195
+ api_assignee = None
196
+ # Find owners via github.team_get_by_name, github.team_list_members
197
+ team_name = owner .split ('/' )[1 ]
198
+ team = github .team_get_by_name (team_name )
199
+ # Exclude author from assignment.
200
+ members = [m ['login' ] for m in github .team_list_members (team ['id' ]) if m ['login' ] != sender ]
201
+ # Is a team member already assigned? The first assigned team member is picked. Bad O(n^2) as
202
+ # Starlark doesn't have sets, n is small.
203
+ for assignee in assignees :
204
+ if assignee in members :
205
+ api_assignee = assignee
206
+ break
207
+ # Otherwise, pick at "random" (we just use timestamp).
208
+ if not api_assignee :
209
+ api_assignee = members [now ().second % len (members )]
210
+ lines .append ('API shepherd assignee is @%s' % api_assignee )
211
+ github .issue_assign (api_assignee )
212
+
188
213
if lines :
189
214
github .issue_create_comment ('\n ' .join (lines ))
190
215
191
216
192
- def _reconcile_and_comment (config ):
193
- _comment (config , _reconcile (config ))
217
+ def _reconcile_and_comment (config , assignees , sender ):
218
+ _comment (config , _reconcile (config ), assignees , sender )
194
219
195
220
196
- def _force_reconcile_and_comment (config ):
197
- _comment (config , _reconcile (config ), force = True )
221
+ def _force_reconcile_and_comment (config , assignees , sender ):
222
+ _comment (config , _reconcile (config ), assignees , sender , force = True )
198
223
199
224
200
- def _pr (action , config ):
225
+ def _pr (action , config , assignees , sender ):
201
226
if action in ['synchronize' , 'opened' ]:
202
- _reconcile_and_comment (config )
227
+ _reconcile_and_comment (config , assignees , sender )
203
228
204
229
205
230
def _pr_review (action , review_state , config ):
0 commit comments