OLD | NEW |
1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import base64 | 5 import base64 |
6 import json | 6 import json |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import sys | 9 import sys |
10 import urllib2 | 10 import urllib2 |
(...skipping 12 matching lines...) Expand all Loading... |
23 def __init__(self, host): | 23 def __init__(self, host): |
24 self.host = host | 24 self.host = host |
25 self.user = self.host.environ.get('GH_USER') | 25 self.user = self.host.environ.get('GH_USER') |
26 self.token = self.host.environ.get('GH_TOKEN') | 26 self.token = self.host.environ.get('GH_TOKEN') |
27 | 27 |
28 assert self.user and self.token, 'must have GH_USER and GH_TOKEN env var
s' | 28 assert self.user and self.token, 'must have GH_USER and GH_TOKEN env var
s' |
29 | 29 |
30 def auth_token(self): | 30 def auth_token(self): |
31 return base64.encodestring('{}:{}'.format(self.user, self.token)).strip(
) | 31 return base64.encodestring('{}:{}'.format(self.user, self.token)).strip(
) |
32 | 32 |
33 def create_pr(self, local_branch_name, desc_title, body): | 33 def request(self, path, method, body=None): |
| 34 assert path.startswith('/') |
| 35 |
| 36 if body: |
| 37 body = json.dumps(body) |
| 38 |
| 39 opener = urllib2.build_opener(urllib2.HTTPHandler) |
| 40 req = urllib2.Request(url=API_BASE + path, data=body) |
| 41 req.add_header('Accept', 'application/vnd.github.v3+json') |
| 42 req.add_header('Authorization', 'Basic {}'.format(self.auth_token())) |
| 43 req.get_method = lambda: method |
| 44 res = opener.open(req) |
| 45 status_code = res.getcode() |
| 46 return json.load(res), status_code |
| 47 |
| 48 def create_pr(self, remote_branch_name, desc_title, body): |
34 """Creates a PR on GitHub. | 49 """Creates a PR on GitHub. |
35 | 50 |
36 API doc: https://developer.github.com/v3/pulls/#create-a-pull-request | 51 API doc: https://developer.github.com/v3/pulls/#create-a-pull-request |
37 | 52 |
38 Returns: | 53 Returns: |
39 A raw response object if successful, None if not. | 54 A raw response object if successful, None if not. |
40 """ | 55 """ |
41 assert local_branch_name | 56 assert remote_branch_name |
42 assert desc_title | 57 assert desc_title |
43 assert body | 58 assert body |
44 | 59 |
45 pr_branch_name = '{}:{}'.format(self.user, local_branch_name) | |
46 | |
47 # TODO(jeffcarp): CC foolip and qyearsley on all PRs for now | 60 # TODO(jeffcarp): CC foolip and qyearsley on all PRs for now |
48 # TODO(jeffcarp): add HTTP to Host and use that here | 61 # TODO(jeffcarp): add HTTP to Host and use that here |
49 path = '/repos/w3c/web-platform-tests/pulls' | 62 path = '/repos/w3c/web-platform-tests/pulls' |
50 body = { | 63 body = { |
51 "title": desc_title, | 64 "title": desc_title, |
52 "body": body, | 65 "body": body, |
53 "head": pr_branch_name, | 66 "head": remote_branch_name, |
54 "base": 'master', | 67 "base": 'master', |
55 "labels": [EXPORT_LABEL] | |
56 } | 68 } |
57 data, status_code = self.request(path, body) | 69 data, status_code = self.request(path, method='POST', body=body) |
58 | 70 |
59 if status_code != 201: | 71 if status_code != 201: |
| 72 # TODO(jeffcarp): log more helpful info |
60 return None | 73 return None |
61 | 74 |
62 return data | 75 return data |
63 | 76 |
| 77 def add_label(self, number): |
| 78 path = '/repos/w3c/web-platform-tests/issues/%d/labels' % number |
| 79 body = [EXPORT_LABEL] |
| 80 return self.request(path, method='POST', body=body) |
| 81 |
64 def in_flight_pull_requests(self): | 82 def in_flight_pull_requests(self): |
65 url_encoded_label = EXPORT_LABEL.replace(' ', '%20') | 83 path = '/search/issues?q=repo:w3c/web-platform-tests%20is:open%20type:pr
%20label:{}'.format(EXPORT_LABEL) |
66 path = '/search/issues?q=repo:w3c/web-platform-tests%20is:open%20type:pr
%20labels:{}'.format(url_encoded_label) | 84 data, status_code = self.request(path, method='GET') |
67 data, status_code = self.request(path) | |
68 if status_code == 200: | 85 if status_code == 200: |
69 return data['items'] | 86 return data['items'] |
70 else: | 87 else: |
71 raise Exception('Non-200 status code (%s): %s' % (status_code, data)
) | 88 raise Exception('Non-200 status code (%s): %s' % (status_code, data)
) |
72 | 89 |
73 def request(self, path, body=None): | |
74 assert path.startswith('/') | |
75 | |
76 if body: | |
77 body = json.dumps(body) | |
78 | |
79 req = urllib2.Request(url=API_BASE + path, data=body) | |
80 req.add_header('Accept', 'application/vnd.github.v3+json') | |
81 req.add_header('Authorization', 'Basic {}'.format(self.auth_token())) | |
82 res = urllib2.urlopen(req) | |
83 status_code = res.getcode() | |
84 return json.load(res), status_code | |
85 | |
86 def merge_pull_request(self, pull_request_number): | 90 def merge_pull_request(self, pull_request_number): |
87 path = '/repos/w3c/web-platform-tests/pulls/%d/merge' % pull_request_num
ber | 91 path = '/repos/w3c/web-platform-tests/pulls/%d/merge' % pull_request_num
ber |
88 body = {} | 92 body = {} |
89 response, content = self.request(path, body) | 93 data, status_code = self.request(path, method='PUT', body=body) |
90 | 94 |
91 if response['status'] == '200': | 95 if status_code == 200: |
92 return json.loads(content) | 96 return data, status_code |
93 else: | 97 else: |
94 raise Exception('PR could not be merged: %d' % pull_request_number) | 98 raise Exception('PR could not be merged: %d' % pull_request_number) |
OLD | NEW |