39
40
version_string as breezy_version,
41
42
from ...config import AuthenticationConfig, GlobalStack, config_dir
43
from ...errors import InvalidHttpResponse
42
44
from ...git.urls import git_url_to_bzr_url
43
45
from ...i18n import gettext
44
46
from ...sixish import PY3
45
47
from ...trace import note
48
from ...transport import get_transport
46
49
from ...transport.http import default_user_agent
47
from ...lazy_import import lazy_import
48
lazy_import(globals(), """
49
from github import Github
52
GITHUB_HOST = 'github.com'
53
WEB_GITHUB_URL = 'https://github.com'
54
API_GITHUB_URL = 'https://api.github.com'
53
57
def store_github_token(scheme, host, token):
185
176
def __repr__(self):
186
177
return "GitHub()"
179
def _api_request(self, method, path):
181
'Accept': 'application/vnd.github.v3+json'}
183
headers['Authorization'] = 'token %s' % self._token
184
response = self.transport.request(
185
method, urlutils.join(self.transport.base, path),
187
if response.status == 401:
188
raise GitHubLoginRequired(self)
191
def _get_repo(self, path):
192
path = 'repos/' + path
193
response = self._api_request('GET', path)
194
if response.status == 404:
195
raise NoSuchProject(path)
196
if response.status == 200:
198
raise InvalidHttpResponse(path, response.text)
200
def _get_user(self, username=None):
202
path = 'users/:%s' % username
205
response = self._api_request('GET', path)
206
if response.status != 200:
207
raise InvalidHttpResponse(path, response.text)
210
def _get_organization(self, name):
211
path = 'orgs/:%s' % name
212
response = self._api_request('GET', path)
213
if response.status != 200:
214
raise InvalidHttpResponse(path, response.text)
217
def _search_issues(self, query):
218
path = 'search/issues'
219
response = self._api_request(
220
'GET', path + '?q=' + urlutils.quote(query))
221
if response.status != 200:
222
raise InvalidHttpResponse(path, response.text)
225
def _create_fork(self, repo, owner=None):
226
(orig_owner, orig_repo) = repo.split('/')
227
path = '/repos/:%s/:%s/forks' % (orig_owner, orig_repo)
229
path += '?organization=%s' % owner
230
response = self._api_request('POST', path)
232
raise InvalidHttpResponse(path, response.text)
189
236
def base_url(self):
190
# TODO(jelmer): Can we get the default URL from the Python API package
192
return "https://github.com"
195
self.gh = connect_github()
197
@convert_github_error
237
return WEB_GITHUB_URL
239
def __init__(self, transport):
240
self._token = retrieve_github_token('https', GITHUB_HOST)
241
self.transport = transport
242
self._current_user = self._get_user()
198
244
def publish_derived(self, local_branch, base_branch, name, project=None,
199
245
owner=None, revision_id=None, overwrite=False,
200
246
allow_lossy=True):
202
248
base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch)
203
base_repo = self.gh.get_repo('%s/%s' % (base_owner, base_project))
249
base_repo = self._get_repo('%s/%s' % (base_owner, base_project))
204
250
if owner is None:
205
owner = self.gh.get_user().login
251
owner = self._current_user['login']
206
252
if project is None:
207
project = base_repo.name
253
project = base_repo['name']
209
remote_repo = self.gh.get_repo('%s/%s' % (owner, project))
255
remote_repo = self._get_repo('%s/%s' % (owner, project))
211
256
except github.UnknownObjectException:
212
base_repo = self.gh.get_repo('%s/%s' % (base_owner, base_project))
213
if owner == self.gh.get_user().login:
214
owner_obj = self.gh.get_user()
216
owner_obj = self.gh.get_organization(owner)
217
remote_repo = owner_obj.create_fork(base_repo)
257
base_repo = self._get_repo('%s/%s' % (base_owner, base_project))
258
remote_repo = self._create_fork(base_repo, owner)
218
259
note(gettext('Forking new repository %s from %s') %
219
(remote_repo.html_url, base_repo.html_url))
260
(remote_repo['html_url'], base_repo['html_url']))
221
note(gettext('Reusing existing repository %s') % remote_repo.html_url)
222
remote_dir = controldir.ControlDir.open(git_url_to_bzr_url(remote_repo.ssh_url))
262
note(gettext('Reusing existing repository %s') % remote_repo['html_url'])
263
remote_dir = controldir.ControlDir.open(git_url_to_bzr_url(remote_repo['ssh_url']))
224
265
push_result = remote_dir.push_branch(
225
266
local_branch, revision_id=revision_id, overwrite=overwrite,
231
272
local_branch, revision_id=revision_id,
232
273
overwrite=overwrite, name=name, lossy=True)
233
274
return push_result.target_branch, github_url_to_bzr_url(
234
remote_repo.html_url, name)
275
remote_repo['html_url'], name)
236
@convert_github_error
237
277
def get_push_url(self, branch):
238
278
owner, project, branch_name = parse_github_branch_url(branch)
239
repo = self.gh.get_repo('%s/%s' % (owner, project))
240
return github_url_to_bzr_url(repo.ssh_url, branch_name)
279
repo = self._get_repo('%s/%s' % (owner, project))
280
return github_url_to_bzr_url(repo['ssh_url'], branch_name)
242
@convert_github_error
243
282
def get_derived_branch(self, base_branch, name, project=None, owner=None):
245
284
base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch)
246
base_repo = self.gh.get_repo('%s/%s' % (base_owner, base_project))
285
base_repo = self._get_repo('%s/%s' % (base_owner, base_project))
247
286
if owner is None:
248
owner = self.gh.get_user().login
287
owner = self._current_user['login']
249
288
if project is None:
250
project = base_repo.name
289
project = base_repo['name']
252
remote_repo = self.gh.get_repo('%s/%s' % (owner, project))
253
full_url = github_url_to_bzr_url(remote_repo.ssh_url, name)
291
remote_repo = self._get_repo('%s/%s' % (owner, project))
292
full_url = github_url_to_bzr_url(remote_repo['ssh_url'], name)
254
293
return _mod_branch.Branch.open(full_url)
255
294
except github.UnknownObjectException:
256
raise errors.NotBranchError('https://github.com/%s/%s' % (owner, project))
295
raise errors.NotBranchError('%s/%s/%s' % (WEB_GITHUB_URL, owner, project))
258
@convert_github_error
259
297
def get_proposer(self, source_branch, target_branch):
260
return GitHubMergeProposalBuilder(self.gh, source_branch, target_branch)
298
return GitHubMergeProposalBuilder(self, source_branch, target_branch)
262
@convert_github_error
263
300
def iter_proposals(self, source_branch, target_branch, status='open'):
264
301
(source_owner, source_repo_name, source_branch_name) = (
265
302
parse_github_branch_url(source_branch))
266
303
(target_owner, target_repo_name, target_branch_name) = (
267
304
parse_github_branch_url(target_branch))
268
target_repo = self.gh.get_repo(
305
target_repo = self._get_repo(
269
306
"%s/%s" % (target_owner, target_repo_name))