/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
1
# Copyright (C) 2018 Breezy Developers
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
0.434.1 by Jelmer Vernooij
Use absolute_import.
17
"""Support for GitHub."""
18
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
19
from __future__ import absolute_import
20
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
21
import json
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
22
import os
23
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
24
from .propose import (
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
25
    Hoster,
7268.8.2 by Jelmer Vernooij
Handle GitHub errors.
26
    HosterLoginRequired,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
27
    MergeProposal,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
28
    MergeProposalBuilder,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
29
    MergeProposalExists,
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
30
    NoSuchProject,
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
31
    PrerequisiteBranchUnsupported,
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
32
    ReopenFailed,
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
33
    UnsupportedHoster,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
34
    )
35
36
from ... import (
7340.1.1 by Martin
Fix use of config_dir in propose plugin
37
    bedding,
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
38
    branch as _mod_branch,
0.432.3 by Jelmer Vernooij
Publish command works for github.
39
    controldir,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
40
    errors,
41
    hooks,
42
    urlutils,
0.432.3 by Jelmer Vernooij
Publish command works for github.
43
    version_string as breezy_version,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
44
    )
7340.1.1 by Martin
Fix use of config_dir in propose plugin
45
from ...config import AuthenticationConfig, GlobalStack
7428.1.1 by Jelmer Vernooij
Handle 403s for pull request creation.
46
from ...errors import InvalidHttpResponse, PermissionDenied
0.431.32 by Jelmer Vernooij
Properly resolve git+ssh URLs.
47
from ...git.urls import git_url_to_bzr_url
0.432.3 by Jelmer Vernooij
Publish command works for github.
48
from ...i18n import gettext
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
49
from ...sixish import PY3
0.432.3 by Jelmer Vernooij
Publish command works for github.
50
from ...trace import note
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
51
from ...transport import get_transport
7296.10.5 by Jelmer Vernooij
use default_user_agent function.
52
from ...transport.http import default_user_agent
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
53
54
55
GITHUB_HOST = 'github.com'
56
WEB_GITHUB_URL = 'https://github.com'
57
API_GITHUB_URL = 'https://api.github.com'
7408.1.3 by Jelmer Vernooij
Support pagination for github.
58
DEFAULT_PER_PAGE = 50
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
59
60
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
61
def store_github_token(scheme, host, token):
7340.1.1 by Martin
Fix use of config_dir in propose plugin
62
    with open(os.path.join(bedding.config_dir(), 'github.conf'), 'w') as f:
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
63
        f.write(token)
64
65
66
def retrieve_github_token(scheme, host):
7340.1.1 by Martin
Fix use of config_dir in propose plugin
67
    path = os.path.join(bedding.config_dir(), 'github.conf')
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
68
    if not os.path.exists(path):
69
        return None
0.435.1 by Jelmer Vernooij
Fix reading github credentials.
70
    with open(path, 'r') as f:
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
71
        return f.read().strip()
72
73
0.431.44 by Jelmer Vernooij
Support get/set description.
74
def determine_title(description):
75
    return description.splitlines()[0]
76
77
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
78
class ValidationFailed(errors.BzrError):
79
80
    _fmt = "GitHub validation failed: %(error)s"
81
82
    def __init__(self, error):
83
        errors.BzrError.__init__(self)
84
        self.error = error
85
86
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
87
class NotGitHubUrl(errors.BzrError):
88
89
    _fmt = "Not a GitHub URL: %(url)s"
90
91
    def __init__(self, url):
92
        errors.BzrError.__init__(self)
93
        self.url = url
94
95
7268.8.2 by Jelmer Vernooij
Handle GitHub errors.
96
class GitHubLoginRequired(HosterLoginRequired):
97
98
    _fmt = "Action requires GitHub login."
99
100
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
101
def connect_github():
7211.13.7 by Jelmer Vernooij
Fix formatting.
102
    """Connect to GitHub.
103
    """
7296.10.5 by Jelmer Vernooij
use default_user_agent function.
104
    user_agent = default_user_agent()
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
105
    auth = AuthenticationConfig()
106
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
107
    credentials = auth.get_credentials('https', GITHUB_HOST)
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
108
    if credentials is not None:
