/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
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
21
from ... import (
0.431.33 by Jelmer Vernooij
Fix URLs from gitlab.
22
    branch as _mod_branch,
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
23
    controldir,
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
24
    errors,
25
    urlutils,
26
    )
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
27
from ...git.urls import git_url_to_bzr_url
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
28
from ...sixish import PY3
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
29
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
30
from .propose import (
0.432.2 by Jelmer Vernooij
Publish command sort of works.
31
    Hoster,
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
32
    MergeProposal,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
33
    MergeProposalBuilder,
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
34
    MergeProposalExists,
0.431.38 by Jelmer Vernooij
Add NoSuchProject.
35
    NoSuchProject,
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
36
    PrerequisiteBranchUnsupported,
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
37
    UnsupportedHoster,
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
38
    )
39
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
40
def mp_status_to_status(status):
41
    return {
42
        'all': 'all',
43
        'open': 'opened',
44
        'merged': 'merged',
45
        'closed': 'closed'}[status]
46
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
47
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
48
class NotGitLabUrl(errors.BzrError):
49
50
    _fmt = "Not a GitLab URL: %(url)s"
51
52
    def __init__(self, url):
53
        errors.BzrError.__init__(self)
54
        self.url = url
55
56
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
57
class NotMergeRequestUrl(errors.BzrError):
58
59
    _fmt = "Not a merge proposal URL: %(url)s"
60
61
    def __init__(self, host, url):
62
        errors.BzrError.__init__(self)
63
        self.host = host
64
        self.url = url
65
66
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
67
class DifferentGitLabInstances(errors.BzrError):
68
69
    _fmt = ("Can't create merge proposals across GitLab instances: "
70
            "%(source_host)s and %(target_host)s")
71
72
    def __init__(self, source_host, target_host):
73
        self.source_host = source_host
74
        self.target_host = target_host
75
76
0.432.10 by Jelmer Vernooij
More test fixes.
77
class GitLabLoginMissing(errors.BzrError):
78
79
    _fmt = ("Please log into GitLab")
80
81
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
82
def default_config_path():
83
    from breezy.config import config_dir
84
    import os
85
    return os.path.join(config_dir(), 'gitlab.conf')
86
87
88
def store_gitlab_token(name, url, private_token):
89
    """Store a GitLab token in a configuration file."""
90
    import configparser
91
    config = configparser.ConfigParser()
92
    path = default_config_path()
93
    config.read([path])
94
    config.add_section(name)
95
    config[name]['url'] = url
96
    config[name]['private_token'] = private_token
97
    with open(path, 'w') as f:
98
        config.write(f)
99
100
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
101
def iter_tokens():
102
    import configparser
103
    from gitlab.config import _DEFAULT_FILES
104
    config = configparser.ConfigParser()
105
    config.read(_DEFAULT_FILES + [default_config_path()])
106
    for name, section in config.items():
107
        yield name, section
108
109
0.432.10 by Jelmer Vernooij
More test fixes.
110
def connect_gitlab(host):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
111
    from gitlab import Gitlab, GitlabGetError
0.432.10 by Jelmer Vernooij
More test fixes.
112
    url = 'https://%s' % host
7268.3.1 by Jelmer Vernooij
Drop support for getting gitlab authentication from authentication.conf.
113
    for name, section in iter_tokens():
114
        if section.get('url') == url:
115
            return Gitlab(**section)
0.432.10 by Jelmer Vernooij
More test fixes.
116
    else:
7268.3.1 by Jelmer Vernooij
Drop support for getting gitlab authentication from authentication.conf.
117
        try:
118
            return Gitlab(url)
119
        except GitlabGetError:
120
            raise GitLabLoginMissing()
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
121
122
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
123
def parse_gitlab_url(url):
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
124
    (scheme, user, password, host, port, path) = urlutils.parse_url(
125
        url)
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
126
    if scheme not in ('git+ssh', 'https', 'http'):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
127
        raise NotGitLabUrl(url)
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
128
    if not host:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
129
        raise NotGitLabUrl(url)
0.432.10 by Jelmer Vernooij
More test fixes.
130
    path = path.strip('/')
0.432.11 by Jelmer Vernooij
Fix some tests.
131
    if path.endswith('.git'):
132
        path = path[:-4]
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
133
    return host, path
134
135
136
def parse_gitlab_branch_url(branch):
137
    url = urlutils.split_segment_parameters(branch.user_url)[0]
138
    host, path = parse_gitlab_url(url)
0.432.10 by Jelmer Vernooij
More test fixes.
139
    return host, path, branch.name
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
140
141
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
142
def parse_gitlab_merge_request_url(url):
143
    (scheme, user, password, host, port, path) = urlutils.parse_url(
144
        url)
145
    if scheme not in ('git+ssh', 'https', 'http'):
146
        raise NotGitLabUrl(url)
147
    if not host:
148
        raise NotGitLabUrl(url)
149
    path = path.strip('/')
150
    parts = path.split('/')
151
    if parts[-2] != 'merge_requests':
152
        raise NotMergeRequestUrl(host, url)
153
    return host, '/'.join(parts[:-2]), int(parts[-1])
154
155
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
156
class GitLabMergeProposal(MergeProposal):
157
158
    def __init__(self, mr):
159
        self._mr = mr
160
161
    @property
162
    def url(self):
163
        return self._mr.web_url
164
165
    def get_description(self):
166
        return self._mr.description
167
168
    def set_description(self, description):
169
        self._mr.description = description
7265.1.1 by Jelmer Vernooij
Fix setting of merge proposal descriptions on gitlab.
170
        self._mr.save()
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
171
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
172
    def get_commit_message(self):
173
        return None
174
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
175
    def _branch_url_from_project(self, project_id, branch_name):
176
        project = self._mr.manager.gitlab.projects.get(project_id)
177
        return gitlab_url_to_bzr_url(project.http_url_to_repo, branch_name)
178
179
    def get_source_branch_url(self):
180
        return self._branch_url_from_project(
181
            self._mr.source_project_id, self._mr.source_branch)
182
183
    def get_target_branch_url(self):
184
        return self._branch_url_from_project(
185
            self._mr.target_project_id, self._mr.target_branch)
186
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
187
    def is_merged(self):
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
188
        return (self._mr.state == 'merged')
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
189
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
190
    def close(self):
191
        self._mr.state_event = 'close'
192
        self._mr.save()
193
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
194
    def merge(self, commit_message=None):
195
        # https://docs.gitlab.com/ee/api/merge_requests.html#accept-mr
196
        self._mr.merge(merge_commit_message=commit_message)
197
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
198
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
199
def gitlab_url_to_bzr_url(url, name):
200
    if not PY3:
201
        name = name.encode('utf-8')
202
    return urlutils.join_segment_parameters(
7211.13.7 by Jelmer Vernooij
Fix formatting.
203
        git_url_to_bzr_url(url), {"branch": name})
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
204
205
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
206
class GitLab(Hoster):
207
    """GitLab hoster implementation."""
208
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
209
    supports_merge_proposal_labels = True
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
210
    supports_merge_proposal_commit_message = False
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
211
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
212
    def __repr__(self):
213
        return "<GitLab(%r)>" % self.gl.url
214
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
215
    @property
216
    def base_url(self):
217
        return self.gl.url
218
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
219
    def __init__(self, gl):
220
        self.gl = gl
221
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
222
    def get_push_url(self, branch):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
223
        (host, project_name, branch_name) = parse_gitlab_branch_url(branch)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
224
        project = self.gl.projects.get(project_name)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
225
        return gitlab_url_to_bzr_url(
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
226
            project.ssh_url_to_repo, branch_name)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
227
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
228
    def publish_derived(self, local_branch, base_branch, name, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
229
                        owner=None, revision_id=None, overwrite=False,
230
                        allow_lossy=True):
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
231
        import gitlab
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
232
        (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch)
0.432.10 by Jelmer Vernooij
More test fixes.
233
        self.gl.auth()
0.431.38 by Jelmer Vernooij
Add NoSuchProject.
234
        try:
