/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/plugins/propose/launchpad.py

  • Committer: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2019-01-08 22:04:13 UTC
  • mfrom: (7233.3.7 my-proposals)
  • Revision ID: breezy.the.bot@gmail.com-20190108220413-jnk79i8o4wk0h52j
Add 'bzr my-proposals' command.

Merged from https://code.launchpad.net/~jelmer/brz/my-proposals/+merge/361363

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
    MergeProposal,
28
28
    MergeProposalBuilder,
29
29
    MergeProposalExists,
30
 
    NoMergeProposal,
31
30
    UnsupportedHoster,
32
31
    )
33
32
 
38
37
    hooks,
39
38
    urlutils,
40
39
    )
 
40
from ...git.refs import ref_to_branch_name
41
41
from ...lazy_import import lazy_import
42
42
lazy_import(globals(), """
43
43
from breezy.plugins.launchpad import (
50
50
 
51
51
# TODO(jelmer): Make selection of launchpad staging a configuration option.
52
52
 
53
 
MERGE_PROPOSAL_STATUSES = [
54
 
    'Work in progress',
55
 
    'Needs review',
56
 
    'Approved',
57
 
    'Rejected',
58
 
    'Merged',
59
 
    'Code failed to merge',
60
 
    'Queued',
61
 
    'Superseded',
62
 
    ]
 
53
def status_to_lp_mp_statuses(status):
 
54
    statuses = []
 
55
    if status in ('open', 'all'):
 
56
        statuses.extend([
 
57
            'Work in progress',
 
58
            'Needs review',
 
59
            'Approved',
 
60
            'Code failed to merge',
 
61
            'Queued'])
 
62
    if status in ('closed', 'all'):
 
63
        statuses.extend(['Rejected', 'Superseded'])
 
64
    if status in ('merged', 'all'):
 
65
        statuses.append('Merged')
 
66
    return statuses
63
67
 
64
68
 
65
69
def plausible_launchpad_url(url):
103
107
    def __init__(self, mp):
104
108
        self._mp = mp
105
109
 
 
110
    def get_source_branch_url(self):
 
111
        if self._mp.source_branch:
 
112
            return self._mp.source_branch.bzr_identity
 
113
        else:
 
114
            branch_name = ref_to_branch_name(
 
115
                self._mp.source_git_path.encode('utf-8'))
 
116
            return urlutils.join_segment_parameters(
 
117
                self._mp.source_git_repository.git_identity,
 
118
                {"branch": branch_name})
 
119
 
 
120
    def get_target_branch_url(self):
 
121
        if self._mp.target_branch:
 
122
            return self._mp.target_branch.bzr_identity
 
123
        else:
 
124
            branch_name = ref_to_branch_name(
 
125
                self._mp.target_git_path.encode('utf-8'))
 
126
            return urlutils.join_segment_parameters(
 
127
                self._mp.target_git_repository.git_identity,
 
128
                {"branch": branch_name})
 
129
 
106
130
    @property
107
131
    def url(self):
108
132
        return lp_api.canonical_url(self._mp)
114
138
class Launchpad(Hoster):
115
139
    """The Launchpad hosting service."""
116
140
 
 
141
    name = 'launchpad'
 
142
 
117
143
    # https://bugs.launchpad.net/launchpad/+bug/397676
118
144
    supports_merge_proposal_labels = False
119
145
 
142
168
        url, params = urlutils.split_segment_parameters(branch.user_url)
143
169
        (scheme, user, password, host, port, path) = urlutils.parse_url(
144
170
            url)
145
 
        repo_lp = self.launchpad.git_repositories.getByPath(path=path.strip('/'))
 
171
        repo_lp = self.launchpad.git_repositories.getByPath(
 
172
            path=path.strip('/'))
146
173
        try:
147
174
            ref_path = params['ref']
148
175
        except KeyError:
155
182
        return (repo_lp, ref_lp)
156
183
 
157
184
    def _get_lp_bzr_branch_from_branch(self, branch):
158
 
        return self.launchpad.branches.getByUrl(url=urlutils.unescape(branch.user_url))
 
185
        return self.launchpad.branches.getByUrl(
 
186
            url=urlutils.unescape(branch.user_url))
159
187
 
160
188
    def _get_derived_git_path(self, base_path, owner, project):
161
189
        base_repo = self.launchpad.git_repositories.getByPath(path=base_path)
162
190
        if project is None:
163
191
            project = '/'.join(base_repo.unique_name.split('/')[1:])
164
 
        # TODO(jelmer): Surely there is a better way of creating one of these URLs?
 
192
        # TODO(jelmer): Surely there is a better way of creating one of these
 
193
        # URLs?
165
194
        return "~%s/%s" % (owner, project)
166
195
 
167
196
    def _publish_git(self, local_branch, base_path, name, owner, project=None,
177
206
        if dir_to is None:
178
207
            try:
179
208
                br_to = local_branch.create_clone_on_transport(
180
 
                    to_transport, revision_id=revision_id, name=name,
181
 
                    stacked_on=main_branch.user_url)
 
209
                    to_transport, revision_id=revision_id, name=name)
182
210
            except errors.NoRoundtrippingSupport:
183
211
                br_to = local_branch.create_clone_on_transport(
184
212
                    to_transport, revision_id=revision_id, name=name,
185
 
                    stacked_on=main_branch.user_url, lossy=True)
 
213
                    lossy=True)
186
214
        else:
187
215
            try:
188
 
                dir_to = dir_to.push_branch(local_branch, revision_id, overwrite=overwrite, name=name)
 
216
                dir_to = dir_to.push_branch(
 
217
                    local_branch, revision_id, overwrite=overwrite, name=name)
189
218
            except errors.NoRoundtrippingSupport:
190
219
                if not allow_lossy:
191
220
                    raise
192
 
                dir_to = dir_to.push_branch(local_branch, revision_id, overwrite=overwrite, name=name, lossy=True)
 
221
                dir_to = dir_to.push_branch(
 
222
                    local_branch, revision_id, overwrite=overwrite, name=name,
 
223
                    lossy=True)
193
224
            br_to = dir_to.target_branch
194
 
        return br_to, ("https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
 
225
        return br_to, (
 
226
            "https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
195
227
 
196
228
    def _get_derived_bzr_path(self, base_branch, name, owner, project):
197
229
        if project is None:
198
230
            base_branch_lp = self._get_lp_bzr_branch_from_branch(base_branch)
199
231
            project = '/'.join(base_branch_lp.unique_name.split('/')[1:-1])
200
 
        # TODO(jelmer): Surely there is a better way of creating one of these URLs?
 
232
        # TODO(jelmer): Surely there is a better way of creating one of these
 
233
        # URLs?
201
234
        return "~%s/%s/%s" % (owner, project, name)
202
235
 
203
236
    def get_push_url(self, branch):
211
244
        else:
212
245
            raise AssertionError
213
246
 
214
 
    def _publish_bzr(self, local_branch, base_branch, name, owner, project=None,
215
 
                     revision_id=None, overwrite=False, allow_lossy=True):
 
247
    def _publish_bzr(self, local_branch, base_branch, name, owner,
 
248
                     project=None, revision_id=None, overwrite=False,
 
249
                     allow_lossy=True):
216
250
        to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
217
251
        to_transport = get_transport("lp:" + to_path)
218
252
        try:
222
256
            dir_to = None
223
257
 
224
258
        if dir_to is None:
225
 
            br_to = local_branch.create_clone_on_transport(to_transport, revision_id=revision_id)
 
259
            br_to = local_branch.create_clone_on_transport(
 
260
                to_transport, revision_id=revision_id)
226
261
        else:
227
 
            br_to = dir_to.push_branch(local_branch, revision_id, overwrite=overwrite).target_branch
 
262
            br_to = dir_to.push_branch(
 
263
                local_branch, revision_id, overwrite=overwrite).target_branch
228
264
        return br_to, ("https://code.launchpad.net/" + to_path)
229
265
 
230
266
    def _split_url(self, url):
239
275
            raise ValueError("unknown host %s" % host)
240
276
        return (vcs, user, password, path, params)
241
277
 
242
 
    def publish_derived(self, local_branch, base_branch, name, project=None, owner=None,
243
 
                        revision_id=None, overwrite=False, allow_lossy=True):
 
278
    def publish_derived(self, local_branch, base_branch, name, project=None,
 
279
                        owner=None, revision_id=None, overwrite=False,
 
280
                        allow_lossy=True):
244
281
        """Publish a branch to the site, derived from base_branch.
245
282
 
246
283
        :param base_branch: branch to derive the new branch from
252
289
        """
253
290
        if owner is None:
254
291
            owner = self.launchpad.me.name
255
 
        (base_vcs, base_user, base_password, base_path, base_params) = self._split_url(
256
 
            base_branch.user_url)
 
292
        (base_vcs, base_user, base_password, base_path,
 
293
            base_params) = self._split_url(base_branch.user_url)
257
294
        # TODO(jelmer): Prevent publishing to development focus
258
295
        if base_vcs == 'bzr':
259
296
            return self._publish_bzr(
271
308
    def get_derived_branch(self, base_branch, name, project=None, owner=None):
272
309
        if owner is None:
273
310
            owner = self.launchpad.me.name
274
 
        (base_vcs, base_user, base_password, base_path, base_params) = self._split_url(
275
 
            base_branch.user_url)
 
311
        (base_vcs, base_user, base_password, base_path,
 
312
            base_params) = self._split_url(base_branch.user_url)
276
313
        if base_vcs == 'bzr':
277
 
            to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
 
314
            to_path = self._get_derived_bzr_path(
 
315
                base_branch, name, owner, project)
278
316
            return _mod_branch.Branch.open("lp:" + to_path)
279
317
        elif base_vcs == 'git':
280
318
            to_path = self._get_derived_git_path(
284
322
        else:
285
323
            raise AssertionError('not a valid Launchpad URL')
286
324
 
287
 
    def get_proposal(self, source_branch, target_branch):
288
 
        (base_vcs, base_user, base_password, base_path, base_params) = (
289
 
            self._split_url(target_branch.user_url))
 
325
    def iter_proposals(self, source_branch, target_branch, status='open'):
 
326
        (base_vcs, base_user, base_password, base_path,
 
327
            base_params) = self._split_url(target_branch.user_url)
 
328
        statuses = status_to_lp_mp_statuses(status)
290
329
        if base_vcs == 'bzr':
291
330
            target_branch_lp = self.launchpad.branches.getByUrl(
292
331
                url=target_branch.user_url)
293
332
            source_branch_lp = self.launchpad.branches.getByUrl(
294
333
                url=source_branch.user_url)
295
 
            for mp in target_branch_lp.getMergeProposals(
296
 
                    status=MERGE_PROPOSAL_STATUSES):
297
 
                if mp.target_branch != target_branch_lp:
298
 
                    continue
299
 
                if mp.source_branch != source_branch_lp:
300
 
                    continue
301
 
                return LaunchpadMergeProposal(mp)
302
 
            raise NoMergeProposal()
 
334
            for mp in target_branch_lp.getMergeProposals(status=statuses):
 
335
                if mp.source_branch_link != source_branch_lp.self_link:
 
336
                    continue
 
337
                yield LaunchpadMergeProposal(mp)
303
338
        elif base_vcs == 'git':
304
339
            (source_repo_lp, source_branch_lp) = (
305
340
                self.lp_host._get_lp_git_ref_from_branch(source_branch))
306
341
            (target_repo_lp, target_branch_lp) = (
307
342
                self.lp_host._get_lp_git_ref_from_branch(target_branch))
308
 
            for mp in target_branch_lp.getMergeProposals(
309
 
                    status=MERGE_PROPOSAL_STATUSES):
 
343
            for mp in target_branch_lp.getMergeProposals(status=statuses):
310
344
                if (target_branch_lp.path != mp.target_git_path or
311
345
                        target_repo_lp != mp.target_git_repository or
312
346
                        source_branch_lp.path != mp.source_git_path or
313
347
                        source_repo_lp != mp.source_git_repository):
314
348
                    continue
315
 
                return LaunchpadMergeProposal(mp)
316
 
            raise NoMergeProposal()
 
349
                yield LaunchpadMergeProposal(mp)
317
350
        else:
318
351
            raise AssertionError('not a valid Launchpad URL')
319
352
 
320
353
    def get_proposer(self, source_branch, target_branch):
321
 
        (base_vcs, base_user, base_password, base_path, base_params) = (
322
 
            self._split_url(target_branch.user_url))
 
354
        (base_vcs, base_user, base_password, base_path,
 
355
            base_params) = self._split_url(target_branch.user_url)
323
356
        if base_vcs == 'bzr':
324
357
            return LaunchpadBazaarMergeProposalBuilder(
325
358
                self, source_branch, target_branch)
329
362
        else:
330
363
            raise AssertionError('not a valid Launchpad URL')
331
364
 
 
365
    @classmethod
 
366
    def iter_instances(cls):
 
367
        yield cls()
 
368
 
 
369
    def iter_my_proposals(self, status='open'):
 
370
        statuses = status_to_lp_mp_statuses(status)
 
371
        for mp in self.launchpad.me.getMergeProposals(status=statuses):
 
372
            yield LaunchpadMergeProposal(mp)
 
373
 
332
374
 
333
375
def connect_launchpad(lp_instance='production'):
334
376
    service = lp_registration.LaunchpadService(lp_instance=lp_instance)
354
396
        self.lp_host = lp_host
355
397
        self.launchpad = lp_host.launchpad
356
398
        self.source_branch = source_branch
357
 
        self.source_branch_lp = self.launchpad.branches.getByUrl(url=source_branch.user_url)
 
399
        self.source_branch_lp = self.launchpad.branches.getByUrl(
 
400
            url=source_branch.user_url)
358
401
        if target_branch is None:
359
402
            self.target_branch_lp = self.source_branch_lp.get_target()
360
 
            self.target_branch = _mod_branch.Branch.open(self.target_branch_lp.bzr_identity)
 
403
            self.target_branch = _mod_branch.Branch.open(
 
404
                self.target_branch_lp.bzr_identity)
361
405
        else:
362
406
            self.target_branch = target_branch
363
 
            self.target_branch_lp = self.launchpad.branches.getByUrl(url=target_branch.user_url)
 
407
            self.target_branch_lp = self.launchpad.branches.getByUrl(
 
408
                url=target_branch.user_url)
364
409
        self.commit_message = message
365
410
        self.approve = approve
366
411
        self.fixes = fixes
414
459
            _call_webservice(
415
460
                mp.createComment,
416
461
                vote=u'Approve',
417
 
                subject='', # Use the default subject.
 
462
                subject='',  # Use the default subject.
418
463
                content=u"Rubberstamp! Proposer approves of own proposal.")
419
464
            _call_webservice(mp.setStatus, status=u'Approved',
420
465
                             revid=self.source_branch.last_revision())
478
523
        self.lp_host = lp_host
479
524
        self.launchpad = lp_host.launchpad
480
525
        self.source_branch = source_branch
481
 
        (self.source_repo_lp, self.source_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(source_branch)
 
526
        (self.source_repo_lp,
 
527
            self.source_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(
 
528
                source_branch)
482
529
        if target_branch is None:
483
530
            self.target_branch_lp = self.source_branch.get_target()
484
 
            self.target_branch = _mod_branch.Branch.open(self.target_branch_lp.git_https_url)
 
531
            self.target_branch = _mod_branch.Branch.open(
 
532
                self.target_branch_lp.git_https_url)
485
533
        else:
486
534
            self.target_branch = target_branch
487
 
            (self.target_repo_lp, self.target_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(target_branch)
 
535
            (self.target_repo_lp, self.target_branch_lp) = (
 
536
                self.lp_host._get_lp_git_ref_from_branch(target_branch))
488
537
        self.commit_message = message
489
538
        self.approve = approve
490
539
        self.fixes = fixes
538
587
            _call_webservice(
539
588
                mp.createComment,
540
589
                vote=u'Approve',
541
 
                subject='', # Use the default subject.
 
590
                subject='',  # Use the default subject.
542
591
                content=u"Rubberstamp! Proposer approves of own proposal.")
543
592
            _call_webservice(
544
593
                mp.setStatus, status=u'Approved',
550
599
        if labels:
551
600
            raise LabelsUnsupported()
552
601
        if prerequisite_branch is not None:
553
 
            (prereq_repo_lp, prereq_branch_lp) = self.lp_host._get_lp_git_ref_from_branch(prerequisite_branch)
 
602
            (prereq_repo_lp, prereq_branch_lp) = (
 
603
                self.lp_host._get_lp_git_ref_from_branch(prerequisite_branch))
554
604
        else:
555
605
            prereq_branch_lp = None
556
606
        if reviewers is None: