/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.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
20
import re
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
21
import shutil
22
import tempfile
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
23
7408.3.1 by Jelmer Vernooij
Move propose module into core.
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
    )
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
40
from ...git.urls import git_url_to_bzr_url
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,
7464.2.1 by Jelmer Vernooij
Fix finding merge proposals by URL.
45
    uris as lp_uris,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
46
    )
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
47
48
from launchpadlib import uris
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
49
""")
0.432.2 by Jelmer Vernooij
Publish command sort of works.
50
from ...transport import get_transport
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
51
52
0.431.4 by Jelmer Vernooij
Add basic GitHub support.
53
# TODO(jelmer): Make selection of launchpad staging a configuration option.
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
54
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
55
def status_to_lp_mp_statuses(status):
56
    statuses = []
57
    if status in ('open', 'all'):
58
        statuses.extend([
59
            'Work in progress',
60
            'Needs review',
61
            'Approved',
62
            'Code failed to merge',
63
            'Queued'])
64
    if status in ('closed', 'all'):
65
        statuses.extend(['Rejected', 'Superseded'])
66
    if status in ('merged', 'all'):
67
        statuses.append('Merged')
68
    return statuses
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
69
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
70
71
def plausible_launchpad_url(url):
72
    if url is None:
73
        return False
74
    if url.startswith('lp:'):
75
        return True
7322.2.1 by Martin
Fix invalid escape sequence warnings in regexps
76
    regex = re.compile(r'([a-z]*\+)*(bzr\+ssh|http|ssh|git|https)'
77
                       r'://(bazaar|git).*\.launchpad\.net')
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
78
    return bool(regex.match(url))
79
80
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
81
class WebserviceFailure(Exception):
82
83
    def __init__(self, message):
84
        self.message = message
85
86
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
87
def _call_webservice(call, *args, **kwargs):
88
    """Make a call to the webservice, wrapping failures.
89
90
    :param call: The call to make.
91
    :param *args: *args for the call.
92
    :param **kwargs: **kwargs for the call.
93
    :return: The result of calling call(*args, *kwargs).