0.432.3 by Jelmer Vernooij
Publish command works for github.
109
        return Github(credentials['user'], credentials['password'],
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
110
                      user_agent=user_agent)
111
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
112
    # TODO(jelmer): token = auth.get_token('https', GITHUB_HOST)
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
113
    if token is not None:
0.431.61 by Jelmer Vernooij
Fix token login.
114
        return Github(token, user_agent=user_agent)
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
115
    else:
116
        note('Accessing GitHub anonymously. To log in, run \'brz gh-login\'.')
117
        return Github(user_agent=user_agent)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
118
119
0.431.44 by Jelmer Vernooij
Support get/set description.
120
class GitHubMergeProposal(MergeProposal):
121
7371.4.2 by Jelmer Vernooij
More fixes.
122
    def __init__(self, gh, pr):
123
        self._gh = gh
0.431.44 by Jelmer Vernooij
Support get/set description.
124
        self._pr = pr
125
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
126
    def __repr__(self):
127
        return "<%s at %r>" % (type(self).__name__, self.url)
128
0.431.44 by Jelmer Vernooij
Support get/set description.
129
    @property
130
    def url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
131
        return self._pr['html_url']
0.431.44 by Jelmer Vernooij
Support get/set description.
132
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
133
    def _branch_from_part(self, part):
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
134
        if part['repo'] is None:
135
            return None
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
136
        return github_url_to_bzr_url(part['repo']['html_url'], part['ref'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
137
138
    def get_source_branch_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
139
        return self._branch_from_part(self._pr['head'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
140
141
    def get_target_branch_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
142
        return self._branch_from_part(self._pr['base'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
143
0.431.44 by Jelmer Vernooij
Support get/set description.
144
    def get_description(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
145
        return self._pr['body']
0.431.44 by Jelmer Vernooij
Support get/set description.
146
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
147
    def get_commit_message(self):
148
        return None
149
7371.4.2 by Jelmer Vernooij
More fixes.
150
    def set_commit_message(self, message):
151
        self._patch({'title': message})
152
153
    def _patch(self, data):
154
        response = self._gh._api_request(
155
            'PATCH', self._pr['url'], body=json.dumps(data).encode('utf-8'))
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
156
        if response.status == 422:
157
            raise ValidationFailed(json.loads(response.text))
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
158
        if response.status != 200:
7371.4.2 by Jelmer Vernooij
More fixes.
159
            raise InvalidHttpResponse(self._pr['url'], response.text)
160
        self._pr = json.loads(response.text)
161
0.431.44 by Jelmer Vernooij
Support get/set description.
162
    def set_description(self, description):
7371.4.2 by Jelmer Vernooij
More fixes.
163
        self._patch({
164
            'body': description,
165
            'title': determine_title(description),
166
            })
0.431.44 by Jelmer Vernooij
Support get/set description.
167
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
168
    def is_merged(self):
7381.5.4 by Jelmer Vernooij
'merged' doesn't appear to always be set.
169
        return bool(self._pr.get('merged_at'))
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
170
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
171
    def is_closed(self):
7381.5.4 by Jelmer Vernooij
'merged' doesn't appear to always be set.
172
        return self._pr['state'] == 'closed' and not bool(self._pr.get('merged_at'))
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
173
174
    def reopen(self):
175
        try:
176
            self._patch({'state': 'open'})
177
        except ValidationFailed as e:
178
            raise ReopenFailed(e.error['errors'][0]['message'])
179
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
180
    def close(self):
7371.4.2 by Jelmer Vernooij
More fixes.
181
        self._patch({'state': 'closed'})
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
182
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
183
    def can_be_merged(self):
184
        return self._pr['mergeable']
185
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
186
    def merge(self, commit_message=None):
187
        # https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button
188
        self._pr.merge(commit_message=commit_message)
189
7414.4.2 by Jelmer Vernooij
Fix gitlab / github merged_by fetching.
190
    def get_merged_by(self):
191
        merged_by = self._pr.get('merged_by')
192
        if merged_by is None:
193
            return None
194
        return merged_by['login']
195
0.431.44 by Jelmer Vernooij
Support get/set description.
196
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
197
def parse_github_url(url):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
198
    (scheme, user, password, host, port, path) = urlutils.parse_url(
199
        url)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
200
    if host != GITHUB_HOST:
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
201
        raise NotGitHubUrl(url)
202
    (owner, repo_name) = path.strip('/').split('/')
0.432.12 by Jelmer Vernooij
Fix .git ends.
203
    if repo_name.endswith('.git'):
204
        repo_name = repo_name[:-4]
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
205
    return owner, repo_name
206
207
208
def parse_github_branch_url(branch):
209
    url = urlutils.split_segment_parameters(branch.user_url)[0]
210
    owner, repo_name = parse_github_url(url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
211
    return owner, repo_name, branch.name
212
213
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
214
def github_url_to_bzr_url(url, branch_name):
215
    if not PY3:
216
        branch_name = branch_name.encode('utf-8')
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
217
    return git_url_to_bzr_url(url, branch_name)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
218
219
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
220
def strip_optional(url):
221
    return url.split('{')[0]
222
223
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
224
class GitHub(Hoster):
225
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
226
    name = 'github'
227
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
228
    supports_merge_proposal_labels = True
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
229
    supports_merge_proposal_commit_message = False
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
230
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
231
    def __repr__(self):
232
        return "GitHub()"
233
7371.4.2 by Jelmer Vernooij
More fixes.
234
    def _api_request(self, method, path, body=None):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
235
        headers = {
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
236
            'Content-Type': 'application/json',
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
237
            'Accept': 'application/vnd.github.v3+json'}
238
        if self._token:
239
            headers['Authorization'] = 'token %s' % self._token
240
        response = self.transport.request(
241
            method, urlutils.join(self.transport.base, path),
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
242
            headers=headers, body=body, retries=3)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
243
        if response.status == 401:
244
            raise GitHubLoginRequired(self)
245
        return response
246
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
247
    def _get_repo(self, owner, repo):
248
        path = 'repos/%s/%s' % (owner, repo)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
249
        response = self._api_request('GET', path)
250
        if response.status == 404:
251
            raise NoSuchProject(path)
252
        if response.status == 200:
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
253
            return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
254
        raise InvalidHttpResponse(path, response.text)
255
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
256
    def _get_repo_pulls(self, path, head=None, state=None):
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
257
        path = path + '?'
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
258
        params = {}
259
        if head is not None:
260
            params['head'] = head
261
        if state is not None:
262
            params['state'] = state
263
        path += ';'.join(['%s=%s' % (k, urlutils.quote(v))
264
                         for k, v in params.items()])
265
        response = self._api_request('GET', path)
266
        if response.status == 404:
267
            raise NoSuchProject(path)
268
        if response.status == 200:
269
            return json.loads(response.text)
270
        raise InvalidHttpResponse(path, response.text)
271
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
272
    def _create_pull(self, path, title, head, base, body=None, labels=None, assignee=None):
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
273
        data = {
274
            'title': title,
275
            'head': head,
276
            'base': base,
277
        }
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
278
        if labels is not None:
279
            data['labels'] = labels
280
        if assignee is not None:
281
            data['assignee'] = assignee
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
282
        if body:
283
            data['body'] = body
284
285
        response = self._api_request(
286
            'POST', path, body=json.dumps(data).encode('utf-8'))
7428.1.1 by Jelmer Vernooij
Handle 403s for pull request creation.
287
        if response.status == 403:
288
            raise PermissionDenied(path, response.text)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
289
        if response.status != 201:
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
290
            raise InvalidHttpResponse(path, 'req is invalid %d %r: %r' % (response.status, data, response.text))
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
291
        return json.loads(response.text)
292
293
    def _get_user_by_email(self, email):
294
        path = 'search/users?q=%s+in:email' % email
295
        response = self._api_request('GET', path)
296
        if response.status != 200:
297
            raise InvalidHttpResponse(path, response.text)
298
        ret = json.loads(response.text)
299
        if ret['total_count'] == 0:
300
            raise KeyError('no user with email %s' % email)
301
        elif ret['total_count'] > 1:
302
            raise ValueError('more than one result for email %s' % email)
303
        return ret['items'][0]
304
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
305
    def _get_user(self, username=None):
306
        if username:
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
307
            path = 'users/%s' % username
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
308
        else:
309
            path = 'user'
310
        response = self._api_request('GET', path)
311
        if response.status != 200:
312
            raise InvalidHttpResponse(path, response.text)
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
313
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
314
315
    def _get_organization(self, name):
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
316
        path = 'orgs/%s' % name
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
317
        response = self._api_request('GET', path)
318
        if response.status != 200:
319
            raise InvalidHttpResponse(path, response.text)
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
320
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
321
7408.1.3 by Jelmer Vernooij
Support pagination for github.
322
    def _list_paged(self, path, parameters=None, per_page=None):
323
        if parameters is None:
324
            parameters = {}
325
        else:
326
            parameters = dict(parameters.items())
327
        if per_page:
328
            parameters['per_page'] = str(per_page)
329
        page = 1
330
        i = 0
331
        while path:
332
            parameters['page'] = str(page)
333
            response = self._api_request(
334
                'GET', path + '?' +
335
                ';'.join(['%s=%s' % (k, urlutils.quote(v))
336
                          for (k, v) in parameters.items()]))
337
            if response.status != 200:
338
                raise InvalidHttpResponse(path, response.text)
339
            data = json.loads(response.text)
340
            for entry in data['items']:
341
                i += 1
342
                yield entry
343
            if i >= data['total_count']:
344
                break
345
            page += 1
346
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
347
    def _search_issues(self, query):
348
        path = 'search/issues'
7408.1.3 by Jelmer Vernooij
Support pagination for github.
349
        return self._list_paged(path, {'q': query}, per_page=DEFAULT_PER_PAGE)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
350
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
351
    def _create_fork(self, path, owner=None):
7410.1.2 by Jelmer Vernooij
Fix fork creation.
352
        if owner and owner != self._current_user['login']:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
353
            path += '?organization=%s' % owner
354
        response = self._api_request('POST', path)
7397.1.2 by Jelmer Vernooij
Fix fork creation on github.
355
        if response.status != 202:
7410.1.2 by Jelmer Vernooij
Fix fork creation.
356
            raise InvalidHttpResponse(path, 'status: %d, %r' % (response.status, response.text))
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
357
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
358
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
359
    @property
360
    def base_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
361
        return WEB_GITHUB_URL
362
363
    def __init__(self, transport):
364
        self._token = retrieve_github_token('https', GITHUB_HOST)
365
        self.transport = transport
366
        self._current_user = self._get_user()
367
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
368
    def publish_derived(self, local_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
369
                        owner=None, revision_id=None, overwrite=False,
370
                        allow_lossy=True):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
371
        base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch)
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
372
        base_repo = self._get_repo(base_owner, base_project)
0.432.3 by Jelmer Vernooij
Publish command works for github.
373
        if owner is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
374
            owner = self._current_user['login']
0.432.3 by Jelmer Vernooij
Publish command works for github.
375
        if project is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
376
            project = base_repo['name']
0.432.3 by Jelmer Vernooij
Publish command works for github.
377
        try:
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
378
            remote_repo = self._get_repo(owner, project)
7410.1.1 by Jelmer Vernooij
Remove last imports of the pygithub module.
379
        except NoSuchProject:
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
380
            base_repo = self._get_repo(base_owner, base_project)
381
            remote_repo = self._create_fork(base_repo['forks_url'], owner)
0.432.3 by Jelmer Vernooij
Publish command works for github.
382
            note(gettext('Forking new repository %s from %s') %
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
383
                 (remote_repo['html_url'], base_repo['html_url']))
0.432.3 by Jelmer Vernooij
Publish command works for github.
384
        else:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
385
            note(gettext('Reusing existing repository %s') % remote_repo['html_url'])
386
        remote_dir = controldir.ControlDir.open(git_url_to_bzr_url(remote_repo['ssh_url']))
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
387
        try:
7211.13.7 by Jelmer Vernooij
Fix formatting.
388
            push_result = remote_dir.push_branch(
389
                local_branch, revision_id=revision_id, overwrite=overwrite,
390
                name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
391
        except errors.NoRoundtrippingSupport:
392
            if not allow_lossy:
393
                raise
7211.13.7 by Jelmer Vernooij
Fix formatting.
394
            push_result = remote_dir.push_branch(
395
                local_branch, revision_id=revision_id,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
396
                overwrite=overwrite, name=name, lossy=True)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
397
        return push_result.target_branch, github_url_to_bzr_url(
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
398
            remote_repo['html_url'], name)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
399
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
400
    def get_push_url(self, branch):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
401
        owner, project, branch_name = parse_github_branch_url(branch)
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
402
        repo = self._get_repo(owner, project)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
403
        return github_url_to_bzr_url(repo['ssh_url'], branch_name)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
404
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
405
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
406
        base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch)
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
407
        base_repo = self._get_repo(base_owner, base_project)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
408
        if owner is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
409
            owner = self._current_user['login']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
410
        if project is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
411
            project = base_repo['name']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
412
        try:
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
413
            remote_repo = self._get_repo(owner, project)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
414
            full_url = github_url_to_bzr_url(remote_repo['ssh_url'], name)
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
415
            return _mod_branch.Branch.open(full_url)
7410.1.1 by Jelmer Vernooij
Remove last imports of the pygithub module.
416
        except NoSuchProject:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
417
            raise errors.NotBranchError('%s/%s/%s' % (WEB_GITHUB_URL, owner, project))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
418
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
419
    def get_proposer(self, source_branch, target_branch):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
420
        return GitHubMergeProposalBuilder(self, source_branch, target_branch)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
421
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
422
    def iter_proposals(self, source_branch, target_branch, status='open'):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
423
        (source_owner, source_repo_name, source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
424
            parse_github_branch_url(source_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
425
        (target_owner, target_repo_name, target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
426
            parse_github_branch_url(target_branch))
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
427
        target_repo = self._get_repo(target_owner, target_repo_name)
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
428
        state = {
429
            'open': 'open',
430
            'merged': 'closed',
431
            'closed': 'closed',
432
            'all': 'all'}
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
433
        pulls = self._get_repo_pulls(
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
434
            strip_optional(target_repo['pulls_url']),
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
435
            head=target_branch_name,
436
            state=state[status])
437
        for pull in pulls:
438
            if (status == 'closed' and pull['merged'] or
439
                    status == 'merged' and not pull['merged']):
440
                continue
441
            if pull['head']['ref'] != source_branch_name:
442
                continue
443
            if pull['head']['repo'] is None:
7268.4.1 by Jelmer Vernooij
Don't attempt to resolve None when repo has gone away.
444
                # Repo has gone the way of the dodo
445
                continue
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
446
            if (pull['head']['repo']['owner']['login'] != source_owner or
447
                    pull['head']['repo']['name'] != source_repo_name):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
448
                continue
7371.4.2 by Jelmer Vernooij
More fixes.
449
            yield GitHubMergeProposal(self, pull)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
450
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
451
    def hosts(self, branch):
452
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
453
            parse_github_branch_url(branch)
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
454
        except NotGitHubUrl:
455
            return False
456
        else:
457
            return True
458
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
459
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
460
    def probe_from_url(cls, url, possible_transports=None):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
461
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
462
            parse_github_url(url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
463
        except NotGitHubUrl:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
464
            raise UnsupportedHoster(url)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
465
        transport = get_transport(
466
            API_GITHUB_URL, possible_transports=possible_transports)
467
        return cls(transport)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
468
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
469
    @classmethod
470
    def iter_instances(cls):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
471
        yield cls(get_transport(API_GITHUB_URL))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
472
0.431.66 by Jelmer Vernooij
Add support for status argument.
473
    def iter_my_proposals(self, status='open'):
474
        query = ['is:pr']
475
        if status == 'open':
476
            query.append('is:open')
477
        elif status == 'closed':
478
            query.append('is:unmerged')
7268.2.1 by Jelmer Vernooij
Don't include open unmerged pull requests in 'closed'.
479
            # Also use "is:closed" otherwise unmerged open pull requests are
480
            # also included.
481
            query.append('is:closed')
0.431.66 by Jelmer Vernooij
Add support for status argument.
482
        elif status == 'merged':
483
            query.append('is:merged')
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
484
        query.append('author:%s' % self._current_user['login'])
7408.1.3 by Jelmer Vernooij
Support pagination for github.
485
        for issue in self._search_issues(query=' '.join(query)):
7360.1.5 by Jelmer Vernooij
Use authenticated request when requesting pull requests from GitHub; authenticated connections have a much lower rate limit.
486
            url = issue['pull_request']['url']
487
            response = self._api_request('GET', url)
488
            if response.status != 200:
489
                raise InvalidHttpResponse(url, response.text)
7371.4.2 by Jelmer Vernooij
More fixes.
490
            yield GitHubMergeProposal(self, json.loads(response.text))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
491
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
492
    def get_proposal_by_url(self, url):
493
        raise UnsupportedHoster(url)
494
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
495
0.432.2 by Jelmer Vernooij
Publish command sort of works.
496
class GitHubMergeProposalBuilder(MergeProposalBuilder):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
497
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
498
    def __init__(self, gh, source_branch, target_branch):
499
        self.gh = gh
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
500
        self.source_branch = source_branch
501
        self.target_branch = target_branch
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
502
        (self.target_owner, self.target_repo_name, self.target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
503
            parse_github_branch_url(self.target_branch))
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
504
        (self.source_owner, self.source_repo_name, self.source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
505
            parse_github_branch_url(self.source_branch))
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
506
507
    def get_infotext(self):
508
        """Determine the initial comment for the merge proposal."""
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
509
        info = []
510
        info.append("Merge %s into %s:%s\n" % (
511
            self.source_branch_name, self.target_owner,
512
            self.target_branch_name))
513
        info.append("Source: %s\n" % self.source_branch.user_url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
514
        info.append("Target: %s\n" % self.target_branch.user_url)
515
        return ''.join(info)
516
517
    def get_initial_body(self):
518
        """Get a body for the proposal for the user to modify.
519
520
        :return: a str or None.
521
        """
522
        return None
523
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
524
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
525
                        prerequisite_branch=None, commit_message=None):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
526
        """Perform the submission."""
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
527
        if prerequisite_branch is not None:
528
            raise PrerequisiteBranchUnsupported(self)
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
529
        # Note that commit_message is ignored, since github doesn't support it.
0.432.7 by Jelmer Vernooij
propose works \o/
530
        # TODO(jelmer): Probe for right repo name
0.432.12 by Jelmer Vernooij
Fix .git ends.
531
        if self.target_repo_name.endswith('.git'):
532
            self.target_repo_name = self.target_repo_name[:-4]
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
533
        # TODO(jelmer): Allow setting title explicitly?
0.431.44 by Jelmer Vernooij
Support get/set description.
534
        title = determine_title(description)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
535
        # TODO(jelmer): Set maintainers_can_modify?
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
536
        target_repo = self.gh._get_repo(
537
            self.target_owner, self.target_repo_name)
7410.1.3 by Jelmer Vernooij
Fix issue handling.
538
        assignees = []
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
539
        if reviewers:
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
540
            assignees = []
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
541
            for reviewer in reviewers:
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
542
                if '@' in reviewer:
543
                    user = self.gh._get_user_by_email(reviewer)
544
                else:
545
                    user = self.gh._get_user(reviewer)
7410.1.3 by Jelmer Vernooij
Fix issue handling.
546
                assignees.append(user['login'])
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
547
        else:
548
            assignees = None
549
        try:
550
            pull_request = self.gh._create_pull(
551
                strip_optional(target_repo['pulls_url']),
552
                title=title, body=description,
553
                head="%s:%s" % (self.source_owner, self.source_branch_name),
554
                base=self.target_branch_name,
555
                labels=labels, assignee=assignees)
556
        except ValidationFailed:
557
            raise MergeProposalExists(self.source_branch.user_url)
7371.4.2 by Jelmer Vernooij
More fixes.
558
        return GitHubMergeProposal(self.gh, pull_request)