/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
1
# Copyright (C) 2010, 2011 Canonical Ltd
0.431.27 by Jelmer Vernooij
Catch 503 errors.
2
# Copyright (C) 2018 Breezy Developers
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
0.434.1 by Jelmer Vernooij
Use absolute_import.
18
"""Support for Launchpad."""
19
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
20
from __future__ import absolute_import
21
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
22
import re
23
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
24
from .propose import (
0.432.2 by Jelmer Vernooij
Publish command sort of works.
25
    Hoster,
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
26
    LabelsUnsupported,
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
27
    MergeProposal,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
28
    MergeProposalBuilder,
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
29
    MergeProposalExists,
0.431.12 by Jelmer Vernooij
Fix Launchpad probing.
30
    UnsupportedHoster,
0.431.3 by Jelmer Vernooij
Add a MergeProposal object.
31
    )
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
32
33
from ... import (
0.432.2 by Jelmer Vernooij
Publish command sort of works.
34
    branch as _mod_branch,
35
    controldir,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
36
    errors,
37
    hooks,
38
    urlutils,
39
    )
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
40
from ...git.refs import ref_to_branch_name
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
41
from ...lazy_import import lazy_import
42
lazy_import(globals(), """
43
from breezy.plugins.launchpad import (
44
    lp_api,
45
    )
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
46
47
from launchpadlib import uris
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
48
""")
0.432.2 by Jelmer Vernooij
Publish command sort of works.
49
from ...transport import get_transport
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
50
51
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
52
# TODO(jelmer): Make selection of launchpad staging a configuration option.
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
53
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
54
def status_to_lp_mp_statuses(status):
55
    statuses = []
56
    if status in ('open', 'all'):
57
        statuses.extend([
58
            'Work in progress',
59
            'Needs review',
60
            'Approved',
61
            'Code failed to merge',
62
            'Queued'])
63
    if status in ('closed', 'all'):
64
        statuses.extend(['Rejected', 'Superseded'])
65
    if status in ('merged', 'all'):
66
        statuses.append('Merged')
67
    return statuses
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
68
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
69
70
def plausible_launchpad_url(url):
71
    if url is None:
72
        return False
73
    if url.startswith('lp:'):
74
        return True
75
    regex = re.compile('([a-z]*\+)*(bzr\+ssh|http|ssh|git|https)'
76
                       '://(bazaar|git).*.launchpad.net')
77
    return bool(regex.match(url))
78
79
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
80
class WebserviceFailure(Exception):
81
82
    def __init__(self, message):
83
        self.message = message
84
85
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
86
def _call_webservice(call, *args, **kwargs):
87
    """Make a call to the webservice, wrapping failures.
88
89
    :param call: The call to make.
90
    :param *args: *args for the call.
91
    :param **kwargs: **kwargs for the call.
92
    :return: The result of calling call(*args, *kwargs).
93
    """
94
    from lazr.restfulclient import errors as restful_errors
95
    try:
96
        return call(*args, **kwargs)
97
    except restful_errors.HTTPError as e:
98
        error_lines = []
99
        for line in e.content.splitlines():
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
100
            if line.startswith(b'Traceback (most recent call last):'):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
101
                break
102
            error_lines.append(line)
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
103
        raise WebserviceFailure(b''.join(error_lines))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
104
105
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
106
class LaunchpadMergeProposal(MergeProposal):
107
108
    def __init__(self, mp):
109
        self._mp = mp
110
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
111
    def get_source_branch_url(self):
112
        if self._mp.source_branch:
113
            return self._mp.source_branch.bzr_identity
114
        else:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
115
            branch_name = ref_to_branch_name(
116
                self._mp.source_git_path.encode('utf-8'))
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
117
            return urlutils.join_segment_parameters(
118
                self._mp.source_git_repository.git_identity,
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
119
                {"branch": branch_name})
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
120
121
    def get_target_branch_url(self):
122
        if self._mp.target_branch:
123
            return self._mp.target_branch.bzr_identity
124
        else:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
125
            branch_name = ref_to_branch_name(
126
                self._mp.target_git_path.encode('utf-8'))
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
127
            return urlutils.join_segment_parameters(
128
                self._mp.target_git_repository.git_identity,
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
129
                {"branch": branch_name})
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
130
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
131
    @property