94
    """
95
    from lazr.restfulclient import errors as restful_errors
96
    try:
97
        return call(*args, **kwargs)
98
    except restful_errors.HTTPError as e:
99
        error_lines = []
100
        for line in e.content.splitlines():
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
101
            if line.startswith(b'Traceback (most recent call last):'):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
102
                break
103
            error_lines.append(line)
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
104
        raise WebserviceFailure(b''.join(error_lines))
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
105
106
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
107
class LaunchpadMergeProposal(MergeProposal):
108
109
    def __init__(self, mp):
110
        self._mp = mp
111
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
112
    def get_source_branch_url(self):
113
        if self._mp.source_branch:
114
            return self._mp.source_branch.bzr_identity
115
        else:
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
116
            return git_url_to_bzr_url(
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
117
                self._mp.source_git_repository.git_identity,
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
118
                ref=self._mp.source_git_path.encode('utf-8'))
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
119
7490.65.1 by Jelmer Vernooij
Add functions fo retrieving merge proposal source revision.
120
    def get_source_revision(self):
121
        if self._mp.source_branch:
122
            last_scanned_id = self._mp.source_branch.last_scanned_id
123
            if last_scanned_id:
124
                return last_scanned_id.encode('utf-8')
125
            else:
126
                return None
127
        else:
128
            from breezy.git.mapping import default_mapping
129
            git_repo = self._mp.source_git_repository
7490.65.2 by Jelmer Vernooij
Fix bugs, add release note.
130
            git_ref = git_repo.getRefByPath(path=self._mp.source_git_path)
131
            sha = git_ref.commit_sha1
7490.65.1 by Jelmer Vernooij
Add functions fo retrieving merge proposal source revision.
132
            if sha is None:
133
                return None
134
            return default_mapping.revision_id_foreign_to_bzr(
135
                sha.encode('ascii'))
136
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
137
    def get_target_branch_url(self):
138
        if self._mp.target_branch:
139
            return self._mp.target_branch.bzr_identity
140
        else:
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
141
            return git_url_to_bzr_url(
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
142
                self._mp.target_git_repository.git_identity,
7408.2.1 by Jelmer Vernooij
Use standard functions for creating Git URLs.
143
                ref=self._mp.target_git_path.encode('utf-8'))
0.431.64 by Jelmer Vernooij
Add get_source_branch_url/get_target_branch_url methods.
144
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
145
    @property
146
    def url(self):
147
        return lp_api.canonical_url(self._mp)
148
149
    def is_merged(self):
150
        return (self._mp.queue_status == 'Merged')
151
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
152
    def is_closed(self):
153
        return (self._mp.queue_status in ('Rejected', 'Superseded'))
154
155
    def reopen(self):
156
        self._mp.setStatus(status='Needs review')
157
0.431.70 by Jelmer Vernooij
Implement get_description/set_description for LaunchpadMergeProposal.
158
    def get_description(self):
159
        return self._mp.description
160
161
    def set_description(self, description):
162
        self._mp.description = description
7290.5.1 by Jelmer Vernooij
Actually send API call to Launchpad when updating description.
163
        self._mp.lp_save()
0.431.70 by Jelmer Vernooij
Implement get_description/set_description for LaunchpadMergeProposal.
164
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
165
    def get_commit_message(self):
166
        return self._mp.commit_message
167
168
    def set_commit_message(self, commit_message):
169
        self._mp.commit_message = commit_message
170
        self._mp.lp_save()
171
7260.2.1 by Jelmer Vernooij
Implement .close on merge proposals.
172
    def close(self):
173
        self._mp.setStatus(status='Rejected')
174
7381.2.1 by Jelmer Vernooij
Implement LaunchpadMergeProposal.can_be_merged.
175
    def can_be_merged(self):
176
        if not self._mp.preview_diff:
177
            # Maybe?
178
            return True
179
        return not bool(self._mp.preview_diff.conflicts)
180
7414.4.1 by Jelmer Vernooij
Add a MergeProposal.get_merged_by method.
181
    def get_merged_by(self):
182
        merge_reporter = self._mp.merge_reporter
183
        if merge_reporter is None:
184
            return None
185
        return merge_reporter.name
186
7414.4.3 by Jelmer Vernooij
Add MergeProposal.get_merged_at.
187
    def get_merged_at(self):
188
        return self._mp.date_merged
189
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
190
    def merge(self, commit_message=None):
191
        target_branch = _mod_branch.Branch.open(
192
            self.get_target_branch_url())
193
        source_branch = _mod_branch.Branch.open(
194
            self.get_source_branch_url())
195
        # TODO(jelmer): Ideally this would use a memorytree, but merge doesn't
196
        # support that yet.
197
        # tree = target_branch.create_memorytree()
198
        tmpdir = tempfile.mkdtemp()
199
        try:
200
            tree = target_branch.create_checkout(
201
                to_location=tmpdir, lightweight=True)
202
            tree.merge_from_branch(source_branch)
203
            tree.commit(commit_message or self._mp.commit_message)
204
        finally:
205
            shutil.rmtree(tmpdir)
206
7490.52.1 by Jelmer Vernooij
Add MergeProposal.post_comment.
207
    def post_comment(self, body):
208
        self._mp.createComment(content=body)
209
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
210
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
211
class Launchpad(Hoster):
212
    """The Launchpad hosting service."""
213
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
214
    name = 'launchpad'
215
0.431.29 by Jelmer Vernooij
Fix github branch name, add bug URL.
216
    # https://bugs.launchpad.net/launchpad/+bug/397676
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
217
    supports_merge_proposal_labels = False
218
7296.8.2 by Jelmer Vernooij
Add feature flag for commit message.
219
    supports_merge_proposal_commit_message = True
220
7490.3.9 by Jelmer Vernooij
Add supports_allow_collaboration flag.
221
    supports_allow_collaboration = False
222
7445.1.1 by Jelmer Vernooij
Add Hoster.merge_proposal_description_format and common function for determining title.
223
    merge_proposal_description_format = 'plain'
224
7254.1.2 by Jelmer Vernooij
Fix propose Launchpad.
225
    def __init__(self, staging=False):
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
226
        self._staging = staging
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
227
        if staging:
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
228
            lp_base_url = uris.STAGING_SERVICE_ROOT
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
229
        else:
7253 by Jelmer Vernooij
Fix default launchpadlib API URL.
230
            lp_base_url = uris.LPNET_SERVICE_ROOT
7490.86.4 by Jelmer Vernooij
Lazily connect to launchpad.
231
        self._api_base_url = lp_base_url
232
        self._launchpad = None
233
234
    @property
235
    def launchpad(self):
236
        if self._launchpad is None:
237
            self._launchpad = lp_api.connect_launchpad(self._api_base_url, version='devel')
238
        return self._launchpad
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
239
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
240
    @property
241
    def base_url(self):
7490.86.5 by Jelmer Vernooij
Avoid accessing launchpad just to get the base URL.
242
        return lp_api.uris.web_root_for_service_root(self._api_base_url)
7260.1.1 by Jelmer Vernooij
Add .base_url property to Hoster.
243
0.433.1 by Jelmer Vernooij
Add Hoster.hosts.
244
    def __repr__(self):
245
        return "Launchpad(staging=%s)" % self._staging
246
247
    def hosts(self, branch):
248
        # TODO(jelmer): staging vs non-staging?
249
        return plausible_launchpad_url(branch.user_url)
250
0.432.2 by Jelmer Vernooij
Publish command sort of works.
251
    @classmethod
7296.10.1 by Jelmer Vernooij
Initial work making gitlab just directly use ReST.
252
    def probe_from_url(cls, url, possible_transports=None):
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
253
        if plausible_launchpad_url(url):
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
254
            return Launchpad()
7268.12.1 by Jelmer Vernooij
Split out probe_from_url.
255
        raise UnsupportedHoster(url)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
256
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
257
    def _get_lp_git_ref_from_branch(self, branch):
258
        url, params = urlutils.split_segment_parameters(branch.user_url)
259
        (scheme, user, password, host, port, path) = urlutils.parse_url(
260
            url)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
261
        repo_lp = self.launchpad.git_repositories.getByPath(
262
            path=path.strip('/'))
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
263
        try:
264
            ref_path = params['ref']
265
        except KeyError:
266
            branch_name = params.get('branch', branch.name)
267
            if branch_name:
268
                ref_path = 'refs/heads/%s' % branch_name
269
            else:
270
                ref_path = repo_lp.default_branch
271
        ref_lp = repo_lp.getRefByPath(path=ref_path)
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
272
        return (repo_lp, ref_lp)
273
274
    def _get_lp_bzr_branch_from_branch(self, branch):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
275
        return self.launchpad.branches.getByUrl(
276
            url=urlutils.unescape(branch.user_url))
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
277
0.431.23 by Jelmer Vernooij
Launchpad fixes.
278
    def _get_derived_git_path(self, base_path, owner, project):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
279
        base_repo = self.launchpad.git_repositories.getByPath(path=base_path)
280
        if project is None:
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
281
            project = urlutils.parse_url(base_repo.git_ssh_url)[-1].strip('/')
282
        if project.startswith('~'):
283
            project = '/'.join(base_path.split('/')[1:])
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
284
        # TODO(jelmer): Surely there is a better way of creating one of these
285
        # URLs?
0.431.23 by Jelmer Vernooij
Launchpad fixes.
286
        return "~%s/%s" % (owner, project)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
287
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
288
    def _publish_git(self, local_branch, base_path, name, owner, project=None,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
289
                     revision_id=None, overwrite=False, allow_lossy=True,
290
                     tag_selector=None):
0.431.23 by Jelmer Vernooij
Launchpad fixes.
291
        to_path = self._get_derived_git_path(base_path, owner, project)
292
        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.
293
        try:
294
            dir_to = controldir.ControlDir.open_from_transport(to_transport)
295
        except errors.NotBranchError:
296
            # Didn't find anything
297
            dir_to = None
298
299
        if dir_to is None:
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
300
            try:
301
                br_to = local_branch.create_clone_on_transport(
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
302
                    to_transport, revision_id=revision_id, name=name,
303
                    tag_selector=tag_selector)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
304
            except errors.NoRoundtrippingSupport:
305
                br_to = local_branch.create_clone_on_transport(
7211.13.7 by Jelmer Vernooij
Fix formatting.
306
                    to_transport, revision_id=revision_id, name=name,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
307
                    lossy=True, tag_selector=tag_selector)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
308
        else:
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
309
            try:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
310
                dir_to = dir_to.push_branch(
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
311
                    local_branch, revision_id, overwrite=overwrite, name=name,
312
                    tag_selector=tag_selector)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
313
            except errors.NoRoundtrippingSupport:
314
                if not allow_lossy:
315
                    raise
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
316
                dir_to = dir_to.push_branch(
317
                    local_branch, revision_id, overwrite=overwrite, name=name,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
318
                    lossy=True, tag_selector=tag_selector)
0.431.51 by Jelmer Vernooij
Allow fallback to lossy by default.
319
            br_to = dir_to.target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
320
        return br_to, (
321
            "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.
322
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
323
    def _get_derived_bzr_path(self, base_branch, name, owner, project):
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
324
        if project is None:
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
325
            base_branch_lp = self._get_lp_bzr_branch_from_branch(base_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
326
            project = '/'.join(base_branch_lp.unique_name.split('/')[1:-1])
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
327
        # TODO(jelmer): Surely there is a better way of creating one of these
328
        # URLs?
0.431.23 by Jelmer Vernooij
Launchpad fixes.
329
        return "~%s/%s/%s" % (owner, project, name)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
330
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
331
    def get_push_url(self, branch):
332
        (vcs, user, password, path, params) = self._split_url(branch.user_url)
333
        if vcs == 'bzr':
334
            branch_lp = self._get_lp_bzr_branch_from_branch(branch)
335
            return branch_lp.bzr_identity
336
        elif vcs == 'git':
337
            return urlutils.join_segment_parameters(
338
                "git+ssh://git.launchpad.net/" + path, params)
339
        else:
340
            raise AssertionError
341
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
342
    def _publish_bzr(self, local_branch, base_branch, name, owner,
343
                     project=None, revision_id=None, overwrite=False,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
344
                     allow_lossy=True, tag_selector=None):
0.431.25 by Jelmer Vernooij
Fix launchpad URL unescaping.
345
        to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
0.431.23 by Jelmer Vernooij
Launchpad fixes.
346
        to_transport = get_transport("lp:" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
347
        try:
348
            dir_to = controldir.ControlDir.open_from_transport(to_transport)
349
        except errors.NotBranchError:
350
            # Didn't find anything
351
            dir_to = None
352
353
        if dir_to is None:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
354
            br_to = local_branch.create_clone_on_transport(
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
355
                to_transport, revision_id=revision_id, tag_selector=tag_selector)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
356
        else:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
357
            br_to = dir_to.push_branch(
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
358
                local_branch, revision_id, overwrite=overwrite,
359
                tag_selector=tag_selector).target_branch
0.431.23 by Jelmer Vernooij
Launchpad fixes.
360
        return br_to, ("https://code.launchpad.net/" + to_path)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
361
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
362
    def _split_url(self, url):
363
        url, params = urlutils.split_segment_parameters(url)
364
        (scheme, user, password, host, port, path) = urlutils.parse_url(url)
365
        path = path.strip('/')
366
        if host.startswith('bazaar.'):
367
            vcs = 'bzr'
368
        elif host.startswith('git.'):
369
            vcs = 'git'
370
        else:
371
            raise ValueError("unknown host %s" % host)
372
        return (vcs, user, password, path, params)
373
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
374
    def publish_derived(self, local_branch, base_branch, name, project=None,
375
                        owner=None, revision_id=None, overwrite=False,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
376
                        allow_lossy=True, tag_selector=None):
0.432.2 by Jelmer Vernooij
Publish command sort of works.
377
        """Publish a branch to the site, derived from base_branch.
