1
# Copyright (C) 2009-2012 Canonical Ltd
1
# Copyright (C) 2009, 2010 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
17
17
"""Tools for dealing with the Launchpad API."""
19
from __future__ import absolute_import
21
19
# Importing this module will be expensive, since it imports launchpadlib and
22
20
# its dependencies. However, our plan is to only load this module when it is
23
21
# needed by a command that uses it.
50
46
STAGING_SERVICE_ROOT,
53
from launchpadlib import uris
55
51
# Declare the minimum version of launchpadlib that we need in order to work.
56
# 1.6.0 is the version of launchpadlib packaged in Ubuntu 10.04, the most
57
# recent Ubuntu LTS release supported on the desktop at the time of writing.
58
MINIMUM_LAUNCHPADLIB_VERSION = (1, 6, 0)
52
# 1.5.1 is the version of launchpadlib packaged in Ubuntu 9.10, the most
53
# recent Ubuntu release at the time of writing.
54
MINIMUM_LAUNCHPADLIB_VERSION = (1, 5, 1)
61
57
def get_cache_directory():
77
73
installed_version, installed_version)
80
def lookup_service_root(service_root):
82
return uris.lookup_service_root(service_root)
84
if service_root != 'qastaging':
86
staging_root = uris.lookup_service_root('staging')
87
return staging_root.replace('staging', 'qastaging')
76
# The older versions of launchpadlib only provided service root constants for
77
# edge and staging, whilst newer versions drop edge. Therefore service root
78
# URIs for which we do not always have constants are derived from the staging
79
# one, which does always exist.
81
# It is necessary to derive, rather than use hardcoded URIs because
82
# launchpadlib <= 1.5.4 requires service root URIs that end in a path of
83
# /beta/, whilst launchpadlib >= 1.5.5 requires service root URIs with no path
86
# Once we have a hard dependency on launchpadlib >= 1.5.4 we can replace all of
87
# bzr's local knowledge of individual Launchpad instances with use of the
88
# launchpadlib.uris module.
89
LAUNCHPAD_API_URLS = {
90
'production': STAGING_SERVICE_ROOT.replace('api.staging.launchpad.net',
92
'qastaging': STAGING_SERVICE_ROOT.replace('api.staging.launchpad.net',
93
'api.qastaging.launchpad.net'),
94
'staging': STAGING_SERVICE_ROOT,
95
'dev': STAGING_SERVICE_ROOT.replace('api.staging.launchpad.net',
90
100
def _get_api_url(service):
113
123
errors.BzrError.__init__(self, branch=branch, url=branch.base)
116
def login(service, timeout=None, proxy_info=None,
117
version=Launchpad.DEFAULT_VERSION):
126
def login(service, timeout=None, proxy_info=None):
118
127
"""Log in to the Launchpad API.
120
129
:return: The root `Launchpad` object from launchpadlib.
122
if proxy_info is None:
123
proxy_info = httplib2.proxy_info_from_environment('https')
124
131
cache_directory = get_cache_directory()
125
132
launchpad = Launchpad.login_with(
126
133
'bzr', _get_api_url(service), cache_directory, timeout=timeout,
127
proxy_info=proxy_info, version=version)
128
# XXX: Work-around a minor security bug in launchpadlib < 1.6.3, which
129
# would create this directory with default umask.
130
osutils.chmod_if_possible(cache_directory, 0700)
134
proxy_info=proxy_info)
135
# XXX: Work-around a minor security bug in launchpadlib 1.5.1, which would
136
# create this directory with default umask.
137
os.chmod(cache_directory, 0700)
200
207
if str(launchpad._root_uri) == STAGING_SERVICE_ROOT:
201
208
return url.replace('bazaar.launchpad.net',
202
209
'bazaar.staging.launchpad.net')
203
elif str(launchpad._root_uri) == lookup_service_root('qastaging'):
210
elif str(launchpad._root_uri) == LAUNCHPAD_API_URLS['qastaging']:
204
211
return url.replace('bazaar.launchpad.net',
205
212
'bazaar.qastaging.launchpad.net')
228
235
"""Create a Bazaar branch on Launchpad for the supplied branch."""
229
236
url = cls.tweak_url(bzr_branch.get_push_location(), launchpad)
230
237
if not cls.plausible_launchpad_url(url):
231
raise errors.BzrError(gettext('%s is not registered on Launchpad') %
238
raise errors.BzrError('%s is not registered on Launchpad' %
233
240
bzr_branch.create_clone_on_transport(transport.get_transport(url))
234
241
lp_branch = launchpad.branches.getByUrl(url=url)
235
242
if lp_branch is None:
236
raise errors.BzrError(gettext('%s is not registered on Launchpad') %
243
raise errors.BzrError('%s is not registered on Launchpad' % url)
240
246
def get_target(self):
243
249
if lp_branch.project is not None:
244
250
dev_focus = lp_branch.project.development_focus
245
251
if dev_focus is None:
246
raise errors.BzrError(gettext('%s has no development focus.') %
252
raise errors.BzrError('%s has no development focus.' %
247
253
lp_branch.bzr_identity)
248
254
target = dev_focus.branch
249
255
if target is None:
250
raise errors.BzrError(gettext(
251
'development focus %s has no branch.') % dev_focus)
256
raise errors.BzrError('development focus %s has no branch.' % dev_focus)
252
257
elif lp_branch.sourcepackage is not None:
253
258
target = lp_branch.sourcepackage.getBranch(pocket="Release")
254
259
if target is None:
255
raise errors.BzrError(gettext(
256
'source package %s has no branch.') %
260
raise errors.BzrError('source package %s has no branch.' %
257
261
lp_branch.sourcepackage)
259
raise errors.BzrError(gettext(
260
'%s has no associated product or source package.') %
263
raise errors.BzrError('%s has no associated product or source package.' %
261
264
lp_branch.bzr_identity)
262
265
return LaunchpadBranch(target, target.bzr_identity)
270
273
if self.lp.last_scanned_id is not None:
271
274
if self.bzr.last_revision() == self.lp.last_scanned_id:
272
trace.note(gettext('%s is already up-to-date.') %
275
trace.note('%s is already up-to-date.' %
273
276
self.lp.bzr_identity)
275
278
graph = self.bzr.repository.get_graph()
276
279
if not graph.is_ancestor(self.lp.last_scanned_id,
277
280
self.bzr.last_revision()):
278
281
raise errors.DivergedBranches(self.bzr, self.push_bzr)
279
trace.note(gettext('Pushing to %s') % self.lp.bzr_identity)
282
trace.note('Pushing to %s' % self.lp.bzr_identity)
280
283
self.bzr.push(self.push_bzr)
282
285
self.bzr.unlock()