132
    def url(self):
133
        return lp_api.canonical_url(self._mp)
134
135
    def is_merged(self):
136
        return (self._mp.queue_status == 'Merged')
137
0.431.70 by Jelmer Vernooij
Implement get_description/set_description for LaunchpadMergeProposal.
138
    def get_description(self):
139
        return self._mp.description
140
141
    def set_description(self, description):
142
        self._mp.description = description
143
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
144
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
145
class Launchpad(Hoster):
146
    """The Launchpad hosting service."""
147
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
148
    name = 'launchpad'
149
0.431.29 by Jelmer Vernooij
Fix github branch name, add bug URL.
150
    # https://bugs.launchpad.net/launchpad/+bug/397676
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
151
    supports_merge_proposal_labels = False
152
7254.1.2 by Jelmer Vernooij
Fix propose Launchpad.
153
    def __init__(self, staging=False):
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
154
        self._staging = staging
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
155
        if staging:
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
156
            lp_base_url = uris.STAGING_SERVICE_ROOT
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
157
        else:
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
158
            lp_base_url = uris.LPNET_SERVICE_ROOT
7240.5.1 by Jelmer Vernooij
Avoid LaunchpadService when connecting to API.
159
        self.launchpad = lp_api.connect_launchpad(lp_base_url)
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
160
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
161
    @property
162
    def base_url(self):
163
        return lp_api.uris.web_root_for_service_root(
164
            str(self.launchpad._root_uri))
165
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
166
    def __repr__(self):
167
        return "Launchpad(staging=%s)" % self._staging
168
169
    def hosts(self, branch):
170
        # TODO(jelmer): staging vs non-staging?
171
        return plausible_launchpad_url(branch.user_url)
172
0.432.2 by Jelmer Vernooij
Publish command sort of works.
173
    @classmethod
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
174
    def probe(cls, branch):
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
175
        if plausible_launchpad_url(branch.user_url):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
176
            return Launchpad()
0.431.12 by Jelmer Vernooij
Fix Launchpad probing.
177
        raise UnsupportedHoster(branch)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
178
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
179
    def _get_lp_git_ref_from_branch(self, branch):
180
        url, params = urlutils.split_segment_parameters(branch.user_url)
181
        (scheme, user, password, host, port, path) = urlutils.parse_url(
182
            url)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
183
        repo_lp = self.launchpad.git_repositories.getByPath(
184
            path=path.strip('/'))
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
185
        try:
186
            ref_path = params['ref']
187
        except KeyError:
188
            branch_name = params.get('branch', branch.name)
189
            if branch_name:
190
                ref_path = 'refs/heads/%s' % branch_name
191
            else:
192
                ref_path = repo_lp.default_branch
193
        ref_lp = repo_lp.getRefByPath(path=ref_path)
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
194
        return (repo_lp, ref_lp)
195
196
    def _get_lp_bzr_branch_from_branch(self, branch):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
197
        return self.launchpad.branches.getByUrl(
198
            url=urlutils.unescape(branch.user_url))
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
199
0.431.23 by Jelmer Vernooij
Launchpad fixes.
200
    def _get_derived_git_path(self, base_path, owner, project):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
201
        base_repo = self.launchpad.git_repositories.getByPath(path=base_path)
202
        if project is None:
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
203
            project = urlutils.parse_url(base_repo.git_ssh_url)[-1].strip('/')
204
        if project.startswith('~'):
205
            project = '/'.join(base_path.split('/')[1:])
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
206
        # TODO(jelmer): Surely there is a better way of creating one of these
207
        # URLs?
0.431.23 by Jelmer Vernooij
Launchpad fixes.
208
        return "~%s/%s" % (owner, project)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
209
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
210
    def _publish_git(self, local_branch, base_path, name, owner, project=None,
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
211
                     revision_id=None, overwrite=False, allow_lossy=True):
0.431.23 by Jelmer Vernooij
Launchpad fixes.
212
        to_path = self._get_derived_git_path(base_path, owner, project)