378
379
        :param base_branch: branch to derive the new branch from
380
        :param new_branch: branch to publish
381
        :param name: Name of the new branch on the remote host
382
        :param project: Optional project name
383
        :param owner: Optional owner
384
        :return: resulting branch
385
        """
386
        if owner is None:
0.432.9 by Jelmer Vernooij
Drop is_compatible nonesense.
387
            owner = self.launchpad.me.name
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
388
        (base_vcs, base_user, base_password, base_path,
389
            base_params) = self._split_url(base_branch.user_url)
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
390
        # TODO(jelmer): Prevent publishing to development focus
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
391
        if base_vcs == 'bzr':
7211.13.7 by Jelmer Vernooij
Fix formatting.
392
            return self._publish_bzr(
393
                local_branch, base_branch, name, project=project, owner=owner,
394
                revision_id=revision_id, overwrite=overwrite,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
395
                allow_lossy=allow_lossy, tag_selector=tag_selector)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
396
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
397
            return self._publish_git(
398
                local_branch, base_path, name, project=project, owner=owner,
399
                revision_id=revision_id, overwrite=overwrite,
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
400
                allow_lossy=allow_lossy, tag_selector=tag_selector)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
401
        else:
0.431.15 by Jelmer Vernooij
Initial work on support for git branches in launchpad.
402
            raise AssertionError('not a valid Launchpad URL')
0.432.2 by Jelmer Vernooij
Publish command sort of works.
403
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
404
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
405
        if owner is None:
406
            owner = self.launchpad.me.name
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
407
        (base_vcs, base_user, base_password, base_path,
408
            base_params) = self._split_url(base_branch.user_url)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
409
        if base_vcs == 'bzr':
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
410
            to_path = self._get_derived_bzr_path(
411
                base_branch, name, owner, project)
0.431.23 by Jelmer Vernooij
Launchpad fixes.
412
            return _mod_branch.Branch.open("lp:" + to_path)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
413
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
414
            to_path = self._get_derived_git_path(
415
                base_path.strip('/'), owner, project)
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
416
            to_url = urlutils.join_segment_parameters(
7240.4.1 by Jelmer Vernooij
Merge lp:brz-propose.
417
                "git+ssh://git.launchpad.net/" + to_path,
418
                {'branch': name})
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
419
            return _mod_branch.Branch.open(to_url)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
420
        else:
421
            raise AssertionError('not a valid Launchpad URL')
422
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
423
    def iter_proposals(self, source_branch, target_branch, status='open'):
424
        (base_vcs, base_user, base_password, base_path,
425
            base_params) = self._split_url(target_branch.user_url)
426
        statuses = status_to_lp_mp_statuses(status)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
427
        if base_vcs == 'bzr':
7211.13.7 by Jelmer Vernooij
Fix formatting.
428
            target_branch_lp = self.launchpad.branches.getByUrl(
429
                url=target_branch.user_url)
430
            source_branch_lp = self.launchpad.branches.getByUrl(
431
                url=source_branch.user_url)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
432
            for mp in target_branch_lp.getMergeProposals(status=statuses):
433
                if mp.source_branch_link != source_branch_lp.self_link:
434
                    continue
435
                yield LaunchpadMergeProposal(mp)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
436
        elif base_vcs == 'git':
7211.13.7 by Jelmer Vernooij
Fix formatting.
437
            (source_repo_lp, source_branch_lp) = (
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
438
                self._get_lp_git_ref_from_branch(source_branch))
7211.13.7 by Jelmer Vernooij
Fix formatting.
439
            (target_repo_lp, target_branch_lp) = (
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
440
                self._get_lp_git_ref_from_branch(target_branch))
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
441
            for mp in target_branch_lp.getMergeProposals(status=statuses):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
442
                if (target_branch_lp.path != mp.target_git_path or
7211.13.7 by Jelmer Vernooij
Fix formatting.
443
                        target_repo_lp != mp.target_git_repository or
444
                        source_branch_lp.path != mp.source_git_path or
445
                        source_repo_lp != mp.source_git_repository):
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
446
                    continue
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
447
                yield LaunchpadMergeProposal(mp)
0.431.35 by Jelmer Vernooij
Add Hoster.get_proposal.
448
        else:
449
            raise AssertionError('not a valid Launchpad URL')
450
0.432.2 by Jelmer Vernooij
Publish command sort of works.
451
    def get_proposer(self, source_branch, target_branch):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
452
        (base_vcs, base_user, base_password, base_path,
453
            base_params) = self._split_url(target_branch.user_url)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
454
        if base_vcs == 'bzr':
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
455
            return LaunchpadBazaarMergeProposalBuilder(
7211.13.7 by Jelmer Vernooij
Fix formatting.
456
                self, source_branch, target_branch)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
457
        elif base_vcs == 'git':
0.431.24 by Jelmer Vernooij
Support git merge proposals.
458
            return LaunchpadGitMergeProposalBuilder(
7211.13.7 by Jelmer Vernooij
Fix formatting.
459
                self, source_branch, target_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
460
        else:
461
            raise AssertionError('not a valid Launchpad URL')
0.432.2 by Jelmer Vernooij
Publish command sort of works.
462
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
463
    @classmethod
464
    def iter_instances(cls):
465
        yield cls()
466
0.431.66 by Jelmer Vernooij
Add support for status argument.
467
    def iter_my_proposals(self, status='open'):
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
468
        statuses = status_to_lp_mp_statuses(status)
0.431.66 by Jelmer Vernooij
Add support for status argument.
469
        for mp in self.launchpad.me.getMergeProposals(status=statuses):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
470
            yield LaunchpadMergeProposal(mp)
471
7414.5.2 by Jelmer Vernooij
Change iter_my_projects to iter_my_forks.
472
    def iter_my_forks(self):
473
        # Launchpad doesn't really have the concept of "forks"
7414.5.1 by Jelmer Vernooij
Add functions for managing projects.
474
        return iter([])
475
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
476
    def get_proposal_by_url(self, url):
477
        # Launchpad doesn't have a way to find a merge proposal by URL.
478
        (scheme, user, password, host, port, path) = urlutils.parse_url(
479
            url)
7296.9.2 by Jelmer Vernooij
Fix detection of launchpad hosts.
480
        LAUNCHPAD_CODE_DOMAINS = [
7464.2.1 by Jelmer Vernooij
Fix finding merge proposals by URL.
481
            ('code.%s' % domain) for domain in lp_uris.LAUNCHPAD_DOMAINS.values()]
7296.9.2 by Jelmer Vernooij
Fix detection of launchpad hosts.
482
        if host not in LAUNCHPAD_CODE_DOMAINS:
483
            raise UnsupportedHoster(url)
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
484
        # TODO(jelmer): Check if this is a launchpad URL. Otherwise, raise
485
        # UnsupportedHoster
486
        # See https://api.launchpad.net/devel/#branch_merge_proposal
487
        # the syntax is:
488
        # https://api.launchpad.net/devel/~<author.name>/<project.name>/<branch.name>/+merge/<id>
489
        api_url = str(self.launchpad._root_uri) + path
490
        mp = self.launchpad.load(api_url)
491
        return LaunchpadMergeProposal(mp)
492
0.432.2 by Jelmer Vernooij
Publish command sort of works.
493
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
494
class LaunchpadBazaarMergeProposalBuilder(MergeProposalBuilder):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
495
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
496
    def __init__(self, lp_host, source_branch, target_branch,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
497
                 staging=None, approve=None, fixes=None):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
498
        """Constructor.