235
            base_project = self.gl.projects.get(base_project)
236
        except gitlab.GitlabGetError as e:
237
            if e.response_code == 404:
238
                raise NoSuchProject(base_project)
239
            else:
240
                raise
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
241
        if owner is None:
0.432.10 by Jelmer Vernooij
More test fixes.
242
            owner = self.gl.user.username
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
243
        if project is None:
0.431.30 by Jelmer Vernooij
s/name/path.
244
            project = base_project.path
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
245
        try:
0.432.10 by Jelmer Vernooij
More test fixes.
246
            target_project = self.gl.projects.get('%s/%s' % (owner, project))
0.431.30 by Jelmer Vernooij
s/name/path.
247
        except gitlab.GitlabGetError as e:
248
            if e.response_code == 404:
249
                target_project = base_project.forks.create({})
250
            else:
251
                raise
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
252
        remote_repo_url = git_url_to_bzr_url(target_project.ssh_url_to_repo)
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
253
        remote_dir = controldir.ControlDir.open(remote_repo_url)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
254
        try:
7211.13.7 by Jelmer Vernooij
Fix formatting.
255
            push_result = remote_dir.push_branch(
256
                local_branch, revision_id=revision_id, overwrite=overwrite,
257
                name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
258
        except errors.NoRoundtrippingSupport:
259
            if not allow_lossy:
260
                raise
7211.13.7 by Jelmer Vernooij
Fix formatting.
261
            push_result = remote_dir.push_branch(
262
                local_branch, revision_id=revision_id, overwrite=overwrite,
263
                name=name, lossy=True)
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
264
        public_url = gitlab_url_to_bzr_url(
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
265
            target_project.http_url_to_repo, name)
0.432.5 by Jelmer Vernooij
Fix publishing to gitlab.
266
        return push_result.target_branch, public_url
0.432.4 by Jelmer Vernooij
Some work on gitlab.
267
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
268
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
269
        import gitlab
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
270
        (host, base_project, base_branch_name) = parse_gitlab_branch_url(base_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
271
        self.gl.auth()
0.431.38 by Jelmer Vernooij
Add NoSuchProject.
272
        try:
273
            base_project = self.gl.projects.get(base_project)
274
        except gitlab.GitlabGetError as e:
275
            if e.response_code == 404:
276
                raise NoSuchProject(base_project)
277
            else:
278
                raise
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
279
        if owner is None:
280
            owner = self.gl.user.username
281
        if project is None:
0.431.30 by Jelmer Vernooij
s/name/path.
282
            project = base_project.path
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
283
        try:
284
            target_project = self.gl.projects.get('%s/%s' % (owner, project))
285
        except gitlab.GitlabGetError as e:
286
            if e.response_code == 404:
287
                raise errors.NotBranchError('%s/%s/%s' % (self.gl.url, owner, project))
288
            raise
0.433.3 by Jelmer Vernooij
Some python 3 compatibility.
289
        return _mod_branch.Branch.open(gitlab_url_to_bzr_url(
7233.3.2 by Jelmer Vernooij
Merge lp:brz-propose.
290
            target_project.ssh_url_to_repo, name))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
291
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
292
    def get_proposer(self, source_branch, target_branch):
293
        return GitlabMergeProposalBuilder(self.gl, source_branch, target_branch)
294
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
295
    def iter_proposals(self, source_branch, target_branch, status):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
296
        import gitlab
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
297
        (source_host, source_project_name, source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
298
            parse_gitlab_branch_url(source_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
299
        (target_host, target_project_name, target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
300
            parse_gitlab_branch_url(target_branch))
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
301
        if source_host != target_host:
302
            raise DifferentGitLabInstances(source_host, target_host)
303
        self.gl.auth()
304
        source_project = self.gl.projects.get(source_project_name)
305
        target_project = self.gl.projects.get(target_project_name)
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
306
        state = mp_status_to_status(status)
0.431.43 by Jelmer Vernooij
Handle 403s during proposal listing.
307
        try:
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
308
            for mr in target_project.mergerequests.list(state=state):
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
309
                if (mr.source_project_id != source_project.id or
7233.3.2 by Jelmer Vernooij
Merge lp:brz-propose.
310
                        mr.source_branch != source_branch_name or
311
                        mr.target_project_id != target_project.id or
312
                        mr.target_branch != target_branch_name):
0.431.43 by Jelmer Vernooij
Handle 403s during proposal listing.
313
                    continue
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
314
                yield GitLabMergeProposal(mr)
0.431.43 by Jelmer Vernooij
Handle 403s during proposal listing.
315
        except gitlab.GitlabListError as e:
316
            if e.response_code == 403:
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
317
                raise errors.PermissionDenied(e.error_message)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
318
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
319
    def hosts(self, branch):
320
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
321
            (host, project, branch_name) = parse_gitlab_branch_url(branch)
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
322
        except NotGitLabUrl:
323
            return False
324
        return (self.gl.url == ('https://%s' % host))
325
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
326
    @classmethod
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
327
    def probe_from_url(cls, url):
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
328
        try:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
329
            (host, project) = parse_gitlab_url(url)
0.431.17 by Jelmer Vernooij
Try harder to avoid detecting any URL as a GitLab URL.
330
        except NotGitLabUrl:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
331
            raise UnsupportedHoster(url)
0.432.4 by Jelmer Vernooij
Some work on gitlab.
332
        import gitlab
0.431.43 by Jelmer Vernooij
Handle 403s during proposal listing.
333
        import requests.exceptions
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
334
        try:
0.432.4 by Jelmer Vernooij
Some work on gitlab.
335
            gl = connect_gitlab(host)
0.431.10 by Jelmer Vernooij
Various other fixes.
336
            gl.auth()
0.431.43 by Jelmer Vernooij
Handle 403s during proposal listing.
337
        except requests.exceptions.SSLError:
7211.13.7 by Jelmer Vernooij
Fix formatting.
338
            # Well, I guess it could be..
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
339
            raise UnsupportedHoster(url)
0.432.4 by Jelmer Vernooij
Some work on gitlab.
340
        except gitlab.GitlabGetError:
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
341
            raise UnsupportedHoster(url)
0.431.10 by Jelmer Vernooij
Various other fixes.
342
        except gitlab.GitlabHttpError as e:
0.431.27 by Jelmer Vernooij
Catch 503 errors.
343
            if e.response_code in (404, 405, 503):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
344
                raise UnsupportedHoster(url)
0.431.10 by Jelmer Vernooij
Various other fixes.
345
            else:
346
                raise
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
347
        return cls(gl)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
348
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
349
    @classmethod
350
    def iter_instances(cls):
351
        from gitlab import Gitlab
352
        for name, credentials in iter_tokens():
353
            if 'url' not in credentials:
354
                continue
355
            gl = Gitlab(**credentials)
356
            yield cls(gl)
357
0.431.66 by Jelmer Vernooij
Add support for status argument.
358
    def iter_my_proposals(self, status='open'):
0.431.68 by Jelmer Vernooij
Add status to other Hosters.
359
        state = mp_status_to_status(status)
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
360
        self.gl.auth()
0.431.66 by Jelmer Vernooij
Add support for status argument.
361
        for mp in self.gl.mergerequests.list(
362
                owner=self.gl.user.username, state=state):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
363
            yield GitLabMergeProposal(mp)
364
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
365
    def get_proposal_by_url(self, url):
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
366
        try:
367
            (host, project, merge_id) = parse_gitlab_merge_request_url(url)
368
        except NotGitLabUrl:
369
            raise UnsupportedHoster(url)
7296.9.4 by Jelmer Vernooij
Fix dealing with non-gitlab sites.
370
        except NotMergeRequestUrl as e:
371
            if self.gl.url == ('https://%s' % e.host):
7296.9.3 by Jelmer Vernooij
Support finding merge proposals by URL on GitLab instances.
372
                raise
373
            else:
374
                raise UnsupportedHoster(url)
375
        if self.gl.url != ('https://%s' % host):
376
            raise UnsupportedHoster(url)
377
        project = self.gl.projects.get(project)
378
        mr = project.mergerequests.get(merge_id)
379
        return GitLabMergeProposal(mr)
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
380
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
381
0.432.2 by Jelmer Vernooij
Publish command sort of works.
382
class GitlabMergeProposalBuilder(MergeProposalBuilder):
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
383
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
384
    def __init__(self, gl, source_branch, target_branch):
385
        self.gl = gl
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
386
        self.source_branch = source_branch
387
        (self.source_host, self.source_project_name, self.source_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
388
            parse_gitlab_branch_url(source_branch))
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
389
        self.target_branch = target_branch
390
        (self.target_host, self.target_project_name, self.target_branch_name) = (
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
391
            parse_gitlab_branch_url(target_branch))
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
392
        if self.source_host != self.target_host:
393
            raise DifferentGitLabInstances(self.source_host, self.target_host)
394
395
    def get_infotext(self):
396
        """Determine the initial comment for the merge proposal."""
397
        info = []
398
        info.append("Gitlab instance: %s\n" % self.target_host)
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
399
        info.append("Source: %s\n" % self.source_branch.user_url)
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
400
        info.append("Target: %s\n" % self.target_branch.user_url)
401
        return ''.join(info)
402
403
    def get_initial_body(self):
404
        """Get a body for the proposal for the user to modify.
405
406
        :return: a str or None.
407
        """
408
        return None
409
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
410
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
411
                        prerequisite_branch=None, commit_message=None):
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
412
        """Perform the submission."""
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
413
        # https://docs.gitlab.com/ee/api/merge_requests.html#create-mr
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
414
        if prerequisite_branch is not None:
415
            raise PrerequisiteBranchUnsupported(self)
0.431.16 by Jelmer Vernooij
gitlab: Report when a merge proposal already exists.
416
        import gitlab
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
417
        # Note that commit_message is ignored, since Gitlab doesn't support it.
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
418
        # TODO(jelmer): Support reviewers
0.432.10 by Jelmer Vernooij
More test fixes.
419
        self.gl.auth()
420
        source_project = self.gl.projects.get(self.source_project_name)
421
        target_project = self.gl.projects.get(self.target_project_name)
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
422
        # TODO(jelmer): Allow setting title explicitly
423
        title = description.splitlines()[0]
424
        # TODO(jelmer): Allow setting allow_collaboration field
425
        # TODO(jelmer): Allow setting milestone field
426
        # TODO(jelmer): Allow setting squash field
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
427
        kwargs = {
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
428
            'title': title,
429
            'target_project_id': target_project.id,
430
            'source_branch': self.source_branch_name,
431
            'target_branch': self.target_branch_name,
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
432
            'description': description}
433
        if labels:
434
            kwargs['labels'] = ','.join(labels)
0.431.16 by Jelmer Vernooij
gitlab: Report when a merge proposal already exists.
435
        try:
436
            merge_request = source_project.mergerequests.create(kwargs)
437
        except gitlab.GitlabCreateError as e:
0.431.34 by Jelmer Vernooij
Cope with gitlab 403.
438
            if e.response_code == 403:
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
439
                raise errors.PermissionDenied(e.error_message)
0.431.16 by Jelmer Vernooij
gitlab: Report when a merge proposal already exists.
440
            if e.response_code == 409:
441
                raise MergeProposalExists(self.source_branch.user_url)
442
            raise
0.431.39 by Jelmer Vernooij
Extend the merge proposal abstraction a bit.
443
        return GitLabMergeProposal(merge_request)
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
444
445
446
def register_gitlab_instance(shortname, url):
447
    """Register a gitlab instance.
448
449
    :param shortname: Short name (e.g. "gitlab")
450
    :param url: URL to the gitlab instance
451
    """
452
    from breezy.bugtracker import (
453
        tracker_registry,
454
        ProjectIntegerBugTracker,
455
        )
456
    tracker_registry.register(
457
        shortname, ProjectIntegerBugTracker(
458
            shortname, url + '/{project}/issues/{id}'))