51
51
# TODO(jelmer): Make selection of launchpad staging a configuration option.
53
MERGE_PROPOSAL_STATUSES = [
59
'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')
65
69
def plausible_launchpad_url(url):
103
107
def __init__(self, mp):
110
def get_source_branch_url(self):
111
if self._mp.source_branch:
112
return self._mp.source_branch.bzr_identity
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})
120
def get_target_branch_url(self):
121
if self._mp.target_branch:
122
return self._mp.target_branch.bzr_identity
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})
108
132
return lp_api.canonical_url(self._mp)
155
182
return (repo_lp, ref_lp)
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))
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
165
194
return "~%s/%s" % (owner, project)
167
196
def _publish_git(self, local_branch, base_path, name, owner, project=None,
177
206
if dir_to is None:
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)
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:
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,
193
224
br_to = dir_to.target_branch
194
return br_to, ("https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
226
"https://git.launchpad.net/%s/+ref/%s" % (to_path, name))
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
201
234
return "~%s/%s/%s" % (owner, project, name)
203
236
def get_push_url(self, branch):
212
245
raise AssertionError
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,
216
250
to_path = self._get_derived_bzr_path(base_branch, name, owner, project)
217
251
to_transport = get_transport("lp:" + to_path)
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)
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)
230
266
def _split_url(self, url):
239
275
raise ValueError("unknown host %s" % host)
240
276
return (vcs, user, password, path, params)
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,
244
281
"""Publish a branch to the site, derived from base_branch.
246
283
:param base_branch: branch to derive the new branch from
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(
285
323
raise AssertionError('not a valid Launchpad URL')
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:
299
if mp.source_branch != source_branch_lp:
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:
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):
315
return LaunchpadMergeProposal(mp)
316
raise NoMergeProposal()
349
yield LaunchpadMergeProposal(mp)
318
351
raise AssertionError('not a valid Launchpad URL')
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)
330
363
raise AssertionError('not a valid Launchpad URL')
366
def iter_instances(cls):
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)
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)
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
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(
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)
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