499
500
        :param source_branch: The branch to propose for merging.
501
        :param target_branch: The branch to merge into.
502
        :param staging: If True, propose the merge against staging instead of
503
            production.
504
        :param approve: If True, mark the new proposal as approved immediately.
505
            This is useful when a project permits some things to be approved
506
            by the submitter (e.g. merges between release and deployment
507
            branches).
508
        """
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
509
        self.lp_host = lp_host
510
        self.launchpad = lp_host.launchpad
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
511
        self.source_branch = source_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
512
        self.source_branch_lp = self.launchpad.branches.getByUrl(
513
            url=source_branch.user_url)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
514
        if target_branch is None:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
515
            self.target_branch_lp = self.source_branch_lp.get_target()
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
516
            self.target_branch = _mod_branch.Branch.open(
517
                self.target_branch_lp.bzr_identity)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
518
        else:
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
519
            self.target_branch = target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
520
            self.target_branch_lp = self.launchpad.branches.getByUrl(
521
                url=target_branch.user_url)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
522
        self.approve = approve
523
        self.fixes = fixes
524
525
    def get_infotext(self):
526
        """Determine the initial comment for the merge proposal."""
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
527
        info = ["Source: %s\n" % self.source_branch_lp.bzr_identity]
528
        info.append("Target: %s\n" % self.target_branch_lp.bzr_identity)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
529
        return ''.join(info)
530
531
    def get_initial_body(self):
532
        """Get a body for the proposal for the user to modify.
