##// END OF EJS Templates
enable posting GitHub downloads
MinRK -
Show More
@@ -1,104 +1,204 b''
1 """Functions for Github authorisation."""
1 """Functions for Github authorisation."""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 try:
4 try:
5 input = raw_input
5 input = raw_input
6 except NameError:
6 except NameError:
7 pass
7 pass
8
8
9 import os
10
9 import requests
11 import requests
10 import getpass
12 import getpass
11 import json
13 import json
12
14
13 # Keyring stores passwords by a 'username', but we're not storing a username and
15 # Keyring stores passwords by a 'username', but we're not storing a username and
14 # password
16 # password
15 fake_username = 'ipython_tools'
17 fake_username = 'ipython_tools'
16
18
17 token = None
19 token = None
18 def get_auth_token():
20 def get_auth_token():
19 global token
21 global token
20
22
21 if token is not None:
23 if token is not None:
22 return token
24 return token
23
25
24 import keyring
26 import keyring
25 token = keyring.get_password('github', fake_username)
27 token = keyring.get_password('github', fake_username)
26 if token is not None:
28 if token is not None:
27 return token
29 return token
28
30
29 print("Please enter your github username and password. These are not "
31 print("Please enter your github username and password. These are not "
30 "stored, only used to get an oAuth token. You can revoke this at "
32 "stored, only used to get an oAuth token. You can revoke this at "
31 "any time on Github.")
33 "any time on Github.")
32 user = input("Username: ")
34 user = input("Username: ")
33 pw = getpass.getpass("Password: ")
35 pw = getpass.getpass("Password: ")
34
36
35 auth_request = {
37 auth_request = {
36 "scopes": [
38 "scopes": [
37 "public_repo",
39 "public_repo",
38 "gist"
40 "gist"
39 ],
41 ],
40 "note": "IPython tools",
42 "note": "IPython tools",
41 "note_url": "https://github.com/ipython/ipython/tree/master/tools",
43 "note_url": "https://github.com/ipython/ipython/tree/master/tools",
42 }
44 }
43 response = requests.post('https://api.github.com/authorizations',
45 response = requests.post('https://api.github.com/authorizations',
44 auth=(user, pw), data=json.dumps(auth_request))
46 auth=(user, pw), data=json.dumps(auth_request))
45 response.raise_for_status()
47 response.raise_for_status()
46 token = json.loads(response.text)['token']
48 token = json.loads(response.text)['token']
47 keyring.set_password('github', fake_username, token)
49 keyring.set_password('github', fake_username, token)
48 return token
50 return token
49
51
50 def make_auth_header():
52 def make_auth_header():
51 return {'Authorization': 'token ' + get_auth_token()}
53 return {'Authorization': 'token ' + get_auth_token()}
52
54
53 def post_issue_comment(project, num, body):
55 def post_issue_comment(project, num, body):
54 url = 'https://api.github.com/repos/{project}/issues/{num}/comments'.format(project=project, num=num)
56 url = 'https://api.github.com/repos/{project}/issues/{num}/comments'.format(project=project, num=num)
55 payload = json.dumps({'body': body})
57 payload = json.dumps({'body': body})
56 r = requests.post(url, data=payload, headers=make_auth_header())
58 r = requests.post(url, data=payload, headers=make_auth_header())
57
59
58 def post_gist(content, description='', filename='file', auth=False):
60 def post_gist(content, description='', filename='file', auth=False):
59 """Post some text to a Gist, and return the URL."""
61 """Post some text to a Gist, and return the URL."""
60 post_data = json.dumps({
62 post_data = json.dumps({
61 "description": description,
63 "description": description,
62 "public": True,
64 "public": True,
63 "files": {
65 "files": {
64 filename: {
66 filename: {
65 "content": content
67 "content": content
66 }
68 }
67 }
69 }
68 }).encode('utf-8')
70 }).encode('utf-8')
69
71
70 headers = make_auth_header() if auth else {}
72 headers = make_auth_header() if auth else {}
71 response = requests.post("https://api.github.com/gists", data=post_data, headers=headers)
73 response = requests.post("https://api.github.com/gists", data=post_data, headers=headers)
72 response.raise_for_status()
74 response.raise_for_status()
73 response_data = json.loads(response.text)
75 response_data = json.loads(response.text)
74 return response_data['html_url']
76 return response_data['html_url']
75
77
76 def get_pull_request(project, num, github_api=3):
78 def get_pull_request(project, num, github_api=3):
77 """get pull request info by number
79 """get pull request info by number
78
80
79 github_api : version of github api to use
81 github_api : version of github api to use
80 """
82 """
81 if github_api==2 :
83 if github_api==2 :
82 url = "http://github.com/api/v2/json/pulls/{project}/{num}".format(project=project, num=num)
84 url = "http://github.com/api/v2/json/pulls/{project}/{num}".format(project=project, num=num)
83 elif github_api == 3:
85 elif github_api == 3:
84 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
86 url = "https://api.github.com/repos/{project}/pulls/{num}".format(project=project, num=num)
85 response = requests.get(url)
87 response = requests.get(url)
86 response.raise_for_status()
88 response.raise_for_status()
87 if github_api == 2 :
89 if github_api == 2 :
88 return json.loads(response.text)['pull']
90 return json.loads(response.text)['pull']
89 return json.loads(response.text)
91 return json.loads(response.text)
90
92
91 def get_pulls_list(project, github_api=3):
93 def get_pulls_list(project, github_api=3):
92 """get pull request list
94 """get pull request list
93
95
94 github_api : version of github api to use
96 github_api : version of github api to use
95 """
97 """
96 if github_api == 3 :
98 if github_api == 3 :
97 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
99 url = "https://api.github.com/repos/{project}/pulls".format(project=project)
98 else :
100 else :
99 url = "http://github.com/api/v2/json/pulls/{project}".format(project=project)
101 url = "http://github.com/api/v2/json/pulls/{project}".format(project=project)
100 response = requests.get(url)
102 response = requests.get(url)
101 response.raise_for_status()
103 response.raise_for_status()
102 if github_api == 2 :
104 if github_api == 2 :
103 return json.loads(response.text)['pulls']
105 return json.loads(response.text)['pulls']
104 return json.loads(response.text)
106 return json.loads(response.text)
107
108 # encode_multipart_formdata is from urllib3.filepost
109 # The only change is to iter_fields, to enforce S3's required key ordering
110
111 def iter_fields(fields):
112 fields = fields.copy()
113 for key in ('key', 'acl', 'Filename', 'success_action_status', 'AWSAccessKeyId',
114 'Policy', 'Signature', 'Content-Type', 'file'):
115 yield (key, fields.pop(key))
116 for (k,v) in fields.items():
117 yield k,v
118
119 def encode_multipart_formdata(fields, boundary=None):
120 """
121 Encode a dictionary of ``fields`` using the multipart/form-data mime format.
122
123 :param fields:
124 Dictionary of fields or list of (key, value) field tuples. The key is
125 treated as the field name, and the value as the body of the form-data
126 bytes. If the value is a tuple of two elements, then the first element
127 is treated as the filename of the form-data section.
128
129 Field names and filenames must be unicode.
130
131 :param boundary:
132 If not specified, then a random boundary will be generated using
133 :func:`mimetools.choose_boundary`.
134 """
135 # copy requests imports in here:
136 from io import BytesIO
137 from requests.packages.urllib3.filepost import (
138 choose_boundary, six, writer, b, get_content_type
139 )
140 body = BytesIO()
141 if boundary is None:
142 boundary = choose_boundary()
143
144 for fieldname, value in iter_fields(fields):
145 body.write(b('--%s\r\n' % (boundary)))
146
147 if isinstance(value, tuple):
148 filename, data = value
149 writer(body).write('Content-Disposition: form-data; name="%s"; '
150 'filename="%s"\r\n' % (fieldname, filename))
151 body.write(b('Content-Type: %s\r\n\r\n' %
152 (get_content_type(filename))))
153 else:
154 data = value
155 writer(body).write('Content-Disposition: form-data; name="%s"\r\n'
156 % (fieldname))
157 body.write(b'Content-Type: text/plain\r\n\r\n')
158
159 if isinstance(data, int):
160 data = str(data) # Backwards compatibility
161 if isinstance(data, six.text_type):
162 writer(body).write(data)
163 else:
164 body.write(data)
165
166 body.write(b'\r\n')
167
168 body.write(b('--%s--\r\n' % (boundary)))
169
170 content_type = b('multipart/form-data; boundary=%s' % boundary)
171
172 return body.getvalue(), content_type
173
174
175 def post_download(project, filename, name=None, description=""):
176 """Upload a file to the GitHub downloads area"""
177 if name is None:
178 name = os.path.basename(filename)
179 with open(filename, 'rb') as f:
180 filedata = f.read()
181
182 url = "https://api.github.com/repos/{project}/downloads".format(project=project)
183
184 payload = json.dumps(dict(name=name, size=len(filedata),
185 description=description))
186 response = requests.post(url, data=payload, headers=make_auth_header())
187 response.raise_for_status()
188 reply = json.loads(response.content)
189 s3_url = reply['s3_url']
190
191 fields = dict(
192 key=reply['path'],
193 acl=reply['acl'],
194 success_action_status=201,
195 Filename=reply['name'],
196 AWSAccessKeyId=reply['accesskeyid'],
197 Policy=reply['policy'],
198 Signature=reply['signature'],
199 file=(reply['name'], filedata),
200 )
201 fields['Content-Type'] = reply['mime_type']
202 data, content_type = encode_multipart_formdata(fields)
203 s3r = requests.post(s3_url, data=data, headers={'Content-Type': content_type})
204 return s3r
General Comments 0
You need to be logged in to leave comments. Login now