Skip to content
This repository was archived by the owner on Mar 14, 2023. It is now read-only.

Commit fcffc5d

Browse files
committed
Fix pagination of contributors
1 parent bfd3be8 commit fcffc5d

File tree

2 files changed

+86
-42
lines changed

2 files changed

+86
-42
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
config

newpr.py

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,90 @@
1212
import gzip
1313
cgitb.enable()
1414

15-
def is_new_contributor(username, stats):
16-
for contributor in stats:
17-
if contributor['login'] == username:
18-
return False
19-
return True
20-
21-
contributors_url = "https://api.github.com/repos/%s/%s/contributors?per_page=400"
22-
collaborators_url = "https://api.github.com/repos/%s/%s/collaborators"
15+
# Maximum per page is 100. Sorted by number of commits, so most of the time the
16+
# contributor will happen early,
17+
contributors_url = "https://api.github.com/repos/%s/%s/contributors?per_page=100"
2318
post_comment_url = "https://api.github.com/repos/%s/%s/issues/%s/comments"
2419

2520
welcome_msg = "Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @%s (or someone else) soon."
2621
warning_summary = '<img src="http://www.joshmatthews.net/warning.svg" alt="warning" height=20> **Warning** <img src="http://www.joshmatthews.net/warning.svg" alt="warning" height=20>\n\n%s'
2722
unsafe_warning_msg = 'These commits modify **unsafe code**. Please review it carefully!'
2823

24+
2925
def api_req(method, url, data=None, username=None, token=None, media_type=None):
30-
data = None if not data else json.dumps(data)
31-
headers = {} if not data else {'Content-Type': 'application/json'}
32-
req = urllib2.Request(url, data, headers)
33-
req.get_method = lambda: method
34-
if token:
35-
base64string = base64.standard_b64encode('%s:x-oauth-basic' % (token)).replace('\n', '')
36-
req.add_header("Authorization", "Basic %s" % base64string)
37-
38-
if media_type:
39-
req.add_header("Accept", media_type)
40-
f = urllib2.urlopen(req)
41-
if f.info().get('Content-Encoding') == 'gzip':
42-
buf = StringIO(f.read())
43-
f = gzip.GzipFile(fileobj=buf)
44-
return f.read()
26+
data = None if not data else json.dumps(data)
27+
headers = {} if not data else {'Content-Type': 'application/json'}
28+
req = urllib2.Request(url, data, headers)
29+
req.get_method = lambda: method
30+
if token:
31+
base64string = base64.standard_b64encode('%s:x-oauth-basic' % (token)).replace('\n', '')
32+
req.add_header("Authorization", "Basic %s" % base64string)
33+
34+
if media_type:
35+
req.add_header("Accept", media_type)
36+
f = urllib2.urlopen(req)
37+
header = f.info()
38+
if header.get('Content-Encoding') == 'gzip':
39+
buf = StringIO(f.read())
40+
f = gzip.GzipFile(fileobj=buf)
41+
body = f.read()
42+
return { "header": header, "body": body }
43+
44+
def post_comment(body, owner, repo, issue, user, token):
45+
global post_comment_url
46+
try:
47+
result = api_req("POST", post_comment_url % (owner, repo, issue), {"body": body}, user, token)['body']
48+
except urllib2.HTTPError, e:
49+
if e.code == 201:
50+
pass
51+
else:
52+
raise e
53+
54+
# This function is adapted from https://github.com/kennethreitz/requests/blob/209a871b638f85e2c61966f82e547377ed4260d9/requests/utils.py#L562
55+
# Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
56+
def parse_header_links(value):
57+
if not value:
58+
return None
59+
60+
links = {}
61+
replace_chars = " '\""
62+
for val in value.split(","):
63+
try:
64+
url, params = val.split(";", 1)
65+
except ValueError:
66+
url, params = val, ''
67+
68+
url = url.strip("<> '\"")
69+
70+
for param in params.split(";"):
71+
try:
72+
key, value = param.split("=")
73+
except ValueError:
74+
break
75+
key = key.strip(replace_chars)
76+
if key == 'rel':
77+
links[value.strip(replace_chars)] = url
78+
79+
return links
80+
81+
def is_new_contributor(username, owner, repo, user, token):
82+
# iterate through the pages to try and find the contributor
83+
url = contributors_url % (owner, repo)
84+
while True:
85+
print 'looking for contribs on ' + url
86+
stats_raw = api_req("GET", url, None, user, token)
87+
stats = json.loads(stats_raw['body'])
88+
links = parse_header_links(stats_raw['header'].get('Link'))
89+
90+
for contributor in stats:
91+
if contributor['login'] == username:
92+
return False
93+
94+
if not links or 'next' not in links:
95+
return True
96+
url = links['next']
97+
98+
4599

46100
print "Content-Type: text/html;charset=utf-8"
47101
print
@@ -55,34 +109,22 @@ def api_req(method, url, data=None, username=None, token=None, media_type=None):
55109
payload_raw = post.getfirst("payload",'')
56110
payload = json.loads(payload_raw)
57111
if payload["action"] != "opened":
58-
sys.exit(0)
112+
sys.exit(0)
59113

60114
owner = payload['pull_request']['base']['repo']['owner']['login']
61115
repo = payload['pull_request']['base']['repo']['name']
62-
stats_raw = api_req("GET", contributors_url % (owner, repo), None, user, token)
63-
stats = json.loads(stats_raw)
64116

65117
author = payload["pull_request"]['user']['login']
66118
issue = str(payload["number"])
67119

68-
def post_comment(body, owner, repo, issue, user, token):
69-
global post_comment_url
70-
try:
71-
result = api_req("POST", post_comment_url % (owner, repo, issue), {"body": body}, user, token)
72-
except urllib2.HTTPError, e:
73-
if e.code == 201:
74-
pass
75-
else:
76-
raise e
77-
78-
if is_new_contributor(author, stats):
79-
collaborators = ['brson', 'nikomatsakis', 'pcwalton', 'alexcrichton', 'aturon', 'huonw'] if repo == 'rust' and owner == 'rust-lang' else ['test_user_selection_ignore_this']
80-
random.seed()
81-
to_notify = random.choice(collaborators)
82-
post_comment(welcome_msg % to_notify, owner, repo, issue, user, token)
120+
if is_new_contributor(author, owner, repo, user, token):
121+
collaborators = ['brson', 'nikomatsakis', 'pcwalton', 'alexcrichton', 'aturon', 'huonw'] if repo == 'rust' and owner == 'rust-lang' else ['test_user_selection_ignore_this']
122+
random.seed()
123+
to_notify = random.choice(collaborators)
124+
post_comment(welcome_msg % to_notify, owner, repo, issue, user, token)
83125

84126
warn_unsafe = False
85-
diff = api_req("GET", payload["pull_request"]["diff_url"])
127+
diff = api_req("GET", payload["pull_request"]["diff_url"])['body']
86128
for line in diff.split('\n'):
87129
if line.startswith('+') and not line.startswith('+++') and line.find('unsafe') > -1:
88130
warn_unsafe = True
@@ -93,3 +135,4 @@ def post_comment(body, owner, repo, issue, user, token):
93135

94136
if warnings:
95137
post_comment(warning_summary % '\n'.join(map(lambda x: '* ' + x, warnings)), owner, repo, issue, user, token)
138+

0 commit comments

Comments
 (0)