Skip to content

Commit d0c1727

Browse files
committed
auto_review_pr: suggest re-filing PRs from fork branches
PRs filed from branches in ruby/ruby (rather than forks) make it harder to manage the bi-directional mirror with git.ruby-lang.org. Add an auto-review comment asking contributors to re-file from a fork. Refactor #review into separate methods for each review type.
1 parent 9dc87b1 commit d0c1727

1 file changed

Lines changed: 67 additions & 24 deletions

File tree

tool/auto_review_pr.rb

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,46 +34,89 @@ class AutoReviewPR
3434
REPO = 'ruby/ruby'
3535

3636
COMMENT_USER = 'github-actions[bot]'
37-
COMMENT_PREFIX = 'The following files are maintained in the following upstream repositories:'
38-
COMMENT_SUFFIX = 'Please file a pull request to the above instead. Thank you!'
37+
38+
UPSTREAM_COMMENT_PREFIX = 'The following files are maintained in the following upstream repositories:'
39+
UPSTREAM_COMMENT_SUFFIX = 'Please file a pull request to the above instead. Thank you!'
40+
41+
FORK_COMMENT_PREFIX = 'It looks like this pull request was filed from a branch in ruby/ruby.'
42+
FORK_COMMENT_BODY = <<~COMMENT
43+
#{FORK_COMMENT_PREFIX}
44+
45+
Since ruby/ruby is bi-directionally mirrored with the official git repository at git.ruby-lang.org, \
46+
having topic branches in ruby/ruby makes it harder to manage the mirror.
47+
48+
Could you please close this pull request and re-file it from a branch in your personal fork instead? \
49+
You can fork https://github.com/ruby/ruby, push your branch there, and open a new pull request from it.
50+
51+
Thank you for your contribution!
52+
COMMENT
3953

4054
def initialize(client)
4155
@client = client
4256
end
4357

4458
def review(pr_number)
45-
# Fetch the list of files changed by the PR
46-
changed_files = @client.get("/repos/#{REPO}/pulls/#{pr_number}/files").map { it.fetch(:filename) }
59+
existing_comments = fetch_existing_comments(pr_number)
60+
review_non_fork_branch(pr_number, existing_comments)
61+
review_upstream_repos(pr_number, existing_comments)
62+
end
4763

48-
# Build a Hash: { upstream_repo => files, ... }
49-
upstream_repos = SyncDefaultGems::Repository.group(changed_files)
50-
upstream_repos.delete(nil) # exclude no-upstream files
51-
upstream_repos.delete('prism') if changed_files.include?('prism_compile.c') # allow prism changes in this case
52-
if upstream_repos.empty?
53-
puts "Skipped: The PR ##{pr_number} doesn't have upstream repositories."
64+
private
65+
66+
def fetch_existing_comments(pr_number)
67+
comments = @client.get("/repos/#{REPO}/issues/#{pr_number}/comments")
68+
comments.map { [it.fetch(:user).fetch(:login), it.fetch(:body)] }
69+
end
70+
71+
def already_commented?(existing_comments, prefix)
72+
existing_comments.any? { |user, comment| user == COMMENT_USER && comment.start_with?(prefix) }
73+
end
74+
75+
def post_comment(pr_number, comment)
76+
result = @client.post("/repos/#{REPO}/issues/#{pr_number}/comments", { body: comment })
77+
puts "Success: #{JSON.pretty_generate(result)}"
78+
end
79+
80+
# Suggest re-filing from a fork if the PR branch is in ruby/ruby itself
81+
def review_non_fork_branch(pr_number, existing_comments)
82+
if already_commented?(existing_comments, FORK_COMMENT_PREFIX)
83+
puts "Skipped: The PR ##{pr_number} already has a fork branch comment."
5484
return
5585
end
5686

57-
# Check if the PR is already reviewed
58-
existing_comments = @client.get("/repos/#{REPO}/issues/#{pr_number}/comments")
59-
existing_comments.map! { [it.fetch(:user).fetch(:login), it.fetch(:body)] }
60-
if existing_comments.any? { |user, comment| user == COMMENT_USER && comment.start_with?(COMMENT_PREFIX) }
61-
puts "Skipped: The PR ##{pr_number} already has an automated review comment."
87+
pr = @client.get("/repos/#{REPO}/pulls/#{pr_number}")
88+
head_repo = pr.dig(:head, :repo, :full_name)
89+
if head_repo != REPO
90+
puts "Skipped: The PR ##{pr_number} is already from a fork (#{head_repo})."
6291
return
6392
end
6493

65-
# Post a comment
66-
comment = format_comment(upstream_repos)
67-
result = @client.post("/repos/#{REPO}/issues/#{pr_number}/comments", { body: comment })
68-
puts "Success: #{JSON.pretty_generate(result)}"
94+
post_comment(pr_number, FORK_COMMENT_BODY)
6995
end
7096

71-
private
97+
# Suggest filing PRs to upstream repositories for files that have one
98+
def review_upstream_repos(pr_number, existing_comments)
99+
if already_commented?(existing_comments, UPSTREAM_COMMENT_PREFIX)
100+
puts "Skipped: The PR ##{pr_number} already has an upstream repos comment."
101+
return
102+
end
103+
104+
changed_files = @client.get("/repos/#{REPO}/pulls/#{pr_number}/files").map { it.fetch(:filename) }
105+
106+
upstream_repos = SyncDefaultGems::Repository.group(changed_files)
107+
upstream_repos.delete(nil)
108+
upstream_repos.delete('prism') if changed_files.include?('prism_compile.c')
109+
if upstream_repos.empty?
110+
puts "Skipped: The PR ##{pr_number} doesn't have upstream repositories."
111+
return
112+
end
113+
114+
post_comment(pr_number, format_upstream_comment(upstream_repos))
115+
end
72116

73-
# upstream_repos: { upstream_repo => files, ... }
74-
def format_comment(upstream_repos)
117+
def format_upstream_comment(upstream_repos)
75118
comment = +''
76-
comment << "#{COMMENT_PREFIX}\n\n"
119+
comment << "#{UPSTREAM_COMMENT_PREFIX}\n\n"
77120

78121
upstream_repos.each do |upstream_repo, files|
79122
comment << "* https://github.com/ruby/#{upstream_repo}\n"
@@ -82,7 +125,7 @@ def format_comment(upstream_repos)
82125
end
83126
end
84127

85-
comment << "\n#{COMMENT_SUFFIX}"
128+
comment << "\n#{UPSTREAM_COMMENT_SUFFIX}"
86129
comment
87130
end
88131
end

0 commit comments

Comments
 (0)