/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,
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
32
    UnsupportedHoster,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
33
    )
34
35
from ... import (
7340.1.1 by Martin
Fix use of config_dir in propose plugin
36
    bedding,
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
37
    branch as _mod_branch,
0.432.3 by Jelmer Vernooij
Publish command works for github.
38
    controldir,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
39
    errors,
40
    hooks,
41
    urlutils,
0.432.3 by Jelmer Vernooij
Publish command works for github.
42
    version_string as breezy_version,
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
43
    )
7340.1.1 by Martin
Fix use of config_dir in propose plugin
44
from ...config import AuthenticationConfig, GlobalStack
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
45
from ...errors import InvalidHttpResponse
0.431.32 by Jelmer Vernooij
Properly resolve git+ssh URLs.
46
from ...git.urls import git_url_to_bzr_url
0.432.3 by Jelmer Vernooij
Publish command works for github.
47
from ...i18n import gettext
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
48
from ...sixish import PY3
0.432.3 by Jelmer Vernooij
Publish command works for github.
49
from ...trace import note
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
50
from ...transport import get_transport
7296.10.5 by Jelmer Vernooij
use default_user_agent function.
51
from ...transport.http import default_user_agent
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
52
53
54
GITHUB_HOST = 'github.com'
55
WEB_GITHUB_URL = 'https://github.com'
56
API_GITHUB_URL = 'https://api.github.com'
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
57
58
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
59
def store_github_token(scheme, host, token):
7340.1.1 by Martin
Fix use of config_dir in propose plugin
60
    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.
61
        f.write(token)
62
63
64
def retrieve_github_token(scheme, host):
7340.1.1 by Martin
Fix use of config_dir in propose plugin
65
    path = os.path.join(bedding.config_dir(), 'github.conf')
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
66
    if not os.path.exists(path):
67
        return None
0.435.1 by Jelmer Vernooij
Fix reading github credentials.
68
    with open(path, 'r') as f:
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
69
        return f.read().strip()
70
71
0.431.44 by Jelmer Vernooij
Support get/set description.
72
def determine_title(description):
73
    return description.splitlines()[0]
74
75
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
76
class NotGitHubUrl(errors.BzrError):
77
78
    _fmt = "Not a GitHub URL: %(url)s"
79
80
    def __init__(self, url):
81
        errors.BzrError.__init__(self)
82
        self.url = url
83
84
7268.8.2 by Jelmer Vernooij
Handle GitHub errors.
85
class GitHubLoginRequired(HosterLoginRequired):
86
87
    _fmt = "Action requires GitHub login."
88
89
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
90
def connect_github():
7211.13.7 by Jelmer Vernooij
Fix formatting.
91
    """Connect to GitHub.
92
    """
7296.10.5 by Jelmer Vernooij
use default_user_agent function.
93
    user_agent = default_user_agent()
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
94
    auth = AuthenticationConfig()
95
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
96
    credentials = auth.get_credentials('https', GITHUB_HOST)
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
97
    if credentials is not None:
0.432.3 by Jelmer Vernooij
Publish command works for github.
98
        return Github(credentials['user'], credentials['password'],
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
99
                      user_agent=user_agent)
100
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
101
    # TODO(jelmer): token = auth.get_token('https', GITHUB_HOST)
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
102
    if token is not None:
0.431.61 by Jelmer Vernooij
Fix token login.
103
        return Github(token, user_agent=user_agent)
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
104
    else:
105
        note('Accessing GitHub anonymously. To log in, run \'brz gh-login\'.')
106
        return Github(user_agent=user_agent)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
107
108
0.431.44 by Jelmer Vernooij
Support get/set description.
109
class GitHubMergeProposal(MergeProposal):
110
7371.4.2 by Jelmer Vernooij
More fixes.
111
    def __init__(self, gh, pr):
112
        self._gh = gh
0.431.44 by Jelmer Vernooij
Support get/set description.
113
        self._pr = pr
114
115
    @property
116
    def url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
117
        return self._pr['html_url']
