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