/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.431.5 by Jelmer Vernooij
Initial work on gitlab 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 GitLab."""
18
7296.10.8 by Jelmer Vernooij
Remove json attribute from Response object, consistent with urllib3 API.
19
import json
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
20
import os
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
21
import time
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
22
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
23
from ... import (
7340.1.1 by Martin
Fix use of config_dir in propose plugin
24
    bedding,
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
25
    branch as _mod_branch,
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
26
    controldir,
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
27
    errors,
28
    urlutils,
29
    )
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
30
from ...git.urls import git_url_to_bzr_url
7380.1.2 by Jelmer Vernooij
Review comments.
31
from ...trace import mutter
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
32
from ...transport import get_transport
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
33
7408.3.1 by Jelmer Vernooij
Move propose module into core.
34
from ...propose import (
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
35
    determine_title,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
36
    Hoster,
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
37
    MergeProposal,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
38
    MergeProposalBuilder,
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
39
    MergeProposalExists,
0.431.38 by Jelmer Vernooij
Add NoSuchProject.
40
    NoSuchProject,
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
41
    PrerequisiteBranchUnsupported,
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
42
    UnsupportedHoster,
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
43
    )
44
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
45
46
_DEFAULT_FILES = ['/etc/python-gitlab.cfg', '~/.python-gitlab.cfg']
7408.1.2 by Jelmer Vernooij
Set default page size to 50.
47
DEFAULT_PAGE_SIZE = 50
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
48
49
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
50
def mp_status_to_status(status):
51
    return {
52
        'all': 'all',
53
        'open': 'opened',
54
        'merged': 'merged',
55
        'closed': 'closed'}[status]
56
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
57
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
58
class NotGitLabUrl(errors.BzrError):
59
60
    _fmt = "Not a GitLab URL: %(url)s"
61
62
    def __init__(self, url):
63
        errors.BzrError.__init__(self)
64
        self.url = url
65
66
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
67
class NotMergeRequestUrl(errors.BzrError):
68
69
    _fmt = "Not a merge proposal URL: %(url)s"
70
71
    def __init__(self, host, url):
72
        errors.BzrError.__init__(self)
73
        self.host = host
74
        self.url = url
75
76
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
77
class DifferentGitLabInstances(errors.BzrError):
78
79
    _fmt = ("Can't create merge proposals across GitLab instances: "
80
            "%(source_host)s and %(target_host)s")
81
82
    def __init__(self, source_host, target_host):
83
        self.source_host = source_host
84
        self.target_host = target_host
85
86
0.432.10 by Jelmer Vernooij
More test fixes.
87
class GitLabLoginMissing(errors.BzrError):
88
89
    _fmt = ("Please log into GitLab")
90
91
7296.10.2 by Jelmer Vernooij
More fixes.
92
class GitlabLoginError(errors.BzrError):
93
94
    _fmt = ("Error logging in: %(error)s")
95
96
    def __init__(self, error):
97
        self.error = error
98
99
7490.96.1 by Jelmer Vernooij
Raise proper exception when getting a 409 back from GitLab.
100
class GitLabConflict(errors.BzrError):
101
7490.106.1 by Jelmer Vernooij
Avoid use of reserved 'msg' argument for BrzError.
102
    _fmt = "Conflict during operation: %(reason)s"
7490.96.1 by Jelmer Vernooij
Raise proper exception when getting a 409 back from GitLab.
103
7490.106.1 by Jelmer Vernooij
Avoid use of reserved 'msg' argument for BrzError.
104
    def __init__(self, reason):
105
        errors.BzrError(self, reason=reason)
7490.96.1 by Jelmer Vernooij
Raise proper exception when getting a 409 back from GitLab.
106
107
7490.67.1 by Jelmer Vernooij
Raise proper exception when forking isn't available for a project.
108
class ForkingDisabled(errors.BzrError):
109
110
    _fmt = ("Forking on project %(project)s is disabled.")
111
112
    def __init__(self, project):
113
        self.project = project
114
115
7490.10.1 by Jelmer Vernooij
Fix handling of 409s for gitlab.
116
class MergeRequestExists(Exception):
117
    """Raised when a merge requests already exists."""
118
119
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
120
def default_config_path():
7340.1.1 by Martin
Fix use of config_dir in propose plugin
121
    return os.path.join(bedding.config_dir(), 'gitlab.conf')
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
122
123
124
def store_gitlab_token(name, url, private_token):
125
    """Store a GitLab token in a configuration file."""
126
    import configparser
127
    config = configparser.ConfigParser()
128
    path = default_config_path()
129
    config.read([path])
130
    config.add_section(name)
131
    config[name]['url'] = url
132
    config[name]['private_token'] = private_token
133
    with open(path, 'w') as f:
134
        config.write(f)
135
136
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
137
def iter_tokens():
138
    import configparser
139
    config = configparser.ConfigParser()
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
140
    config.read(
141
        [os.path.expanduser(p) for p in _DEFAULT_FILES] +
142
        [default_config_path()])
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
143
    for name, section in config.items():
144
        yield name, section
145
146
7359.1.2 by Jelmer Vernooij
Some fixes for gitlab API.
147
def get_credentials_by_url(url):
148
    for name, credentials in iter_tokens():
149
        if 'url' not in credentials:
150
            continue
151
        if credentials['url'].rstrip('/') == url.rstrip('/'):
152
            return credentials
153
    else:
154
        return None
155
156
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
157
def parse_gitlab_url(url):
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
158
    (scheme, user, password, host, port, path) = urlutils.parse_url(
159
        url)
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
160
    if scheme not in ('git+ssh', 'https', 'http'):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
161
        raise NotGitLabUrl(url)
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
162
    if not host:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
163
        raise NotGitLabUrl(url)
0.432.10 by Jelmer Vernooij
More test fixes.
164
    path = path.strip('/')
0.432.11 by Jelmer Vernooij
Fix some tests.
165
    if path.endswith('.git'):
166
        path = path[:-4]
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
167
    return host, path
168
169
170
def parse_gitlab_branch_url(branch):
7441.1.1 by Jelmer Vernooij
Add strip_segment_parameters function.
171
    url = urlutils.strip_segment_parameters(branch.user_url)
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
172
    host, path = parse_gitlab_url(url)
0.432.10 by Jelmer Vernooij
More test fixes.
173
    return host, path, branch.name
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
174
175
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
176
def parse_gitlab_merge_request_url(url):
177
    (scheme, user, password, host, port, path) = urlutils.parse_url(
178
        url)
179
    if scheme not in ('git+ssh', 'https', 'http'):
180
        raise NotGitLabUrl(url)
181
    if not host:
182
        raise NotGitLabUrl(url)
183
    path = path.strip('/')
184
    parts = path.split('/')
7490.23.1 by Jelmer Vernooij
Add support for newer style gitlab merge proposal URLs.
185
    if len(parts) < 2:
186
        raise NotMergeRequestUrl(host, url)
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
187
    if parts[-2] != 'merge_requests':
188
        raise NotMergeRequestUrl(host, url)
7490.23.1 by Jelmer Vernooij
Add support for newer style gitlab merge proposal URLs.
189
    if parts[-3] == '-':
190
        project_name = '/'.join(parts[:-3])
191
    else:
192
        project_name = '/'.join(parts[:-2])
193
    return host, project_name, int(parts[-1])
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
194
195
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
196
class GitLabMergeProposal(MergeProposal):
197
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
198
    def __init__(self, gl, mr):
199
        self.gl = gl
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
200
        self._mr = mr
201
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
202
    def _update(self, **kwargs):
203
        self.gl._update_merge_request(self._mr['project_id'], self._mr['iid'], kwargs)
204
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
205
    def __repr__(self):
206
        return "<%s at %r>" % (type(self).__name__, self._mr['web_url'])
207
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
208
    @property
209
    def url(self):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
210
        return self._mr['web_url']
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
211
212
    def get_description(self):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
213
        return self._mr['description']
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
214
215
    def set_description(self, description):
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
216
        self._update(description=description, title=determine_title(description))
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
217
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
218
    def get_commit_message(self):
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
219
        return self._mr.get('merge_commit_message')
220
221
    def set_commit_message(self, message):
222
        raise errors.UnsupportedOperation(self.set_commit_message, self)
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
223
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
224
    def _branch_url_from_project(self, project_id, branch_name):
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
225
        if project_id is None:
226
            return None
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
227
        project = self.gl._get_project(project_id)
7296.10.3 by Jelmer Vernooij
More fixes.
228
        return gitlab_url_to_bzr_url(project['http_url_to_repo'], branch_name)
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
229
230
    def get_source_branch_url(self):
231
        return self._branch_url_from_project(
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
232
            self._mr['source_project_id'], self._mr['source_branch'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
233
7490.65.1 by Jelmer Vernooij
Add functions fo retrieving merge proposal source revision.
234
    def get_source_revision(self):
235
        from breezy.git.mapping import default_mapping
7490.65.2 by Jelmer Vernooij
Fix bugs, add release note.
236
        sha = self._mr['sha']
237
        if sha is None:
238
            return None
239
        return default_mapping.revision_id_foreign_to_bzr(sha.encode('ascii'))
7490.65.1 by Jelmer Vernooij
Add functions fo retrieving merge proposal source revision.
240
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
241
    def get_target_branch_url(self):
242
        return self._branch_url_from_project(
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
243
            self._mr['target_project_id'], self._mr['target_branch'])
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
244
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
245
    def _get_project_name(self, project_id):
246
        source_project = self.gl._get_project(project_id)
247
        return source_project['path_with_namespace']
248
249
    def get_source_project(self):
250
        return self._get_project_name(self._mr['source_project_id'])
251
252
    def get_target_project(self):
253
        return self._get_project_name(self._mr['target_project_id'])
254
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
255
    def is_merged(self):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
256
        return (self._mr['state'] == 'merged')
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
257
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
258
    def is_closed(self):
259
        return (self._mr['state'] == 'closed')
260
261
    def reopen(self):
7405.2.1 by Jelmer Vernooij
Fix reopen behaviour for gitlab.
262
        return self._update(state_event='reopen')
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
263
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
264
    def close(self):
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
265
        self._update(state_event='close')
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
266
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
267
    def merge(self, commit_message=None):
268
        # https://docs.gitlab.com/ee/api/merge_requests.html#accept-mr
269
        self._mr.merge(merge_commit_message=commit_message)
270
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
271
    def can_be_merged(self):
272
        if self._mr['merge_status'] == 'cannot_be_merged':
273
            return False
274
        elif self._mr['merge_status'] == 'can_be_merged':
275
            return True
7490.30.2 by Jelmer Vernooij
Support cannot_be_merged_rechecked
276
        elif self._mr['merge_status'] in (
277
                'unchecked', 'cannot_be_merged_recheck'):
278
            # See https://gitlab.com/gitlab-org/gitlab/-/commit/7517105303c for
279
            # an explanation of the distinction between unchecked and
280
            # cannot_be_merged_recheck
7490.30.1 by Jelmer Vernooij
Support the 'unchecked' value for can_be_merged.
281
            return None
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
282
        else:
283
            raise ValueError(self._mr['merge_status'])
284
7414.4.1 by Jelmer Vernooij
Add a MergeProposal.get_merged_by method.
285
    def get_merged_by(self):
7414.4.2 by Jelmer Vernooij
Fix gitlab / github merged_by fetching.
286
        user = self._mr.get('merged_by')
287
        if user is None:
288
            return None
289
        return user['username']
7414.4.1 by Jelmer Vernooij
Add a MergeProposal.get_merged_by method.
290
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
291
    def get_merged_at(self):
292
        merged_at = self._mr.get('merged_at')
293
        if merged_at is None:
294
            return None
7414.4.4 by Jelmer Vernooij
Use iso8601 module.
295
        import iso8601
296
        return iso8601.parse_date(merged_at)
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
297
7490.52.1 by Jelmer Vernooij
Add MergeProposal.post_comment.
298
    def post_comment(self, body):
299
        kwargs = {'body': body}
300
        self.gl._post_merge_request_note(
301
            self._mr['project_id'], self._mr['iid'], kwargs)
302
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
303
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
304
def gitlab_url_to_bzr_url(url, name):
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
305
    return git_url_to_bzr_url(url, branch=name)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
306
307
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
308
class GitLab(Hoster):
309
    """GitLab hoster implementation."""
310
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
311
    supports_merge_proposal_labels = True
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
312
    supports_merge_proposal_commit_message = False
7490.3.9 by Jelmer Vernooij
Add supports_allow_collaboration flag.
313
    supports_allow_collaboration = True
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
314
    merge_proposal_description_format = 'markdown'
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
315
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
316
    def __repr__(self):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
317
        return "<GitLab(%r)>" % self.base_url
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
318
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
319
    @property
320
    def base_url(self):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
321
        return self.transport.base
322
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
323
    @property
324
    def base_hostname(self):
325
        return urlutils.parse_url(self.base_url)[3]
326
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
327
    def _api_request(self, method, path, fields=None, body=None):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
328
        return self.transport.request(
329
            method, urlutils.join(self.base_url, 'api', 'v4', path),
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
330
            headers=self.headers, fields=fields, body=body)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
331
332
    def __init__(self, transport, private_token):
333
        self.transport = transport
334
        self.headers = {"Private-Token": private_token}
335
        self.check()
336
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
337
    def _get_user(self, username):
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
338
        path = 'users/%s' % urlutils.quote(str(username), '')
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
339
        response = self._api_request('GET', path)
340
        if response.status == 404:
341
            raise KeyError('no such user %s' % username)
342
        if response.status == 200:
343
            return json.loads(response.data)
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
344
        raise errors.UnexpectedHttpStatus(path, response.status)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
345
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
346
    def _get_user_by_email(self, email):
347
        path = 'users?search=%s' % urlutils.quote(str(email), '')
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
348
        response = self._api_request('GET', path)
349
        if response.status == 404:
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
350
            raise KeyError('no such user %s' % email)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
351
        if response.status == 200:
352
            ret = json.loads(response.data)
353
            if len(ret) != 1:
354
                raise ValueError('unexpected number of results; %r' % ret)
355
            return ret[0]
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
356
        raise errors.UnexpectedHttpStatus(path, response.status)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
357
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
358
    def _get_project(self, project_name):
7359.1.2 by Jelmer Vernooij
Some fixes for gitlab API.
359
        path = 'projects/%s' % urlutils.quote(str(project_name), '')
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
360
        response = self._api_request('GET', path)
361
        if response.status == 404:
362
            raise NoSuchProject(project_name)
363
        if response.status == 200:
7296.10.8 by Jelmer Vernooij
Remove json attribute from Response object, consistent with urllib3 API.
364
            return json.loads(response.data)
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
365
        raise errors.UnexpectedHttpStatus(path, response.status)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
366
7490.102.1 by Jelmer Vernooij
Fix support for owner in GitLab proposals.
367
    def _fork_project(self, project_name, timeout=50, interval=5, owner=None):
7359.1.2 by Jelmer Vernooij
Some fixes for gitlab API.
368
        path = 'projects/%s/fork' % urlutils.quote(str(project_name), '')
7490.102.1 by Jelmer Vernooij
Fix support for owner in GitLab proposals.
369
        fields = {}
370
        if owner is not None:
371
            fields['namespace'] = owner
372
        response = self._api_request('POST', path, fields=fields)
7490.67.1 by Jelmer Vernooij
Raise proper exception when forking isn't available for a project.
373
        if response.status == 404:
374
            raise ForkingDisabled(project_name)
7490.96.1 by Jelmer Vernooij
Raise proper exception when getting a 409 back from GitLab.
375
        if response.status == 409:
7490.96.2 by Jelmer Vernooij
Avoid async for now.
376
            resp = json.loads(response.data)
377
            raise GitLabConflict(resp.get('message'))
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
378
        if response.status not in (200, 201):
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
379
            raise errors.UnexpectedHttpStatus(path, response.status)
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
380
        # The response should be valid JSON, but let's ignore it
7397.1.1 by Jelmer Vernooij
Fix project forking.
381
        project = json.loads(response.data)
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
382
        # Spin and wait until import_status for new project
383
        # is complete.
7380.1.2 by Jelmer Vernooij
Review comments.
384
        deadline = time.time() + timeout
7397.1.1 by Jelmer Vernooij
Fix project forking.
385
        while project['import_status'] not in ('finished', 'none'):
7380.1.2 by Jelmer Vernooij
Review comments.
386
            mutter('import status is %s', project['import_status'])
387
            if time.time() > deadline:
388
                raise Exception('timeout waiting for project to become available')
389
            time.sleep(interval)
7397.1.1 by Jelmer Vernooij
Fix project forking.
390
            project = self._get_project(project['path_with_namespace'])
391
        return project
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
392
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
393
    def get_current_user(self):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
394
        return self._current_user['username']
395
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
396
    def get_user_url(self, username):
397
        return urlutils.join(self.base_url, username)
398
7408.1.1 by Jelmer Vernooij
Use paging to iterate over all gitlab pull requests.
399
    def _list_paged(self, path, parameters=None, per_page=None):
400
        if parameters is None:
401
            parameters = {}
402
        else:
403
            parameters = dict(parameters.items())
404
        if per_page:
7408.1.3 by Jelmer Vernooij
Support pagination for github.
405
            parameters['per_page'] = str(per_page)
7408.1.1 by Jelmer Vernooij
Use paging to iterate over all gitlab pull requests.
406
        page = "1"
407
        while page:
408
            parameters['page'] = page
409
            response = self._api_request(
410
                'GET', path + '?' +
411
                ';'.join(['%s=%s' % item for item in parameters.items()]))
412
            if response.status == 403:
413
                raise errors.PermissionDenied(response.text)
414
            if response.status != 200:
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
415
                raise errors.UnexpectedHttpStatus(path, response.status)
7408.1.1 by Jelmer Vernooij
Use paging to iterate over all gitlab pull requests.
416
            page = response.getheader("X-Next-Page")
417
            for entry in json.loads(response.data):
418
                yield entry
419
7296.10.9 by Jelmer Vernooij
Fix method name spacing.
420
    def _list_merge_requests(self, owner=None, project=None, state=None):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
421
        if project is not None:
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
422
            path = 'projects/%s/merge_requests' % urlutils.quote(str(project), '')
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
423
        else:
424
            path = 'merge_requests'
425
        parameters = {}
426
        if state:
427
            parameters['state'] = state
428
        if owner:
429
            parameters['owner_id'] = urlutils.quote(owner, '')
7408.1.2 by Jelmer Vernooij
Set default page size to 50.
430
        return self._list_paged(path, parameters, per_page=DEFAULT_PAGE_SIZE)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
431
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
432
    def _get_merge_request(self, project, merge_id):
433
        path = 'projects/%s/merge_requests/%d' % (urlutils.quote(str(project), ''), merge_id)
434
        response = self._api_request('GET', path)
435
        if response.status == 403:
436
            raise errors.PermissionDenied(response.text)
437
        if response.status != 200:
7490.90.7 by Jelmer Vernooij
Fix syntax.
438
            raise errors.UnexpectedHttpStatus(path, response.status)
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
439
        return json.loads(response.data)
440
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
441
    def _list_projects(self, owner):
442
        path = 'users/%s/projects' % urlutils.quote(str(owner), '')
443
        parameters = {}
444
        return self._list_paged(path, parameters, per_page=DEFAULT_PAGE_SIZE)
445
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
446
    def _update_merge_request(self, project_id, iid, mr):
447
        path = 'projects/%s/merge_requests/%s' % (
448
            urlutils.quote(str(project_id), ''), iid)
449
        response = self._api_request('PUT', path, fields=mr)
450
        if response.status == 200:
451
            return json.loads(response.data)
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
452
        raise errors.UnexpectedHttpStatus(path, response.status)
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
453
7490.52.1 by Jelmer Vernooij
Add MergeProposal.post_comment.
454
    def _post_merge_request_note(self, project_id, iid, kwargs):
455
        path = 'projects/%s/merge_requests/%s/notes' % (
456
            urlutils.quote(str(project_id), ''), iid)
457
        response = self._api_request('POST', path, fields=kwargs)
458
        if response.status == 201:
459
            json.loads(response.data)
460
            return
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
461
        raise errors.UnexpectedHttpStatus(path, response.status)
7490.52.1 by Jelmer Vernooij
Add MergeProposal.post_comment.
462
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
463
    def _create_mergerequest(
464
            self, title, source_project_id, target_project_id,
7296.10.3 by Jelmer Vernooij
More fixes.
465
            source_branch_name, target_branch_name, description,
7490.14.1 by Jelmer Vernooij
Various git fixes.
466
            labels=None, allow_collaboration=False):
7359.1.2 by Jelmer Vernooij
Some fixes for gitlab API.
467
        path = 'projects/%s/merge_requests' % source_project_id
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
468
        fields = {
469
            'title': title,
470
            'source_branch': source_branch_name,
471
            'target_branch': target_branch_name,
472
            'target_project_id': target_project_id,
473
            'description': description,
7490.14.1 by Jelmer Vernooij
Various git fixes.
474
            'allow_collaboration': allow_collaboration,
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
475
            }
476
        if labels:
477
            fields['labels'] = labels
7380.1.1 by Jelmer Vernooij
Several more fixes for git merge proposals.
478
        response = self._api_request('POST', path, fields=fields)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
479
        if response.status == 403:
480
            raise errors.PermissionDenied(response.text)
481
        if response.status == 409:
7490.10.1 by Jelmer Vernooij
Fix handling of 409s for gitlab.
482
            raise MergeRequestExists()
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
483
        if response.status != 201:
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
484
            raise errors.UnexpectedHttpStatus(path, response.status)
7296.10.8 by Jelmer Vernooij
Remove json attribute from Response object, consistent with urllib3 API.
485
        return json.loads(response.data)
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
486
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
487
    def get_push_url(self, branch):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
488
        (host, project_name, branch_name) = parse_gitlab_branch_url(branch)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
489
        project = self._get_project(project_name)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
490
        return gitlab_url_to_bzr_url(
7296.10.3 by Jelmer Vernooij
More fixes.
491
            project['ssh_url_to_repo'], branch_name)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
492
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
493
    def publish_derived(self, local_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
494
                        owner=None, revision_id=None, overwrite=False,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
495
                        allow_lossy=True, tag_selector=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
496
        (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch)
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
497
        if owner is None:
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
498
            owner = self.get_current_user()
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
499
        if project is None:
7296.10.3 by Jelmer Vernooij
More fixes.
500
            project = self._get_project(base_project)['path']
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
501
        try:
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
502
            target_project = self._get_project('%s/%s' % (owner, project))
503
        except NoSuchProject:
7490.102.1 by Jelmer Vernooij
Fix support for owner in GitLab proposals.
504
            target_project = self._fork_project(base_project, owner=owner)
7296.10.3 by Jelmer Vernooij
More fixes.
505
        remote_repo_url = git_url_to_bzr_url(target_project['ssh_url_to_repo'])
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
506
        remote_dir = controldir.ControlDir.open(remote_repo_url)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
507
        try:
7211.13.7 by Jelmer Vernooij
Fix formatting.
508
            push_result = remote_dir.push_branch(
509
                local_branch, revision_id=revision_id, overwrite=overwrite,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
510
                name=name, tag_selector=tag_selector)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
511
        except errors.NoRoundtrippingSupport:
512
            if not allow_lossy:
513
                raise
7211.13.7 by Jelmer Vernooij
Fix formatting.
514
            push_result = remote_dir.push_branch(
515
                local_branch, revision_id=revision_id, overwrite=overwrite,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
516
                name=name, lossy=True, tag_selector=tag_selector)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
517
        public_url = gitlab_url_to_bzr_url(
7296.10.3 by Jelmer Vernooij
More fixes.
518
            target_project['http_url_to_repo'], name)
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
519
        return push_result.target_branch, public_url
0.432.4 by Jelmer Vernooij
Some work on gitlab.
520
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
521
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
522
        (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
523
        if owner is None:
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
524
            owner = self.get_current_user()
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
525
        if project is None:
7296.10.3 by Jelmer Vernooij
More fixes.
526
            project = self._get_project(base_project)['path']
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
527
        try:
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
528
            target_project = self._get_project('%s/%s' % (owner, project))
529
        except NoSuchProject:
530
            raise errors.NotBranchError('%s/%s/%s' % (self.base_url, owner, project))
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
531
        return _mod_branch.Branch.open(gitlab_url_to_bzr_url(
7296.10.3 by Jelmer Vernooij
More fixes.
532
            target_project['ssh_url_to_repo'], name))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
533
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
534
    def get_proposer(self, source_branch, target_branch):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
535
        return GitlabMergeProposalBuilder(self, source_branch, target_branch)
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
536
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
537
    def iter_proposals(self, source_branch, target_branch, status):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
538
        (source_host, source_project_name, source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
539
            parse_gitlab_branch_url(source_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
540
        (target_host, target_project_name, target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
541
            parse_gitlab_branch_url(target_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
542
        if source_host != target_host:
543
            raise DifferentGitLabInstances(source_host, target_host)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
544
        source_project = self._get_project(source_project_name)
545
        target_project = self._get_project(target_project_name)
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
546
        state = mp_status_to_status(status)
7360.1.4 by Jelmer Vernooij
Fix retrieval of proposals from gitlab.
547
        for mr in self._list_merge_requests(
7296.10.3 by Jelmer Vernooij
More fixes.
548
                project=target_project['id'], state=state):
549
            if (mr['source_project_id'] != source_project['id'] or
550
                    mr['source_branch'] != source_branch_name or
551
                    mr['target_project_id'] != target_project['id'] or
552
                    mr['target_branch'] != target_branch_name):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
553
                continue
554
            yield GitLabMergeProposal(self, mr)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
555
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
556
    def hosts(self, branch):
557
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
558
            (host, project, branch_name) = parse_gitlab_branch_url(branch)
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
559
        except NotGitLabUrl:
560
            return False
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
561
        return self.base_hostname == host
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
562
563
    def check(self):
564
        response = self._api_request('GET', 'user')
565
        if response.status == 200:
7296.10.8 by Jelmer Vernooij
Remove json attribute from Response object, consistent with urllib3 API.
566
            self._current_user = json.loads(response.data)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
567
            return
7296.10.2 by Jelmer Vernooij
More fixes.
568
        if response == 401:
7296.10.8 by Jelmer Vernooij
Remove json attribute from Response object, consistent with urllib3 API.
569
            if json.loads(response.data) == {"message": "401 Unauthorized"}:
7296.10.2 by Jelmer Vernooij
More fixes.
570
                raise GitLabLoginMissing()
571
            else:
572
                raise GitlabLoginError(response.text)
7490.86.2 by Jelmer Vernooij
If there is no github login, then don't list github as supported.
573
        raise UnsupportedHoster(self.base_url)
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
574
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
575
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
576
    def probe_from_url(cls, url, possible_transports=None):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
577
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
578
            (host, project) = parse_gitlab_url(url)
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
579
        except NotGitLabUrl:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
580
            raise UnsupportedHoster(url)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
581
        transport = get_transport(
582
            'https://%s' % host, possible_transports=possible_transports)
7359.1.2 by Jelmer Vernooij
Some fixes for gitlab API.
583
        credentials = get_credentials_by_url(transport.base)
584
        if credentials is not None:
585
            return cls(transport, credentials.get('private_token'))
586
        raise UnsupportedHoster(url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
587
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
588
    @classmethod
589
    def iter_instances(cls):
590
        for name, credentials in iter_tokens():
591
            if 'url' not in credentials:
592
                continue
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
593
            yield cls(
594
                get_transport(credentials['url']),
595
                private_token=credentials.get('private_token'))
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
596
0.431.66 by Jelmer Vernooij
Add support for status argument.
597
    def iter_my_proposals(self, status='open'):
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
598
        state = mp_status_to_status(status)
7296.10.9 by Jelmer Vernooij
Fix method name spacing.
599
        for mp in self._list_merge_requests(
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
600
                owner=self.get_current_user(), state=state):
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
601
            yield GitLabMergeProposal(self, mp)
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
602
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
603
    def iter_my_forks(self):
7490.105.1 by Jelmer Vernooij
Add some hoster metadata fields.
604
        for project in self._list_projects(owner=self.get_current_user()):
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
605
            base_project = project.get('forked_from_project')
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
606
            if not base_project:
607
                continue
608
            yield project['path_with_namespace']
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
609
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
610
    def get_proposal_by_url(self, url):
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
611
        try:
612
            (host, project, merge_id) = parse_gitlab_merge_request_url(url)
613
        except NotGitLabUrl:
614
            raise UnsupportedHoster(url)
7296.9.4 by Jelmer Vernooij
Fix dealing with non-gitlab sites.
615
        except NotMergeRequestUrl as e:
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
616
            if self.base_hostname == e.host:
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
617
                raise
618
            else:
619
                raise UnsupportedHoster(url)
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
620
        if self.base_hostname != host:
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
621
            raise UnsupportedHoster(url)
7360.1.4 by Jelmer Vernooij
Fix retrieval of proposals from gitlab.
622
        project = self._get_project(project)
7490.23.2 by Jelmer Vernooij
More gitlab fixes.
623
        mr = self._get_merge_request(project['path_with_namespace'], merge_id)
624
        return GitLabMergeProposal(self, mr)
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
625
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
626
    def delete_project(self, project):
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
627
        path = 'projects/%s' % urlutils.quote(str(project), '')
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
628
        response = self._api_request('DELETE', path)
629
        if response.status == 404:
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
630
            raise NoSuchProject(project)
631
        if response.status != 202:
7490.90.6 by Jelmer Vernooij
More fixes for hg probing.
632
            raise errors.UnexpectedHttpStatus(path, response.status)
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
633
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
634
0.432.2 by Jelmer Vernooij
Publish command sort of works.
635
class GitlabMergeProposalBuilder(MergeProposalBuilder):
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
636
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
637
    def __init__(self, gl, source_branch, target_branch):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
638
        self.gl = gl
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
639
        self.source_branch = source_branch
640
        (self.source_host, self.source_project_name, self.source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
641
            parse_gitlab_branch_url(source_branch))
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
642
        self.target_branch = target_branch
643
        (self.target_host, self.target_project_name, self.target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
644
            parse_gitlab_branch_url(target_branch))
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
645
        if self.source_host != self.target_host:
646
            raise DifferentGitLabInstances(self.source_host, self.target_host)
647
648
    def get_infotext(self):
649
        """Determine the initial comment for the merge proposal."""
650
        info = []
651
        info.append("Gitlab instance: %s\n" % self.target_host)
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
652
        info.append("Source: %s\n" % self.source_branch.user_url)
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
653
        info.append("Target: %s\n" % self.target_branch.user_url)
654
        return ''.join(info)
655
656
    def get_initial_body(self):
657
        """Get a body for the proposal for the user to modify.
