/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
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
196
    def get_merged_at(self):
197
        merged_at = self._pr.get('merged_at')
198
        if merged_at is None:
199
            return None
7414.4.4 by Jelmer Vernooij
Use iso8601 module.
200
        import iso8601
201
        return iso8601.parse_date(merged_at)
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
202
0.431.44 by Jelmer Vernooij
Support get/set description.
203
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
204
def parse_github_url(url):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
205
    (scheme, user, password, host, port, path) = urlutils.parse_url(
206
        url)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
207
    if host != GITHUB_HOST:
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
208
        raise NotGitHubUrl(url)
209
    (owner, repo_name) = path.strip('/').split('/')
0.432.12 by Jelmer Vernooij
Fix .git ends.
210
    if repo_name.endswith('.git'):
211
        repo_name = repo_name[:-4]
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
212
    return owner, repo_name
213
214
215
def parse_github_branch_url(branch):
216
    url = urlutils.split_segment_parameters(branch.user_url)[0]
217
    owner, repo_name = parse_github_url(url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
218
    return owner, repo_name, branch.name
219
220
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
221
def github_url_to_bzr_url(url, branch_name):
222
    if not PY3:
223
        branch_name = branch_name.encode('utf-8')
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
224
    return git_url_to_bzr_url(url, branch_name)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
225
226
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
227
def strip_optional(url):
228
    return url.split('{')[0]
229
230
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
231
class GitHub(Hoster):
232
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
233
    name = 'github'
234
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
235
    supports_merge_proposal_labels = True
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
236
    supports_merge_proposal_commit_message = False
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
237
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
238
    def __repr__(self):
239
        return "GitHub()"
240
7371.4.2 by Jelmer Vernooij
More fixes.
241
    def _api_request(self, method, path, body=None):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
242
        headers = {
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
243
            'Content-Type': 'application/json',
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
244
            'Accept': 'application/vnd.github.v3+json'}
245
        if self._token:
246
            headers['Authorization'] = 'token %s' % self._token
247
        response = self.transport.request(
248
            method, urlutils.join(self.transport.base, path),
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
249
            headers=headers, body=body, retries=3)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
250
        if response.status == 401:
251
            raise GitHubLoginRequired(self)
252
        return response
253
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
254
    def _get_repo(self, owner, repo):
255
        path = 'repos/%s/%s' % (owner, repo)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
256
        response = self._api_request('GET', path)
257
        if response.status == 404:
258
            raise NoSuchProject(path)
259
        if response.status == 200:
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
260
            return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
261
        raise InvalidHttpResponse(path, response.text)
262
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
263
    def _get_repo_pulls(self, path, head=None, state=None):
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
264
        path = path + '?'
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
265
        params = {}
266
        if head is not None:
267
            params['head'] = head
268
        if state is not None:
269
            params['state'] = state
270
        path += ';'.join(['%s=%s' % (k, urlutils.quote(v))
271
                         for k, v in params.items()])
272
        response = self._api_request('GET', path)
273
        if response.status == 404:
274
            raise NoSuchProject(path)
275
        if response.status == 200:
276
            return json.loads(response.text)
277
        raise InvalidHttpResponse(path, response.text)
278
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
279
    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.
280
        data = {
281
            'title': title,
282
            'head': head,
283
            'base': base,
284
        }
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
285
        if labels is not None:
286
            data['labels'] = labels
287
        if assignee is not None:
288
            data['assignee'] = assignee
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
289
        if body:
290
            data['body'] = body
291
292
        response = self._api_request(
293
            'POST', path, body=json.dumps(data).encode('utf-8'))
7428.1.1 by Jelmer Vernooij
Handle 403s for pull request creation.
294
        if response.status == 403:
295
            raise PermissionDenied(path, response.text)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
296
        if response.status != 201:
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
297
            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.
298
        return json.loads(response.text)
299
300
    def _get_user_by_email(self, email):
301
        path = 'search/users?q=%s+in:email' % email
302
        response = self._api_request('GET', path)
303
        if response.status != 200:
304
            raise InvalidHttpResponse(path, response.text)
305
        ret = json.loads(response.text)
306
        if ret['total_count'] == 0:
307
            raise KeyError('no user with email %s' % email)
308
        elif ret['total_count'] > 1:
309
            raise ValueError('more than one result for email %s' % email)
310
        return ret['items'][0]
311
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
312
    def _get_user(self, username=None):
313
        if username:
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
314
            path = 'users/%s' % username
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
315
        else:
316
            path = 'user'
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
322
    def _get_organization(self, name):
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
323
        path = 'orgs/%s' % name
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
324
        response = self._api_request('GET', path)
325
        if response.status != 200:
326
            raise InvalidHttpResponse(path, response.text)
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
327
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
328
7408.1.3 by Jelmer Vernooij
Support pagination for github.
329
    def _list_paged(self, path, parameters=None, per_page=None):
330
        if parameters is None:
331
            parameters = {}
332
        else:
333
            parameters = dict(parameters.items())
334
        if per_page:
335
            parameters['per_page'] = str(per_page)
336
        page = 1
337
        i = 0
338
        while path:
339
            parameters['page'] = str(page)
340
            response = self._api_request(
341
                'GET', path + '?' +
342
                ';'.join(['%s=%s' % (k, urlutils.quote(v))
343
                          for (k, v) in parameters.items()]))
344
            if response.status != 200:
345
                raise InvalidHttpResponse(path, response.text)
346
            data = json.loads(response.text)
347
            for entry in data['items']:
348
                i += 1
349
                yield entry
350
            if i >= data['total_count']:
351
                break
352
            page += 1
353
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
354
    def _search_issues(self, query):
355
        path = 'search/issues'
7408.1.3 by Jelmer Vernooij
Support pagination for github.
356
        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.
357
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
358
    def _create_fork(self, path, owner=None):
7410.1.2 by Jelmer Vernooij
Fix fork creation.
359
        if owner and owner != self._current_user['login']:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
360
            path += '?organization=%s' % owner
361
        response = self._api_request('POST', path)
7397.1.2 by Jelmer Vernooij
Fix fork creation on github.
362
        if response.status != 202:
7410.1.2 by Jelmer Vernooij
Fix fork creation.
363
            raise InvalidHttpResponse(path, 'status: %d, %r' % (response.status, response.text))
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
364
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
365
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
366
    @property
367
    def base_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
368
        return WEB_GITHUB_URL
369
370
    def __init__(self, transport):
371
        self._token = retrieve_github_token('https', GITHUB_HOST)
372
        self.transport = transport
373
        self._current_user = self._get_user()
374
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
375
    def publish_derived(self, local_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
376
                        owner=None, revision_id=None, overwrite=False,
377
                        allow_lossy=True):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
378
        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.
379
        base_repo = self._get_repo(base_owner, base_project)
0.432.3 by Jelmer Vernooij
Publish command works for github.
380
        if owner is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
381
            owner = self._current_user['login']
0.432.3 by Jelmer Vernooij
Publish command works for github.
382
        if project is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
383
            project = base_repo['name']
0.432.3 by Jelmer Vernooij
Publish command works for github.
384
        try:
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
385
            remote_repo = self._get_repo(owner, project)
7410.1.1 by Jelmer Vernooij
Remove last imports of the pygithub module.
386
        except NoSuchProject:
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
387
            base_repo = self._get_repo(base_owner, base_project)
388
            remote_repo = self._create_fork(base_repo['forks_url'], owner)
0.432.3 by Jelmer Vernooij
Publish command works for github.
389
            note(gettext('Forking new repository %s from %s') %
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
390
                 (remote_repo['html_url'], base_repo['html_url']))
0.432.3 by Jelmer Vernooij
Publish command works for github.
391
        else:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
392
            note(gettext('Reusing existing repository %s') % remote_repo['html_url'])
393
        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.
394
        try:
7211.13.7 by Jelmer Vernooij
Fix formatting.
395
            push_result = remote_dir.push_branch(
396
                local_branch, revision_id=revision_id, overwrite=overwrite,
397
                name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
398
        except errors.NoRoundtrippingSupport:
399
            if not allow_lossy:
400
                raise
7211.13.7 by Jelmer Vernooij
Fix formatting.
401
            push_result = remote_dir.push_branch(
402
                local_branch, revision_id=revision_id,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
403
                overwrite=overwrite, name=name, lossy=True)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
404
        return push_result.target_branch, github_url_to_bzr_url(
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
405
            remote_repo['html_url'], name)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
406
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
407
    def get_push_url(self, branch):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
408
        owner, project, branch_name = parse_github_branch_url(branch)
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
409
        repo = self._get_repo(owner, project)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
410
        return github_url_to_bzr_url(repo['ssh_url'], branch_name)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
411
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
412
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
413
        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.
414
        base_repo = self._get_repo(base_owner, base_project)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
415
        if owner is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
416
            owner = self._current_user['login']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
417
        if project is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
418
            project = base_repo['name']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
419
        try:
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
420
            remote_repo = self._get_repo(owner, project)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
421
            full_url = github_url_to_bzr_url(remote_repo['ssh_url'], name)
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
422
            return _mod_branch.Branch.open(full_url)
7410.1.1 by Jelmer Vernooij
Remove last imports of the pygithub module.
423
        except NoSuchProject:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
424
            raise errors.NotBranchError('%s/%s/%s' % (WEB_GITHUB_URL, owner, project))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
425
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
426
    def get_proposer(self, source_branch, target_branch):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
427
        return GitHubMergeProposalBuilder(self, source_branch, target_branch)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
428
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
429
    def iter_proposals(self, source_branch, target_branch, status='open'):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
430
        (source_owner, source_repo_name, source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
431
            parse_github_branch_url(source_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
432
        (target_owner, target_repo_name, target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
433
            parse_github_branch_url(target_branch))
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
434
        target_repo = self._get_repo(target_owner, target_repo_name)
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
435
        state = {
436
            'open': 'open',
437
            'merged': 'closed',
438
            'closed': 'closed',
439
            'all': 'all'}
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
440
        pulls = self._get_repo_pulls(
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
441
            strip_optional(target_repo['pulls_url']),
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
442
            head=target_branch_name,
443
            state=state[status])
444
        for pull in pulls:
445
            if (status == 'closed' and pull['merged'] or
446
                    status == 'merged' and not pull['merged']):
447
                continue
448
            if pull['head']['ref'] != source_branch_name:
449
                continue
450
            if pull['head']['repo'] is None:
7268.4.1 by Jelmer Vernooij
Don't attempt to resolve None when repo has gone away.
451
                # Repo has gone the way of the dodo
452
                continue
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
453
            if (pull['head']['repo']['owner']['login'] != source_owner or
454
                    pull['head']['repo']['name'] != source_repo_name):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
455
                continue
7371.4.2 by Jelmer Vernooij
More fixes.
456
            yield GitHubMergeProposal(self, pull)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
457
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
458
    def hosts(self, branch):
459
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
460
            parse_github_branch_url(branch)
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
461
        except NotGitHubUrl:
462
            return False
463
        else:
464
            return True
465
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
466
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
467
    def probe_from_url(cls, url, possible_transports=None):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
468
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
469
            parse_github_url(url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
470
        except NotGitHubUrl:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
471
            raise UnsupportedHoster(url)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
472
        transport = get_transport(
473
            API_GITHUB_URL, possible_transports=possible_transports)
474
        return cls(transport)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
475
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
476
    @classmethod
477
    def iter_instances(cls):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
478
        yield cls(get_transport(API_GITHUB_URL))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
479
0.431.66 by Jelmer Vernooij
Add support for status argument.
480
    def iter_my_proposals(self, status='open'):
481
        query = ['is:pr']
482
        if status == 'open':
483
            query.append('is:open')
484
        elif status == 'closed':
485
            query.append('is:unmerged')
7268.2.1 by Jelmer Vernooij
Don't include open unmerged pull requests in 'closed'.
486
            # Also use "is:closed" otherwise unmerged open pull requests are
487
            # also included.
488
            query.append('is:closed')
0.431.66 by Jelmer Vernooij
Add support for status argument.
489
        elif status == 'merged':
490
            query.append('is:merged')
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
491
        query.append('author:%s' % self._current_user['login'])
7408.1.3 by Jelmer Vernooij
Support pagination for github.
492
        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.
493
            url = issue['pull_request']['url']
494
            response = self._api_request('GET', url)
495
            if response.status != 200:
496
                raise InvalidHttpResponse(url, response.text)
7371.4.2 by Jelmer Vernooij
More fixes.
497
            yield GitHubMergeProposal(self, json.loads(response.text))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
498
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
499
    def get_proposal_by_url(self, url):
500
        raise UnsupportedHoster(url)
501
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
502
0.432.2 by Jelmer Vernooij
Publish command sort of works.
503
class GitHubMergeProposalBuilder(MergeProposalBuilder):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
504
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
505
    def __init__(self, gh, source_branch, target_branch):
506
        self.gh = gh
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
507
        self.source_branch = source_branch
508
        self.target_branch = target_branch
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
509
        (self.target_owner, self.target_repo_name, self.target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
510
            parse_github_branch_url(self.target_branch))
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
511
        (self.source_owner, self.source_repo_name, self.source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
512
            parse_github_branch_url(self.source_branch))
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
513
514
    def get_infotext(self):
515
        """Determine the initial comment for the merge proposal."""
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
516
        info = []
517
        info.append("Merge %s into %s:%s\n" % (
518
            self.source_branch_name, self.target_owner,
519
            self.target_branch_name))
520
        info.append("Source: %s\n" % self.source_branch.user_url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
521
        info.append("Target: %s\n" % self.target_branch.user_url)
522
        return ''.join(info)
523
524
    def get_initial_body(self):
525
        """Get a body for the proposal for the user to modify.
526
527
        :return: a str or None.
528
        """
529
        return None
530
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
531
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
532
                        prerequisite_branch=None, commit_message=None):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
533
        """Perform the submission."""
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
534
        if prerequisite_branch is not None:
535
            raise PrerequisiteBranchUnsupported(self)
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
536
        # Note that commit_message is ignored, since github doesn't support it.
0.432.7 by Jelmer Vernooij
propose works \o/
537
        # TODO(jelmer): Probe for right repo name
0.432.12 by Jelmer Vernooij
Fix .git ends.
538
        if self.target_repo_name.endswith('.git'):
539
            self.target_repo_name = self.target_repo_name[:-4]
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
540
        # TODO(jelmer): Allow setting title explicitly?
0.431.44 by Jelmer Vernooij
Support get/set description.
541
        title = determine_title(description)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
542
        # TODO(jelmer): Set maintainers_can_modify?
7414.2.1 by Jelmer Vernooij
Support following links in github repositories.
543
        target_repo = self.gh._get_repo(
544
            self.target_owner, self.target_repo_name)
7410.1.3 by Jelmer Vernooij
Fix issue handling.
545
        assignees = []
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
546
        if reviewers:
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
547
            assignees = []
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
548
            for reviewer in reviewers:
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
549
                if '@' in reviewer:
550
                    user = self.gh._get_user_by_email(reviewer)
551
                else:
552
                    user = self.gh._get_user(reviewer)
7410.1.3 by Jelmer Vernooij
Fix issue handling.
553
                assignees.append(user['login'])
7426.1.1 by Jelmer Vernooij
Fix setting of attributes on GitHub pull requests.
554
        else:
555
            assignees = None
556
        try:
557
            pull_request = self.gh._create_pull(
558
                strip_optional(target_repo['pulls_url']),
559
                title=title, body=description,
560
                head="%s:%s" % (self.source_owner, self.source_branch_name),
561
                base=self.target_branch_name,
562
                labels=labels, assignee=assignees)
563
        except ValidationFailed:
564
            raise MergeProposalExists(self.source_branch.user_url)
7371.4.2 by Jelmer Vernooij
More fixes.
565
        return GitHubMergeProposal(self.gh, pull_request)