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