658
659
        :return: a str or None.
660
        """
661
        return None
662
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
663
    def create_proposal(self, description, reviewers=None, labels=None,
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
664
                        prerequisite_branch=None, commit_message=None,
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
665
                        work_in_progress=False, allow_collaboration=False):
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
666
        """Perform the submission."""
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
667
        # https://docs.gitlab.com/ee/api/merge_requests.html#create-mr
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
668
        if prerequisite_branch is not None:
669
            raise PrerequisiteBranchUnsupported(self)
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
670
        # Note that commit_message is ignored, since Gitlab doesn't support it.
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
671
        source_project = self.gl._get_project(self.source_project_name)
672
        target_project = self.gl._get_project(self.target_project_name)
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
673
        # TODO(jelmer): Allow setting title explicitly
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
674
        title = determine_title(description)
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
675
        if work_in_progress:
676
            title = 'WIP: %s' % title
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
677
        # TODO(jelmer): Allow setting milestone field
678
        # TODO(jelmer): Allow setting squash field
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
679
        kwargs = {
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
680
            'title': title,
7296.10.3 by Jelmer Vernooij
More fixes.
681
            'source_project_id': source_project['id'],
682
            'target_project_id': target_project['id'],
7371.4.4 by Jelmer Vernooij
Pull in more fixes from janitor.
683
            'source_branch_name': self.source_branch_name,
684
            'target_branch_name': self.target_branch_name,
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
685
            'description': description,
686
            'allow_collaboration': allow_collaboration}
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
687
        if labels:
688
            kwargs['labels'] = ','.join(labels)
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
689
        if reviewers:
690
            kwargs['assignee_ids'] = []
691
            for reviewer in reviewers:
692
                if '@' in reviewer:
693
                    user = self.gl._get_user_by_email(reviewer)
694
                else:
695
                    user = self.gl._get_user(reviewer)
696
                kwargs['assignee_ids'].append(user['id'])
7490.10.1 by Jelmer Vernooij
Fix handling of 409s for gitlab.
697
        try:
698
            merge_request = self.gl._create_mergerequest(**kwargs)
699
        except MergeRequestExists:
7490.25.1 by Jelmer Vernooij
Fix raising of ProposalExists.
700
            raise MergeProposalExists(self.source_branch.user_url)
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
701
        return GitLabMergeProposal(self.gl, merge_request)
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
702
703
704
def register_gitlab_instance(shortname, url):
705
    """Register a gitlab instance.
706
707
    :param shortname: Short name (e.g. "gitlab")
708
    :param url: URL to the gitlab instance
709
    """
710
    from breezy.bugtracker import (
711
        tracker_registry,
712
        ProjectIntegerBugTracker,
713
        )
714
    tracker_registry.register(
715
        shortname, ProjectIntegerBugTracker(
716
            shortname, url + '/{project}/issues/{id}'))