@@ -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
88131end
0 commit comments