533
534
        :return: a str or None.
535
        """
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
536
        if not self.hooks['merge_proposal_body']:
537
            return None
538
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
539
        def list_modified_files():
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
540
            lca_tree = self.source_branch_lp.find_lca_tree(
541
                self.target_branch_lp)
542
            source_tree = self.source_branch.basis_tree()
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
543
            files = modified_files(lca_tree, source_tree)
544
            return list(files)
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
545
        with self.target_branch.lock_read(), \
546
                self.source_branch.lock_read():
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
547
            body = None
548
            for hook in self.hooks['merge_proposal_body']:
549
                body = hook({
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
550
                    'target_branch': self.target_branch_lp.bzr_identity,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
551
                    'modified_files_callback': list_modified_files,
552
                    'old_body': body,
553
                })
554
            return body
555
556
    def check_proposal(self):
557
        """Check that the submission is sensible."""
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
558
        if self.source_branch_lp.self_link == self.target_branch_lp.self_link:
7490.61.1 by Jelmer Vernooij
Rename BzrCommandError to CommandError.
559
            raise errors.CommandError(
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
560
                'Source and target branches must be different.')
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
561
        for mp in self.source_branch_lp.landing_targets:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
562
            if mp.queue_status in ('Merged', 'Rejected'):
563
                continue
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
564
            if mp.target_branch.self_link == self.target_branch_lp.self_link:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
565
                raise MergeProposalExists(lp_api.canonical_url(mp))
566
567
    def approve_proposal(self, mp):
0.431.11 by Jelmer Vernooij
Fix launchpad handling.
568
        with self.source_branch.lock_read():
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
569
            _call_webservice(
570
                mp.createComment,
571
                vote=u'Approve',
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
572
                subject='',  # Use the default subject.
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
573
                content=u"Rubberstamp! Proposer approves of own proposal.")
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
574
            _call_webservice(mp.setStatus, status=u'Approved',
575
                             revid=self.source_branch.last_revision())
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
576
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
577
    def create_proposal(self, description, reviewers=None, labels=None,
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
578
                        prerequisite_branch=None, commit_message=None,
7490.6.1 by Jelmer Vernooij
Add allow-collaboration flag.
579
                        work_in_progress=False, allow_collaboration=False):
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
580
        """Perform the submission."""
0.431.13 by Jelmer Vernooij
Add support for labels on merge proposals.
581
        if labels:
7295.2.1 by Jelmer Vernooij
Fix some issues reported by lgtm.com.
582
            raise LabelsUnsupported(self)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
583
        if prerequisite_branch is not None:
584
            prereq = self.launchpad.branches.getByUrl(
585
                url=prerequisite_branch.user_url)
586
        else:
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
587
            prereq = None
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
588
        if reviewers is None:
7381.5.1 by Jelmer Vernooij
Several more fixes for merge proposals. Add functions for reopening merge proposals.
589
            reviewer_objs = []
590
        else:
591
            reviewer_objs = []
592
            for reviewer in reviewers:
593
                if '@' in reviewer:
594
                    reviewer_obj = self.launchpad.people.getByEmail(email=reviewer)
595
                else:
596
                    reviewer_obj = self.launchpad.people[reviewer]
597
                reviewer_objs.append(reviewer_obj)
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
598
        try:
599
            mp = _call_webservice(
600
                self.source_branch_lp.createMergeProposal,
601
                target_branch=self.target_branch_lp,
602
                prerequisite_branch=prereq,
0.431.42 by Jelmer Vernooij
Remove unnecessary encode (breaks on Python 3).
603
                initial_comment=description.strip(),
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
604
                commit_message=commit_message,
7467.3.1 by Jelmer Vernooij
Add a work_in_progress flag.
605
                needs_review=(not work_in_progress),
7381.5.5 by Jelmer Vernooij
Review feedback.
606
                reviewers=[reviewer.self_link for reviewer in reviewer_objs],
607
                review_types=['' for reviewer in reviewer_objs])
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
608
        except WebserviceFailure as e:
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
609
            # Urgh.
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
610
            if (b'There is already a branch merge proposal '
7211.13.7 by Jelmer Vernooij
Fix formatting.
611
                    b'registered for branch ') in e.message:
0.431.28 by Jelmer Vernooij
Implement Hoster.get_push_url.
612
                raise MergeProposalExists(self.source_branch.user_url)
613
            raise
614
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
615
        if self.approve:
616
            self.approve_proposal(mp)
617
        if self.fixes:
618
            if self.fixes.startswith('lp:'):
619
                self.fixes = self.fixes[3:]
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
620
            _call_webservice(
621
                mp.linkBug,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
622
                bug=self.launchpad.bugs[int(self.fixes)])
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
623
        return LaunchpadMergeProposal(mp)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
624
625
626
class LaunchpadGitMergeProposalBuilder(MergeProposalBuilder):
627
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
628
    def __init__(self, lp_host, source_branch, target_branch,
0.431.24 by Jelmer Vernooij
Support git merge proposals.
629
                 staging=None, approve=None, fixes=None):
630
        """Constructor.
