52
51
# TODO(jelmer): Make selection of launchpad staging a configuration option.
54
MERGE_PROPOSAL_STATUSES = [
60
'Code failed to merge',
53
def status_to_lp_mp_statuses(status):
55
if status in ('open', 'all'):
60
'Code failed to merge',
62
if status in ('closed', 'all'):
63
statuses.extend(['Rejected', 'Superseded'])
64
if status in ('merged', 'all'):
65
statuses.append('Merged')
66
69
def plausible_launchpad_url(url):
108
111
if self._mp.source_branch:
109
112
return self._mp.source_branch.bzr_identity
114
branch_name = ref_to_branch_name(
115
self._mp.source_git_path.encode('utf-8'))
111
116
return urlutils.join_segment_parameters(
112
117
self._mp.source_git_repository.git_identity,
113
{"branch": ref_to_branch_name(self._mp.source_git_path.encode('utf-8'))})
118
{"branch": branch_name})
115
120
def get_target_branch_url(self):
116
121
if self._mp.target_branch:
117
122
return self._mp.target_branch.bzr_identity
124
branch_name = ref_to_branch_name(
125
self._mp.target_git_path.encode('utf-8'))
119
126
return urlutils.join_segment_parameters(
120
127
self._mp.target_git_repository.git_identity,
121
{"branch": ref_to_branch_name(self._mp.target_git_path.encode('utf-8'))})
128
{"branch": branch_name})
174
182
return (repo_lp, ref_lp)
176
184
def _get_lp_bzr_branch_from_branch(self, branch):
177
return self.launchpad.branches.getByUrl(url=urlutils.unescape(branch.user_url))
185
return self.launchpad.branches.getByUrl(
186
url=urlutils.unescape(branch.user_url))
179
188
def _get_derived_git_path(self, base_path, owner, project):
180
189
base_repo = self.launchpad.git_repositories.getByPath(path=base_path)
181
190
if project is None:
182
191
project = '/'.join(base_repo.unique_name.split('/')[1:])
183
# 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
184
194
return "~%s/%s" % (owner, project)
186
196
def _publish_git(self, local_branch, base_path, name, owner, project=None,
206
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)
207
218
except errors.NoRoundtrippingSupport:
208
219
if not allow_lossy:
210
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,
211
224
br_to = dir_to.target_branch
212
return br_to, ("https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
226
"https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
214
228
def _get_derived_bzr_path(self, base_branch, name, owner, project):
215
229
if project is None:
216
230
base_branch_lp = self._get_lp_bzr_branch_from_branch(base_branch)
217
231
project = '/'.join(base_branch_lp.unique_name.split('/')[1:-1])
218
# 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
219
234
return "~%s/%s/%s" % (owner, project, name)
221
236
def get_push_url(self, branch):
230
245
raise AssertionError
232
def _publish_bzr(self, local_branch, base_branch, name, owner, project=None,
233
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,
234
250
to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
235
251
to_transport = get_transport("lp:" + to_path)
242
258
if dir_to is None:
243
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)
245
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
246
264
return br_to, ("https://code.launchpad.net/" + to_path)
248
266
def _split_url(self, url):
257
275
raise ValueError("unknown host %s" % host)
258
276
return (vcs, user, password, path, params)
260
def publish_derived(self, local_branch, base_branch, name, project=None, owner=None,
261
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,
262
281
"""Publish a branch to the site, derived from base_branch.
264
283
:param base_branch: branch to derive the new branch from
271
290
if owner is None:
272
291
owner = self.launchpad.me.name
273
(base_vcs, base_user, base_password, base_path, base_params) = self._split_url(
274
base_branch.user_url)
292
(base_vcs, base_user, base_password, base_path,
293
base_params) = self._split_url(base_branch.user_url)
275
294
# TODO(jelmer): Prevent publishing to development focus
276
295
if base_vcs == 'bzr':
277
296
return self._publish_bzr(
289
308
def get_derived_branch(self, base_branch, name, project=None, owner=None):
290
309
if owner is None:
291
310
owner = self.launchpad.me.name
292
(base_vcs, base_user, base_password, base_path, base_params) = self._split_url(
293
base_branch.user_url)
311
(base_vcs, base_user, base_password, base_path,
312
base_params) = self._split_url(base_branch.user_url)
294
313
if base_vcs == 'bzr':
295
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)
296
316
return _mod_branch.Branch.open("lp:" + to_path)
297
317
elif base_vcs == 'git':
298
318
to_path = self._get_derived_git_path(
303
323
raise AssertionError('not a valid Launchpad URL')
305
def get_proposal(self, source_branch, target_branch):
306
(base_vcs, base_user, base_password, base_path, base_params) = (
307
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)
308
329
if base_vcs == 'bzr':
309
330
target_branch_lp = self.launchpad.branches.getByUrl(
310
331
url=target_branch.user_url)
311
332
source_branch_lp = self.launchpad.branches.getByUrl(
312
333
url=source_branch.user_url)
313
for mp in target_branch_lp.getMergeProposals(
314
status=MERGE_PROPOSAL_STATUSES):
315
if mp.target_branch != target_branch_lp:
317
if mp.source_branch != source_branch_lp:
319
return LaunchpadMergeProposal(mp)
320
raise NoMergeProposal()
334
for mp in target_branch_lp.getMergeProposals(status=statuses):
335
if mp.source_branch_link != source_branch_lp.self_link:
337
yield LaunchpadMergeProposal(mp)
321
338
elif base_vcs == 'git':
322
339
(source_repo_lp, source_branch_lp) = (
323
340
self.lp_host._get_lp_git_ref_from_branch(source_branch))
324
341
(target_repo_lp, target_branch_lp) = (
325
342
self.lp_host._get_lp_git_ref_from_branch(target_branch))
326
for mp in target_branch_lp.getMergeProposals(
327
status=MERGE_PROPOSAL_STATUSES):
343
for mp in target_branch_lp.getMergeProposals(status=statuses):
328
344
if (target_branch_lp.path != mp.target_git_path or
329
345
target_repo_lp != mp.target_git_repository or
330
346
source_branch_lp.path != mp.source_git_path or
331
347
source_repo_lp != mp.source_git_repository):
333
return LaunchpadMergeProposal(mp)
334
raise NoMergeProposal()
349
yield LaunchpadMergeProposal(mp)
336
351
raise AssertionError('not a valid Launchpad URL')
338
353
def get_proposer(self, source_branch, target_branch):
339
(base_vcs, base_user, base_password, base_path, base_params) = (
340
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)
341
356
if base_vcs == 'bzr':
342
357
return LaunchpadBazaarMergeProposalBuilder(
343
358
self, source_branch, target_branch)
354
369
def iter_my_proposals(self, status='open'):
356
if status in ('open', 'all'):
361
'Code failed to merge',
363
if status in ('closed', 'all'):
364
statuses.extend(['Rejected', 'Superseded'])
365
if status in ('merged', 'all'):
366
statuses.append('Merged')
370
statuses = status_to_lp_mp_statuses(status)
367
371
for mp in self.launchpad.me.getMergeProposals(status=statuses):
368
372
yield LaunchpadMergeProposal(mp)
392
396
self.lp_host = lp_host
393
397
self.launchpad = lp_host.launchpad
394
398
self.source_branch = source_branch
395
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)
396
401
if target_branch is None:
397
402
self.target_branch_lp = self.source_branch_lp.get_target()
398
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)
400
406
self.target_branch = target_branch
401
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)
402
409
self.commit_message = message
403
410
self.approve = approve
404
411
self.fixes = fixes
516
523
self.lp_host = lp_host
517
524
self.launchpad = lp_host.launchpad
518
525
self.source_branch = source_branch
519
(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(
520
529
if target_branch is None:
521
530
self.target_branch_lp = self.source_branch.get_target()
522
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)
524
534
self.target_branch = target_branch
525
(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))
526
537
self.commit_message = message
527
538
self.approve = approve
528
539
self.fixes = fixes