213
        to_transport = get_transport("git+ssh://git.launchpad.net/" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
214
        try:
215
            dir_to = controldir.ControlDir.open_from_transport(to_transport)
216
        except errors.NotBranchError:
217
            # Didn't find anything
218
            dir_to = None
219
220
        if dir_to is None:
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
221
            try:
222
                br_to = local_branch.create_clone_on_transport(
7233.3.2 by Jelmer Vernooij
Merge lp:brz-propose.
223
                    to_transport, revision_id=revision_id, name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
224
            except errors.NoRoundtrippingSupport:
225
                br_to = local_branch.create_clone_on_transport(
7211.13.7 by Jelmer Vernooij
Fix formatting.
226
                    to_transport, revision_id=revision_id, name=name,
7233.3.2 by Jelmer Vernooij
Merge lp:brz-propose.
227
                    lossy=True)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
228
        else:
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
229
            try:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
230
                dir_to = dir_to.push_branch(
231
                    local_branch, revision_id, overwrite=overwrite, name=name)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
232
            except errors.NoRoundtrippingSupport:
233
                if not allow_lossy:
234
                    raise
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
235
                dir_to = dir_to.push_branch(
236
                    local_branch, revision_id, overwrite=overwrite, name=name,
237
                    lossy=True)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
238
            br_to = dir_to.target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
239
        return br_to, (
240
            "https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
241
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
242
    def _get_derived_bzr_path(self, base_branch, name, owner, project):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
243
        if project is None:
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
244
            base_branch_lp = self._get_lp_bzr_branch_from_branch(base_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
245
            project = '/'.join(base_branch_lp.unique_name.split('/')[1:-1])
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
246
        # TODO(jelmer): Surely there is a better way of creating one of these
247
        # URLs?
0.431.23 by Jelmer Vernooij
Launchpad fixes.
248
        return "~%s/%s/%s" % (owner, project, name)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
249
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
250
    def get_push_url(self, branch):
251
        (vcs, user, password, path, params) = self._split_url(branch.user_url)
252
        if vcs == 'bzr':
253
            branch_lp = self._get_lp_bzr_branch_from_branch(branch)
254
            return branch_lp.bzr_identity
255
        elif vcs == 'git':
256
            return urlutils.join_segment_parameters(
257
                "git+ssh://git.launchpad.net/" + path, params)
258
        else:
259
            raise AssertionError
260
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
261
    def _publish_bzr(self, local_branch, base_branch, name, owner,
262
                     project=None, revision_id=None, overwrite=False,
263
                     allow_lossy=True):
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
264
        to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
0.431.23 by Jelmer Vernooij
Launchpad fixes.
265
        to_transport = get_transport("lp:" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
266
        try:
267
            dir_to = controldir.ControlDir.open_from_transport(to_transport)
268
        except errors.NotBranchError:
269
            # Didn't find anything
270
            dir_to = None
271
272
        if dir_to is None:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
273
            br_to = local_branch.create_clone_on_transport(
274
                to_transport, revision_id=revision_id)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
275
        else:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
276
            br_to = dir_to.push_branch(
277
                local_branch, revision_id, overwrite=overwrite).target_branch
0.431.23 by Jelmer Vernooij
Launchpad fixes.
278
        return br_to, ("https://code.launchpad.net/" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
279
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
280
    def _split_url(self, url):
281
        url, params = urlutils.split_segment_parameters(url)
282
        (scheme, user, password, host, port, path) = urlutils.parse_url(url)
283
        path = path.strip('/')
284
        if host.startswith('bazaar.'):
285
            vcs = 'bzr'
286
        elif host.startswith('git.'):
287
            vcs = 'git'
288
        else:
289
            raise ValueError("unknown host %s" % host)
290
        return (vcs, user, password, path, params)
291
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
292
    def publish_derived(self, local_branch, base_branch, name, project=None,
293
                        owner=None, revision_id=None, overwrite=False,
294
                        allow_lossy=True):
0.432.2 by Jelmer Vernooij
Publish command sort of works.
295
        """Publish a branch to the site, derived from base_branch.
296
297
        :param base_branch: branch to derive the new branch from
298
        :param new_branch: branch to publish
299
        :param name: Name of the new branch on the remote host
300
        :param project: Optional project name
301
        :param owner: Optional owner
302
        :return: resulting branch
303
        """
304
        if owner is None:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
305
            owner = self.launchpad.me.name
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
306
        (base_vcs, base_user, base_password, base_path,
307
            base_params) = self._split_url(base_branch.user_url)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
308
        # TODO(jelmer): Prevent publishing to development focus
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
309
        if base_vcs == 'bzr':
7211.13.7 by Jelmer Vernooij
Fix formatting.
310
            return self._publish_bzr(
311
                local_branch, base_branch, name, project=project, owner=owner,
312
                revision_id=revision_id, overwrite=overwrite,
313
                allow_lossy=allow_lossy)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
314
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
315
            return self._publish_git(
316
                local_branch, base_path, name, project=project, owner=owner,
317
                revision_id=revision_id, overwrite=overwrite,
318
                allow_lossy=allow_lossy)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
319
        else:
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
320
            raise AssertionError('not a valid Launchpad URL')
0.432.2 by Jelmer Vernooij
Publish command sort of works.
321
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
322
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
323
        if owner is None:
324
            owner = self.launchpad.me.name
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
325
        (base_vcs, base_user, base_password, base_path,
326
            base_params) = self._split_url(base_branch.user_url)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
327
        if base_vcs == 'bzr':
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
328
            to_path = self._get_derived_bzr_path(
329
                base_branch, name, owner, project)
0.431.23 by Jelmer Vernooij
Launchpad fixes.
330
            return _mod_branch.Branch.open("lp:" + to_path)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
331
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
332
            to_path = self._get_derived_git_path(
333
                base_path.strip('/'), owner, project)
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
334
            to_url = urlutils.join_segment_parameters(
7240.4.1 by Jelmer Vernooij
Merge lp:brz-propose.
335
                "git+ssh://git.launchpad.net/" + to_path,
336
                {'branch': name})
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
337
            return _mod_branch.Branch.open(to_url)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
338
        else:
339
            raise AssertionError('not a valid Launchpad URL')
340
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
341
    def iter_proposals(self, source_branch, target_branch, status='open'):
342
        (base_vcs, base_user, base_password, base_path,
343
            base_params) = self._split_url(target_branch.user_url)
344
        statuses = status_to_lp_mp_statuses(status)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
345
        if base_vcs == 'bzr':
7211.13.7 by Jelmer Vernooij
Fix formatting.
346
            target_branch_lp = self.launchpad.branches.getByUrl(
347
                url=target_branch.user_url)
348
            source_branch_lp = self.launchpad.branches.getByUrl(
349
                url=source_branch.user_url)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
350
            for mp in target_branch_lp.getMergeProposals(status=statuses):
351
                if mp.source_branch_link != source_branch_lp.self_link:
352
                    continue
353
                yield LaunchpadMergeProposal(mp)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
354
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
355
            (source_repo_lp, source_branch_lp) = (
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
356
                self._get_lp_git_ref_from_branch(source_branch))
7211.13.7 by Jelmer Vernooij
Fix formatting.
357
            (target_repo_lp, target_branch_lp) = (
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
358
                self._get_lp_git_ref_from_branch(target_branch))
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
359
            for mp in target_branch_lp.getMergeProposals(status=statuses):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
360
                if (target_branch_lp.path != mp.target_git_path or
7211.13.7 by Jelmer Vernooij
Fix formatting.
361
                        target_repo_lp != mp.target_git_repository or
362
                        source_branch_lp.path != mp.source_git_path or
363
                        source_repo_lp != mp.source_git_repository):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
364
                    continue
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
365
                yield LaunchpadMergeProposal(mp)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
366
        else:
367
            raise AssertionError('not a valid Launchpad URL')
368
0.432.2 by Jelmer Vernooij
Publish command sort of works.
369
    def get_proposer(self, source_branch, target_branch):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
370
        (base_vcs, base_user, base_password, base_path,
371
            base_params) = self._split_url(target_branch.user_url)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
372
        if base_vcs == 'bzr':
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
373
            return LaunchpadBazaarMergeProposalBuilder(
7211.13.7 by Jelmer Vernooij
Fix formatting.
374
                self, source_branch, target_branch)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
375
        elif base_vcs == 'git':
0.431.24 by Jelmer Vernooij
Support git merge proposals.
376
            return LaunchpadGitMergeProposalBuilder(
7211.13.7 by Jelmer Vernooij
Fix formatting.
377
                self, source_branch, target_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
378
        else:
379
            raise AssertionError('not a valid Launchpad URL')
0.432.2 by Jelmer Vernooij
Publish command sort of works.
380
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
381
    @classmethod
382
    def iter_instances(cls):
383
        yield cls()
384
0.431.66 by Jelmer Vernooij
Add support for status argument.
385
    def iter_my_proposals(self, status='open'):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
386
        statuses = status_to_lp_mp_statuses(status)
0.431.66 by Jelmer Vernooij
Add support for status argument.
387
        for mp in self.launchpad.me.getMergeProposals(status=statuses):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
388
            yield LaunchpadMergeProposal(mp)
389
0.432.2 by Jelmer Vernooij
Publish command sort of works.
390
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
391
class LaunchpadBazaarMergeProposalBuilder(MergeProposalBuilder):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
392
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
393
    def __init__(self, lp_host, source_branch, target_branch, message=None,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
394
                 staging=None, approve=None, fixes=None):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
395
        """Constructor.
396
397
        :param source_branch: The branch to propose for merging.
398
        :param target_branch: The branch to merge into.
399
        :param message: The commit message to use.  (May be None.)
400
        :param staging: If True, propose the merge against staging instead of
401
            production.
402
        :param approve: If True, mark the new proposal as approved immediately.
403
            This is useful when a project permits some things to be approved
404
            by the submitter (e.g. merges between release and deployment
405
            branches).
406
        """
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
407
        self.lp_host = lp_host
408
        self.launchpad = lp_host.launchpad
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
409
        self.source_branch = source_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
410
        self.source_branch_lp = self.launchpad.branches.getByUrl(
411
            url=source_branch.user_url)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
412
        if target_branch is None:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
413
            self.target_branch_lp = self.source_branch_lp.get_target()
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
414
            self.target_branch = _mod_branch.Branch.open(
415
                self.target_branch_lp.bzr_identity)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
416
        else:
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
417
            self.target_branch = target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
418
            self.target_branch_lp = self.launchpad.branches.getByUrl(
419
                url=target_branch.user_url)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
420
        self.commit_message = message
421
        self.approve = approve
422
        self.fixes = fixes
423
424
    def get_infotext(self):
425
        """Determine the initial comment for the merge proposal."""
426
        if self.commit_message is not None:
427
            return self.commit_message.strip().encode('utf-8')
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
428
        info = ["Source: %s\n" % self.source_branch_lp.bzr_identity]
429
        info.append("Target: %s\n" % self.target_branch_lp.bzr_identity)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
430
        return ''.join(info)
431
432
    def get_initial_body(self):
433
        """Get a body for the proposal for the user to modify.
434
435
        :return: a str or None.
436
        """
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
437
        if not self.hooks['merge_proposal_body']:
438
            return None
439
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
440
        def list_modified_files():
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
441
            lca_tree = self.source_branch_lp.find_lca_tree(
442
                self.target_branch_lp)
443
            source_tree = self.source_branch.basis_tree()
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
444
            files = modified_files(lca_tree, source_tree)
445
            return list(files)
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
446
        with self.target_branch.lock_read(), \
447
                self.source_branch.lock_read():
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
448
            body = None
449
            for hook in self.hooks['merge_proposal_body']:
450
                body = hook({
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
451
                    'target_branch': self.target_branch_lp.bzr_identity,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
452
                    'modified_files_callback': list_modified_files,
453
                    'old_body': body,
454
                })
455
            return body
456
457
    def check_proposal(self):
458
        """Check that the submission is sensible."""
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
459
        if self.source_branch_lp.self_link == self.target_branch_lp.self_link:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
460
            raise errors.BzrCommandError(
461
                'Source and target branches must be different.')
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
462
        for mp in self.source_branch_lp.landing_targets:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
463
            if mp.queue_status in ('Merged', 'Rejected'):
464
                continue
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
465
            if mp.target_branch.self_link == self.target_branch_lp.self_link:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
466
                raise MergeProposalExists(lp_api.canonical_url(mp))
467
468
    def approve_proposal(self, mp):
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
469
        with self.source_branch.lock_read():
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
470
            _call_webservice(
471
                mp.createComment,
472
                vote=u'Approve',
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
473
                subject='',  # Use the default subject.
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
474
                content=u"Rubberstamp! Proposer approves of own proposal.")
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
475
            _call_webservice(mp.setStatus, status=u'Approved',
476
                             revid=self.source_branch.last_revision())
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
477
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
478
    def create_proposal(self, description, reviewers=None, labels=None,
479
                        prerequisite_branch=None):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
480
        """Perform the submission."""
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
481
        if labels:
482
            raise LabelsUnsupported()
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
483
        if prerequisite_branch is not None:
484
            prereq = self.launchpad.branches.getByUrl(
485
                url=prerequisite_branch.user_url)
486
        else:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
487
            prereq = None
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
488
        if reviewers is None:
489
            reviewers = []
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
490
        try:
491
            mp = _call_webservice(
492
                self.source_branch_lp.createMergeProposal,
493
                target_branch=self.target_branch_lp,
494
                prerequisite_branch=prereq,
0.431.42 by Jelmer Vernooij
Remove unnecessary encode (breaks on Python 3).
495
                initial_comment=description.strip(),
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
496
                commit_message=self.commit_message,
497
                reviewers=[self.launchpad.people[reviewer].self_link
498
                           for reviewer in reviewers],
499
                review_types=[None for reviewer in reviewers])
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
500
        except WebserviceFailure as e:
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
501
            # Urgh.
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
502
            if (b'There is already a branch merge proposal '
7211.13.7 by Jelmer Vernooij
Fix formatting.
503
                    b'registered for branch ') in e.message:
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
504
                raise MergeProposalExists(self.source_branch.user_url)
505
            raise
506
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
507
        if self.approve:
508
            self.approve_proposal(mp)
509
        if self.fixes:
510
            if self.fixes.startswith('lp:'):
511
                self.fixes = self.fixes[3:]
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
512
            _call_webservice(
513
                mp.linkBug,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
514
                bug=self.launchpad.bugs[int(self.fixes)])
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
515
        return LaunchpadMergeProposal(mp)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
516
517
518
class LaunchpadGitMergeProposalBuilder(MergeProposalBuilder):
519
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
520
    def __init__(self, lp_host, source_branch, target_branch, message=None,
0.431.24 by Jelmer Vernooij
Support git merge proposals.
521
                 staging=None, approve=None, fixes=None):
522
        """Constructor.
523
524
        :param source_branch: The branch to propose for merging.
525
        :param target_branch: The branch to merge into.
526
        :param message: The commit message to use.  (May be None.)
527
        :param staging: If True, propose the merge against staging instead of
528
            production.
529
        :param approve: If True, mark the new proposal as approved immediately.
530
            This is useful when a project permits some things to be approved
531
            by the submitter (e.g. merges between release and deployment
532
            branches).
533
        """
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
534
        self.lp_host = lp_host
535
        self.launchpad = lp_host.launchpad
0.431.24 by Jelmer Vernooij
Support git merge proposals.
536
        self.source_branch = source_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
537
        (self.source_repo_lp,
538
            self.source_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(
539
                source_branch)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
540
        if target_branch is None:
541
            self.target_branch_lp = self.source_branch.get_target()
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
542
            self.target_branch = _mod_branch.Branch.open(
543
                self.target_branch_lp.git_https_url)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
544
        else:
545
            self.target_branch = target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
546
            (self.target_repo_lp, self.target_branch_lp) = (
547
                self.lp_host._get_lp_git_ref_from_branch(target_branch))
0.431.24 by Jelmer Vernooij
Support git merge proposals.
548
        self.commit_message = message
549
        self.approve = approve
550
        self.fixes = fixes
551
552
    def get_infotext(self):
553
        """Determine the initial comment for the merge proposal."""
554
        if self.commit_message is not None:
555
            return self.commit_message.strip().encode('utf-8')
556
        info = ["Source: %s\n" % self.source_branch.user_url]
557
        info.append("Target: %s\n" % self.target_branch.user_url)
558
        return ''.join(info)
559
560
    def get_initial_body(self):
561
        """Get a body for the proposal for the user to modify.
562
563
        :return: a str or None.
564
        """
565
        if not self.hooks['merge_proposal_body']:
566
            return None
567
568
        def list_modified_files():
569
            lca_tree = self.source_branch_lp.find_lca_tree(
570
                self.target_branch_lp)
571
            source_tree = self.source_branch.basis_tree()
572
            files = modified_files(lca_tree, source_tree)
573
            return list(files)
574
        with self.target_branch.lock_read(), \
575
                self.source_branch.lock_read():
576
            body = None
577
            for hook in self.hooks['merge_proposal_body']:
578
                body = hook({
579
                    'target_branch': self.target_branch,
580
                    'modified_files_callback': list_modified_files,
581
                    'old_body': body,
582
                })
583
            return body
584
585
    def check_proposal(self):
586
        """Check that the submission is sensible."""
587
        if self.source_branch_lp.self_link == self.target_branch_lp.self_link:
588
            raise errors.BzrCommandError(
589
                'Source and target branches must be different.')
590
        for mp in self.source_branch_lp.landing_targets:
591
            if mp.queue_status in ('Merged', 'Rejected'):
592
                continue
593
            if mp.target_branch.self_link == self.target_branch_lp.self_link:
594
                raise MergeProposalExists(lp_api.canonical_url(mp))
595
596
    def approve_proposal(self, mp):
597
        with self.source_branch.lock_read():
598
            _call_webservice(
599
                mp.createComment,
600
                vote=u'Approve',
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
601
                subject='',  # Use the default subject.
0.431.24 by Jelmer Vernooij
Support git merge proposals.
602
                content=u"Rubberstamp! Proposer approves of own proposal.")
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
603
            _call_webservice(
604
                mp.setStatus, status=u'Approved',
605
                revid=self.source_branch.last_revision())
0.431.24 by Jelmer Vernooij
Support git merge proposals.
606
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
607
    def create_proposal(self, description, reviewers=None, labels=None,
608
                        prerequisite_branch=None):
0.431.24 by Jelmer Vernooij
Support git merge proposals.
609
        """Perform the submission."""
610
        if labels:
611
            raise LabelsUnsupported()
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
612
        if prerequisite_branch is not None:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
613
            (prereq_repo_lp, prereq_branch_lp) = (
614
                self.lp_host._get_lp_git_ref_from_branch(prerequisite_branch))
0.431.24 by Jelmer Vernooij
Support git merge proposals.
615
        else:
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
616
            prereq_branch_lp = None
0.431.24 by Jelmer Vernooij
Support git merge proposals.
617
        if reviewers is None:
618
            reviewers = []
619
        try:
620
            mp = _call_webservice(
621
                self.source_branch_lp.createMergeProposal,
622
                merge_target=self.target_branch_lp,
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
623
                merge_prerequisite=prereq_branch_lp,
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
624
                initial_comment=description.strip(),
0.431.24 by Jelmer Vernooij
Support git merge proposals.
625
                commit_message=self.commit_message,
626
                needs_review=True,
627
                reviewers=[self.launchpad.people[reviewer].self_link
628
                           for reviewer in reviewers],
629
                review_types=[None for reviewer in reviewers])
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
630
        except WebserviceFailure as e:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
631
            # Urgh.
632
            if ('There is already a branch merge proposal '
7211.13.7 by Jelmer Vernooij
Fix formatting.
633
                    'registered for branch ') in e.message:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
634
                raise MergeProposalExists(self.source_branch.user_url)
635
            raise
636
        if self.approve:
637
            self.approve_proposal(mp)
638
        if self.fixes:
639
            if self.fixes.startswith('lp:'):
640
                self.fixes = self.fixes[3:]
641
            _call_webservice(
642
                mp.linkBug,
643
                bug=self.launchpad.bugs[int(self.fixes)])
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
644
        return LaunchpadMergeProposal(mp)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
645
646
647
def modified_files(old_tree, new_tree):
648
    """Return a list of paths in the new tree with modified contents."""
649
    for f, (op, path), c, v, p, n, (ok, k), e in new_tree.iter_changes(
650
            old_tree):
651
        if c and k == 'file':
652
            yield str(path)