631
632
        :param source_branch: The branch to propose for merging.
633
        :param target_branch: The branch to merge into.
634
        :param staging: If True, propose the merge against staging instead of
635
            production.
636
        :param approve: If True, mark the new proposal as approved immediately.
637
            This is useful when a project permits some things to be approved
638
            by the submitter (e.g. merges between release and deployment
639
            branches).
640
        """
0.431.26 by Jelmer Vernooij
Fix handling of proposals for Launchapd Git URLs.
641
        self.lp_host = lp_host
642
        self.launchpad = lp_host.launchpad
0.431.24 by Jelmer Vernooij
Support git merge proposals.
643
        self.source_branch = source_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
644
        (self.source_repo_lp,
645
            self.source_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(
646
                source_branch)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
647
        if target_branch is None:
648
            self.target_branch_lp = self.source_branch.get_target()
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
649
            self.target_branch = _mod_branch.Branch.open(
650
                self.target_branch_lp.git_https_url)
0.431.24 by Jelmer Vernooij
Support git merge proposals.
651
        else:
652
            self.target_branch = target_branch
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
653
            (self.target_repo_lp, self.target_branch_lp) = (
654
                self.lp_host._get_lp_git_ref_from_branch(target_branch))
0.431.24 by Jelmer Vernooij
Support git merge proposals.
655
        self.approve = approve
656
        self.fixes = fixes
657
658
    def get_infotext(self):
659
        """Determine the initial comment for the merge proposal."""
660
        info = ["Source: %s\n" % self.source_branch.user_url]
661
        info.append("Target: %s\n" % self.target_branch.user_url)
662
        return ''.join(info)
663
664
    def get_initial_body(self):
665
        """Get a body for the proposal for the user to modify.
