diff --git a/ci/repokitteh/modules/ownerscheck.star b/ci/repokitteh/modules/ownerscheck.star index e93010f89a7f6..08b728a9daccf 100644 --- a/ci/repokitteh/modules/ownerscheck.star +++ b/ci/repokitteh/modules/ownerscheck.star @@ -8,7 +8,8 @@ # "path": "api/", # "label": "api", # "allow_global_approval": True, -# "github_status_label" = "any API change", +# "github_status_label": "any API change", +# "auto_assign": True, # }, # ], # ) @@ -27,8 +28,13 @@ # # 'label' refers to a GitHub label applied to any matching PR. The GitHub check status # can be customized with `github_status_label`. +# +# If 'auto_assign' is set True, a randomly selected reviwer from the owner team will +# be selected and set as a reviewer on the PR if there is not already a member of the +# owner team set as reviewer or assignee for the PR. load("text", "match") +load("time", "now") load("github.com/repokitteh/modules/lib/utils.star", "react") def _store_partial_approval(who, files): @@ -64,7 +70,8 @@ def _get_relevant_specs(specs, changed_files): label=spec.get("label", None), path_match=path_match, allow_global_approval=allow_global_approval, - status_label=status_label)) + status_label=status_label, + auto_assign=spec.get("auto_assign", False))) print("specs: %s" % relevant) @@ -152,20 +159,19 @@ def _reconcile(config, specs=None): return results -def _comment(config, results, force=False): +def _comment(config, results, assignees, sender, force=False): lines = [] for spec, approved in results: if approved: continue - mention = spec.owner + owner = spec.owner - if mention[0] != '@': - mention = '@' + mention + if owner[-1] == '!': + owner = owner[:-1] - if mention[-1] == '!': - mention = mention[:-1] + mention = '@' + owner match_description = spec.path_match if match_description: @@ -185,21 +191,40 @@ def _comment(config, results, force=False): elif mode == 'fyi': lines.append('CC %s: FYI only%s.' % (mention, match_description)) + if mode != 'skip' and spec.auto_assign: + api_assignee = None + # Find owners via github.team_get_by_name, github.team_list_members + team_name = owner.split('/')[1] + team = github.team_get_by_name(team_name) + # Exclude author from assignment. + members = [m['login'] for m in github.team_list_members(team['id']) if m['login'] != sender] + # Is a team member already assigned? The first assigned team member is picked. Bad O(n^2) as + # Starlark doesn't have sets, n is small. + for assignee in assignees: + if assignee in members: + api_assignee = assignee + break + # Otherwise, pick at "random" (we just use timestamp). + if not api_assignee: + api_assignee = members[now().second % len(members)] + lines.append('API shepherd assignee is @%s' % api_assignee) + github.issue_assign(api_assignee) + if lines: github.issue_create_comment('\n'.join(lines)) -def _reconcile_and_comment(config): - _comment(config, _reconcile(config)) +def _reconcile_and_comment(config, assignees, sender): + _comment(config, _reconcile(config), assignees, sender) -def _force_reconcile_and_comment(config): - _comment(config, _reconcile(config), force=True) +def _force_reconcile_and_comment(config, assignees, sender): + _comment(config, _reconcile(config), assignees, sender, force=True) -def _pr(action, config): +def _pr(action, config, assignees, sender): if action in ['synchronize', 'opened']: - _reconcile_and_comment(config) + _reconcile_and_comment(config, assignees, sender) def _pr_review(action, review_state, config): diff --git a/repokitteh.star b/repokitteh.star index bf4c78b592915..5181346d560a0 100644 --- a/repokitteh.star +++ b/repokitteh.star @@ -21,6 +21,7 @@ use( "path": "api/envoy/", "label": "api", "github_status_label": "any API change", + "auto_assign": True, }, { "owner": "envoyproxy/api-watchers",