0.431.44 by Jelmer Vernooij
Support get/set description.
118
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
119
    def _branch_from_part(self, part):
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
120
        if part['repo'] is None:
121
            return None
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
122
        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.
123
124
    def get_source_branch_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
125
        return self._branch_from_part(self._pr['head'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
126
127
    def get_target_branch_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
128
        return self._branch_from_part(self._pr['base'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
129
0.431.44 by Jelmer Vernooij
Support get/set description.
130
    def get_description(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
131
        return self._pr['body']
0.431.44 by Jelmer Vernooij
Support get/set description.
132
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
133
    def get_commit_message(self):
134
        return None
135
7371.4.2 by Jelmer Vernooij
More fixes.
136
    def set_commit_message(self, message):
137
        self._patch({'title': message})
138
139
    def _patch(self, data):
140
        response = self._gh._api_request(
141
            'PATCH', self._pr['url'], body=json.dumps(data).encode('utf-8'))
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
142
        if response.status != 200:
7371.4.2 by Jelmer Vernooij
More fixes.
143
            raise InvalidHttpResponse(self._pr['url'], response.text)
144
        self._pr = json.loads(response.text)
145
0.431.44 by Jelmer Vernooij
Support get/set description.
146
    def set_description(self, description):
7371.4.2 by Jelmer Vernooij
More fixes.
147
        self._patch({
148
            'body': description,
149
            'title': determine_title(description),
150
            })
0.431.44 by Jelmer Vernooij
Support get/set description.
151
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
152
    def is_merged(self):
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
153
        return self._pr['state'] == 'merged'
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
154
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
155
    def close(self):
7371.4.2 by Jelmer Vernooij
More fixes.
156
        self._patch({'state': 'closed'})
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
157
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
158
    def can_be_merged(self):
159
        return self._pr['mergeable']
160
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
161
    def merge(self, commit_message=None):
162
        # https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-button
163
        self._pr.merge(commit_message=commit_message)
164
0.431.44 by Jelmer Vernooij
Support get/set description.
165
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
166
def parse_github_url(url):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
167
    (scheme, user, password, host, port, path) = urlutils.parse_url(
168
        url)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
169
    if host != GITHUB_HOST:
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
170
        raise NotGitHubUrl(url)
171
    (owner, repo_name) = path.strip('/').split('/')
0.432.12 by Jelmer Vernooij
Fix .git ends.
172
    if repo_name.endswith('.git'):
173
        repo_name = repo_name[:-4]
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
174
    return owner, repo_name
175
176
177
def parse_github_branch_url(branch):
178
    url = urlutils.split_segment_parameters(branch.user_url)[0]
179
    owner, repo_name = parse_github_url(url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
180
    return owner, repo_name, branch.name
181
182
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
183
def github_url_to_bzr_url(url, branch_name):
184
    if not PY3:
185
        branch_name = branch_name.encode('utf-8')
186
    return urlutils.join_segment_parameters(
7211.13.7 by Jelmer Vernooij
Fix formatting.
187
        git_url_to_bzr_url(url), {"branch": branch_name})
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
188
189
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
190
class GitHub(Hoster):
191
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
192
    name = 'github'
193
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
194
    supports_merge_proposal_labels = True
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
195
    supports_merge_proposal_commit_message = False
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
196
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
197
    def __repr__(self):
198
        return "GitHub()"
199
7371.4.2 by Jelmer Vernooij
More fixes.
200
    def _api_request(self, method, path, body=None):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
201
        headers = {
202
            'Accept': 'application/vnd.github.v3+json'}
203
        if self._token:
204
            headers['Authorization'] = 'token %s' % self._token
205
        response = self.transport.request(
206
            method, urlutils.join(self.transport.base, path),
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
207
            headers=headers, body=body, retries=3)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
208
        if response.status == 401:
209
            raise GitHubLoginRequired(self)
210
        return response
211
212
    def _get_repo(self, path):
213
        path = 'repos/' + path
214
        response = self._api_request('GET', path)
215
        if response.status == 404:
216
            raise NoSuchProject(path)
217
        if response.status == 200:
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
218
            return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
219
        raise InvalidHttpResponse(path, response.text)
220
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
221
    def _get_repo_pulls(self, path, head=None, state=None):
222
        path = 'repos/' + path + '/pulls?'
223
        params = {}
224
        if head is not None:
225
            params['head'] = head
226
        if state is not None:
227
            params['state'] = state
228
        path += ';'.join(['%s=%s' % (k, urlutils.quote(v))
229
                         for k, v in params.items()])
230
        response = self._api_request('GET', path)
231
        if response.status == 404:
232
            raise NoSuchProject(path)
233
        if response.status == 200:
234
            return json.loads(response.text)
235
        raise InvalidHttpResponse(path, response.text)
236
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
237
    def _get_user(self, username=None):
238
        if username:
239
            path = 'users/:%s' % username
240
        else:
241
            path = 'user'
242
        response = self._api_request('GET', path)
243
        if response.status != 200:
244
            raise InvalidHttpResponse(path, response.text)
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
245
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
246
247
    def _get_organization(self, name):
248
        path = 'orgs/:%s' % name
249
        response = self._api_request('GET', path)
250
        if response.status != 200:
251
            raise InvalidHttpResponse(path, response.text)
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
252
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
253
254
    def _search_issues(self, query):
255
        path = 'search/issues'
256
        response = self._api_request(
257
            'GET', path + '?q=' + urlutils.quote(query))
258
        if response.status != 200:
259
            raise InvalidHttpResponse(path, response.text)
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
262
    def _create_fork(self, repo, owner=None):
263
        (orig_owner, orig_repo) = repo.split('/')
264
        path = '/repos/:%s/:%s/forks' % (orig_owner, orig_repo)
265
        if owner:
266
            path += '?organization=%s' % owner
267
        response = self._api_request('POST', path)
268
        if response != 202:
269
            raise InvalidHttpResponse(path, response.text)
7359.1.3 by Jelmer Vernooij
Fix GitHub API interaction.
270
        return json.loads(response.text)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
271
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
272
    @property
273
    def base_url(self):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
274
        return WEB_GITHUB_URL
275
276
    def __init__(self, transport):
277
        self._token = retrieve_github_token('https', GITHUB_HOST)
278
        self.transport = transport
279
        self._current_user = self._get_user()
280
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
281
    def publish_derived(self, local_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
282
                        owner=None, revision_id=None, overwrite=False,
283
                        allow_lossy=True):
0.432.12 by Jelmer Vernooij
Fix .git ends.
284
        import github
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
285
        base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
286
        base_repo = self._get_repo('%s/%s' % (base_owner, base_project))
0.432.3 by Jelmer Vernooij
Publish command works for github.
287
        if owner is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
288
            owner = self._current_user['login']
0.432.3 by Jelmer Vernooij
Publish command works for github.
289
        if project is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
290
            project = base_repo['name']
0.432.3 by Jelmer Vernooij
Publish command works for github.
291
        try:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
292
            remote_repo = self._get_repo('%s/%s' % (owner, project))
0.432.12 by Jelmer Vernooij
Fix .git ends.
293
        except github.UnknownObjectException:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
294
            base_repo = self._get_repo('%s/%s' % (base_owner, base_project))
295
            remote_repo = self._create_fork(base_repo, owner)
0.432.3 by Jelmer Vernooij
Publish command works for github.
296
            note(gettext('Forking new repository %s from %s') %
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
297
                 (remote_repo['html_url'], base_repo['html_url']))
0.432.3 by Jelmer Vernooij
Publish command works for github.
298
        else:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
299
            note(gettext('Reusing existing repository %s') % remote_repo['html_url'])
300
        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.
301
        try:
7211.13.7 by Jelmer Vernooij
Fix formatting.
302
            push_result = remote_dir.push_branch(
303
                local_branch, revision_id=revision_id, overwrite=overwrite,
304
                name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
305
        except errors.NoRoundtrippingSupport:
306
            if not allow_lossy:
307
                raise
7211.13.7 by Jelmer Vernooij
Fix formatting.
308
            push_result = remote_dir.push_branch(
309
                local_branch, revision_id=revision_id,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
310
                overwrite=overwrite, name=name, lossy=True)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
311
        return push_result.target_branch, github_url_to_bzr_url(
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
312
            remote_repo['html_url'], name)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
313
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
314
    def get_push_url(self, branch):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
315
        owner, project, branch_name = parse_github_branch_url(branch)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
316
        repo = self._get_repo('%s/%s' % (owner, project))
317
        return github_url_to_bzr_url(repo['ssh_url'], branch_name)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
318
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
319
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
320
        import github
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
321
        base_owner, base_project, base_branch_name = parse_github_branch_url(base_branch)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
322
        base_repo = self._get_repo('%s/%s' % (base_owner, base_project))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
323
        if owner is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
324
            owner = self._current_user['login']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
325
        if project is None:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
326
            project = base_repo['name']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
327
        try:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
328
            remote_repo = self._get_repo('%s/%s' % (owner, project))
329
            full_url = github_url_to_bzr_url(remote_repo['ssh_url'], name)
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
330
            return _mod_branch.Branch.open(full_url)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
331
        except github.UnknownObjectException:
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
332
            raise errors.NotBranchError('%s/%s/%s' % (WEB_GITHUB_URL, owner, project))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
333
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
334
    def get_proposer(self, source_branch, target_branch):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
335
        return GitHubMergeProposalBuilder(self, source_branch, target_branch)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
336
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
337
    def iter_proposals(self, source_branch, target_branch, status='open'):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
338
        (source_owner, source_repo_name, source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
339
            parse_github_branch_url(source_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
340
        (target_owner, target_repo_name, target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
341
            parse_github_branch_url(target_branch))
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
342
        target_repo_path = "%s/%s" % (target_owner, target_repo_name)
343
        target_repo = self._get_repo(target_repo_path)
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
344
        state = {
345
            'open': 'open',
346
            'merged': 'closed',
347
            'closed': 'closed',
348
            'all': 'all'}
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
349
        pulls = self._get_repo_pulls(
350
            target_repo_path,
351
            head=target_branch_name,
352
            state=state[status])
353
        for pull in pulls:
354
            if (status == 'closed' and pull['merged'] or
355
                    status == 'merged' and not pull['merged']):
356
                continue
357
            if pull['head']['ref'] != source_branch_name:
358
                continue
359
            if pull['head']['repo'] is None:
7268.4.1 by Jelmer Vernooij
Don't attempt to resolve None when repo has gone away.
360
                # Repo has gone the way of the dodo
361
                continue
7371.4.1 by Jelmer Vernooij
Fix iter_proposals for GitHub.
362
            if (pull['head']['repo']['owner']['login'] != source_owner or
363
                    pull['head']['repo']['name'] != source_repo_name):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
364
                continue
7371.4.2 by Jelmer Vernooij
More fixes.
365
            yield GitHubMergeProposal(self, pull)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
366
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
367
    def hosts(self, branch):
368
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
369
            parse_github_branch_url(branch)
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
370
        except NotGitHubUrl:
371
            return False
372
        else:
373
            return True
374
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
375
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
376
    def probe_from_url(cls, url, possible_transports=None):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
377
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
378
            parse_github_url(url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
379
        except NotGitHubUrl:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
380
            raise UnsupportedHoster(url)
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
381
        transport = get_transport(
382
            API_GITHUB_URL, possible_transports=possible_transports)
383
        return cls(transport)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
384
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
385
    @classmethod
386
    def iter_instances(cls):
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
387
        yield cls(get_transport(API_GITHUB_URL))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
388
0.431.66 by Jelmer Vernooij
Add support for status argument.
389
    def iter_my_proposals(self, status='open'):
390
        query = ['is:pr']
391
        if status == 'open':
392
            query.append('is:open')
393
        elif status == 'closed':
394
            query.append('is:unmerged')
7268.2.1 by Jelmer Vernooij
Don't include open unmerged pull requests in 'closed'.
395
            # Also use "is:closed" otherwise unmerged open pull requests are
396
            # also included.
397
            query.append('is:closed')
0.431.66 by Jelmer Vernooij
Add support for status argument.
398
        elif status == 'merged':
399
            query.append('is:merged')
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
400
        query.append('author:%s' % self._current_user['login'])
401
        for issue in self._search_issues(query=' '.join(query))['items']:
7360.1.5 by Jelmer Vernooij
Use authenticated request when requesting pull requests from GitHub; authenticated connections have a much lower rate limit.
402
            url = issue['pull_request']['url']
403
            response = self._api_request('GET', url)
404
            if response.status != 200:
405
                raise InvalidHttpResponse(url, response.text)
7371.4.2 by Jelmer Vernooij
More fixes.
406
            yield GitHubMergeProposal(self, json.loads(response.text))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
407
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
408
    def get_proposal_by_url(self, url):
409
        raise UnsupportedHoster(url)
410
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
411
0.432.2 by Jelmer Vernooij
Publish command sort of works.
412
class GitHubMergeProposalBuilder(MergeProposalBuilder):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
413
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
414
    def __init__(self, gh, source_branch, target_branch):
415
        self.gh = gh
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
416
        self.source_branch = source_branch
417
        self.target_branch = target_branch
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
418
        (self.target_owner, self.target_repo_name, self.target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
419
            parse_github_branch_url(self.target_branch))
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
420
        (self.source_owner, self.source_repo_name, self.source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
421
            parse_github_branch_url(self.source_branch))
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
422
423
    def get_infotext(self):
424
        """Determine the initial comment for the merge proposal."""
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
425
        info = []
426
        info.append("Merge %s into %s:%s\n" % (
427
            self.source_branch_name, self.target_owner,
428
            self.target_branch_name))
429
        info.append("Source: %s\n" % self.source_branch.user_url)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
430
        info.append("Target: %s\n" % self.target_branch.user_url)
431
        return ''.join(info)
432
433
    def get_initial_body(self):
434
        """Get a body for the proposal for the user to modify.
435
436
        :return: a str or None.
437
        """
438
        return None
439
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
440
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
441
                        prerequisite_branch=None, commit_message=None):
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
442
        """Perform the submission."""
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
443
        if prerequisite_branch is not None:
444
            raise PrerequisiteBranchUnsupported(self)
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
445
        # Note that commit_message is ignored, since github doesn't support it.
0.432.10 by Jelmer Vernooij
More test fixes.
446
        import github
0.432.7 by Jelmer Vernooij
propose works \o/
447
        # TODO(jelmer): Probe for right repo name
0.432.12 by Jelmer Vernooij
Fix .git ends.
448
        if self.target_repo_name.endswith('.git'):
449
            self.target_repo_name = self.target_repo_name[:-4]
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
450
        target_repo = self.gh._get_repo("%s/%s" % (self.target_owner, self.target_repo_name))
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
451
        # TODO(jelmer): Allow setting title explicitly?
0.431.44 by Jelmer Vernooij
Support get/set description.
452
        title = determine_title(description)
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
453
        # TOOD(jelmer): Set maintainers_can_modify?
0.432.10 by Jelmer Vernooij
More test fixes.
454
        try:
455
            pull_request = target_repo.create_pull(
456
                title=title, body=description,
457
                head="%s:%s" % (self.source_owner, self.source_branch_name),
458
                base=self.target_branch_name)
459
        except github.GithubException as e:
460
            if e.status == 422:
461
                raise MergeProposalExists(self.source_branch.user_url)
462
            raise
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
463
        if reviewers:
464
            for reviewer in reviewers:
465
                pull_request.assignees.append(
7296.11.1 by Jelmer Vernooij
Initial work migrating GitHub away from library.
466
                    self.gh._get_user(reviewer)['login'])
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
467
        if labels:
468
            for label in labels:
469
                pull_request.issue.labels.append(label)
7371.4.2 by Jelmer Vernooij
More fixes.
470
        return GitHubMergeProposal(self.gh, pull_request)