666
667
        :return: a str or None.
668
        """
669
        if not self.hooks['merge_proposal_body']:
670
            return None
671
672
        def list_modified_files():
673
            lca_tree = self.source_branch_lp.find_lca_tree(
674
                self.target_branch_lp)
675
            source_tree = self.source_branch.basis_tree()
676
            files = modified_files(lca_tree, source_tree)
677
            return list(files)
678
        with self.target_branch.lock_read(), \
679
                self.source_branch.lock_read():
680
            body = None
681
            for hook in self.hooks['merge_proposal_body']:
682
                body = hook({
683
                    'target_branch': self.target_branch,
684
                    'modified_files_callback': list_modified_files,
685
                    'old_body': body,
686
                })
687
            return body
688
689
    def check_proposal(self):
690
        """Check that the submission is sensible."""
691
        if self.source_branch_lp.self_link == self.target_branch_lp.self_link:
7490.61.1 by Jelmer Vernooij
Rename BzrCommandError to CommandError.
692
            raise errors.CommandError(
0.431.24 by Jelmer Vernooij
Support git merge proposals.
693
                'Source and target branches must be different.')
694
        for mp in self.source_branch_lp.landing_targets:
695
            if mp.queue_status in ('Merged', 'Rejected'):
696
                continue
697
            if mp.target_branch.self_link == self.target_branch_lp.self_link:
698
                raise MergeProposalExists(lp_api.canonical_url(mp))
699
700
    def approve_proposal(self, mp):
701
        with self.source_branch.lock_read():
702
            _call_webservice(
703
                mp.createComment,
704
                vote=u'Approve',
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
705
                subject='',  # Use the default subject.
0.431.24 by Jelmer Vernooij
Support git merge proposals.
706
                content=u"Rubberstamp! Proposer approves of own proposal.")
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
707
            _call_webservice(
708
                mp.setStatus, status=u'Approved',
709
                revid=self.source_branch.last_revision())
0.431.24 by Jelmer Vernooij
Support git merge proposals.
710
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
711
    def create_proposal(self, description, reviewers=None, labels=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
712
                        prerequisite_branch=None, commit_message=None):
0.431.24 by Jelmer Vernooij
Support git merge proposals.
713
        """Perform the submission."""
714
        if labels:
7295.2.1 by Jelmer Vernooij
Fix some issues reported by lgtm.com.
715
            raise LabelsUnsupported(self)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
716
        if prerequisite_branch is not None:
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
717
            (prereq_repo_lp, prereq_branch_lp) = (
718
                self.lp_host._get_lp_git_ref_from_branch(prerequisite_branch))
0.431.24 by Jelmer Vernooij
Support git merge proposals.
719
        else:
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
720
            prereq_branch_lp = None
0.431.24 by Jelmer Vernooij
Support git merge proposals.
721
        if reviewers is None:
722
            reviewers = []
723
        try:
724
            mp = _call_webservice(
725
                self.source_branch_lp.createMergeProposal,
726
                merge_target=self.target_branch_lp,
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
727
                merge_prerequisite=prereq_branch_lp,
0.431.71 by Jelmer Vernooij
Some fixes for Git on Launchpad.
728
                initial_comment=description.strip(),
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
729
                commit_message=commit_message,
0.431.24 by Jelmer Vernooij
Support git merge proposals.
730
                needs_review=True,
731
                reviewers=[self.launchpad.people[reviewer].self_link
732
                           for reviewer in reviewers],
733
                review_types=[None for reviewer in reviewers])
0.431.58 by Jelmer Vernooij
Fix python3 compatibility.
734
        except WebserviceFailure as e:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
735
            # Urgh.
736
            if ('There is already a branch merge proposal '
7211.13.7 by Jelmer Vernooij
Fix formatting.
737
                    'registered for branch ') in e.message:
0.431.24 by Jelmer Vernooij
Support git merge proposals.
738
                raise MergeProposalExists(self.source_branch.user_url)
739
            raise
740
        if self.approve:
741
            self.approve_proposal(mp)
742
        if self.fixes:
743
            if self.fixes.startswith('lp:'):
744
                self.fixes = self.fixes[3:]
745
            _call_webservice(
746
                mp.linkBug,
747
                bug=self.launchpad.bugs[int(self.fixes)])
0.431.46 by Jelmer Vernooij
Add MergeProposal.is_merged.
748
        return LaunchpadMergeProposal(mp)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
749
750
751
def modified_files(old_tree, new_tree):
752
    """Return a list of paths in the new tree with modified contents."""
7322.1.6 by Jelmer Vernooij
Use the new attributes on TreeChange.
753
    for change in new_tree.iter_changes(old_tree):
754
        if change.changed_content and change.kind[1] == 'file':
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
755
            yield str(path)