~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/launchpad/lp_api.py

  • Committer: Martin Pool
  • Date: 2010-02-25 06:17:27 UTC
  • mfrom: (5055 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5057.
  • Revision ID: mbp@sourcefrog.net-20100225061727-4sd9lt0qmdc6087t
merge news

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2010 Canonical Ltd
2
2
#
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
23
23
 
24
24
import os
25
25
import re
26
 
import urlparse
27
26
 
28
27
from bzrlib import (
29
28
    branch,
33
32
    trace,
34
33
    transport,
35
34
    )
36
 
from bzrlib.i18n import gettext
37
35
from bzrlib.plugins.launchpad.lp_registration import (
38
36
    InvalidLaunchpadInstance,
 
37
    NotLaunchpadBranch,
39
38
    )
40
39
 
41
40
try:
44
43
    raise errors.DependencyNotPresent('launchpadlib', e)
45
44
 
46
45
from launchpadlib.launchpad import (
 
46
    EDGE_SERVICE_ROOT,
47
47
    STAGING_SERVICE_ROOT,
48
48
    Launchpad,
49
49
    )
74
74
            installed_version, installed_version)
75
75
 
76
76
 
77
 
# The older versions of launchpadlib only provided service root constants for
78
 
# edge and staging, whilst newer versions drop edge. Therefore service root
79
 
# URIs for which we do not always have constants are derived from the staging
80
 
# one, which does always exist.
81
 
#
82
 
# It is necessary to derive, rather than use hardcoded URIs because
83
 
# launchpadlib <= 1.5.4 requires service root URIs that end in a path of
84
 
# /beta/, whilst launchpadlib >= 1.5.5 requires service root URIs with no path
85
 
# info.
86
 
#
87
 
# Once we have a hard dependency on launchpadlib >= 1.5.4 we can replace all of
88
 
# bzr's local knowledge of individual Launchpad instances with use of the
89
 
# launchpadlib.uris module.
90
77
LAUNCHPAD_API_URLS = {
91
 
    'production': STAGING_SERVICE_ROOT.replace('api.staging.launchpad.net',
92
 
        'api.launchpad.net'),
93
 
    'qastaging': STAGING_SERVICE_ROOT.replace('api.staging.launchpad.net',
94
 
        'api.qastaging.launchpad.net'),
 
78
    'production': 'https://api.launchpad.net/beta/',
 
79
    'edge': EDGE_SERVICE_ROOT,
95
80
    'staging': STAGING_SERVICE_ROOT,
96
 
    'dev': STAGING_SERVICE_ROOT.replace('api.staging.launchpad.net',
97
 
        'api.launchpad.dev'),
 
81
    'dev': 'https://api.launchpad.dev/beta/',
98
82
    }
99
83
 
100
84
 
101
85
def _get_api_url(service):
102
86
    """Return the root URL of the Launchpad API.
103
87
 
104
 
    e.g. For the 'staging' Launchpad service, this function returns
105
 
    launchpadlib.launchpad.STAGING_SERVICE_ROOT.
 
88
    e.g. For the 'edge' Launchpad service, this function returns
 
89
    launchpadlib.launchpad.EDGE_SERVICE_ROOT.
106
90
 
107
91
    :param service: A `LaunchpadService` object.
108
92
    :return: A URL as a string.
117
101
        raise InvalidLaunchpadInstance(lp_instance)
118
102
 
119
103
 
120
 
class NoLaunchpadBranch(errors.BzrError):
121
 
    _fmt = 'No launchpad branch could be found for branch "%(url)s".'
122
 
 
123
 
    def __init__(self, branch):
124
 
        errors.BzrError.__init__(self, branch=branch, url=branch.base)
125
 
 
126
 
 
127
104
def login(service, timeout=None, proxy_info=None):
128
105
    """Log in to the Launchpad API.
129
106
 
197
174
        url = bzr_branch.get_push_location()
198
175
        if url is not None:
199
176
            yield url
200
 
        url = bzr_branch.get_parent()
201
 
        if url is not None:
202
 
            yield url
203
177
        yield bzr_branch.base
204
178
 
205
179
    @staticmethod
206
180
    def tweak_url(url, launchpad):
207
181
        """Adjust a URL to work with staging, if needed."""
208
 
        if str(launchpad._root_uri) == STAGING_SERVICE_ROOT:
209
 
            return url.replace('bazaar.launchpad.net',
210
 
                               'bazaar.staging.launchpad.net')
211
 
        elif str(launchpad._root_uri) == LAUNCHPAD_API_URLS['qastaging']:
212
 
            return url.replace('bazaar.launchpad.net',
213
 
                               'bazaar.qastaging.launchpad.net')
214
 
        return url
 
182
        if str(launchpad._root_uri) != STAGING_SERVICE_ROOT:
 
183
            return url
 
184
        if url is None:
 
185
            return None
 
186
        return url.replace('bazaar.launchpad.net',
 
187
                           'bazaar.staging.launchpad.net')
215
188
 
216
189
    @classmethod
217
 
    def from_bzr(cls, launchpad, bzr_branch, create_missing=True):
 
190
    def from_bzr(cls, launchpad, bzr_branch):
218
191
        """Find a Launchpad branch from a bzr branch."""
219
192
        check_update = True
220
193
        for url in cls.candidate_urls(bzr_branch):
225
198
            if lp_branch is not None:
226
199
                break
227
200
        else:
228
 
            if not create_missing:
229
 
                raise NoLaunchpadBranch(bzr_branch)
230
201
            lp_branch = cls.create_now(launchpad, bzr_branch)
231
202
            check_update = False
232
203
        return cls(lp_branch, bzr_branch.base, bzr_branch, check_update)
236
207
        """Create a Bazaar branch on Launchpad for the supplied branch."""
237
208
        url = cls.tweak_url(bzr_branch.get_push_location(), launchpad)
238
209
        if not cls.plausible_launchpad_url(url):
239
 
            raise errors.BzrError(gettext('%s is not registered on Launchpad') %
 
210
            raise errors.BzrError('%s is not registered on Launchpad' %
240
211
                                  bzr_branch.base)
241
212
        bzr_branch.create_clone_on_transport(transport.get_transport(url))
242
213
        lp_branch = launchpad.branches.getByUrl(url=url)
243
214
        if lp_branch is None:
244
 
            raise errors.BzrError(gettext('%s is not registered on Launchpad') %
245
 
                                                                            url)
 
215
            raise errors.BzrError('%s is not registered on Launchpad' % url)
246
216
        return lp_branch
247
217
 
248
 
    def get_target(self):
249
 
        """Return the 'LaunchpadBranch' for the target of this one."""
 
218
    def get_dev_focus(self):
 
219
        """Return the 'LaunchpadBranch' for the dev focus of this one."""
250
220
        lp_branch = self.lp
251
 
        if lp_branch.project is not None:
252
 
            dev_focus = lp_branch.project.development_focus
253
 
            if dev_focus is None:
254
 
                raise errors.BzrError(gettext('%s has no development focus.') %
255
 
                                  lp_branch.bzr_identity)
256
 
            target = dev_focus.branch
257
 
            if target is None:
258
 
                raise errors.BzrError(gettext(
259
 
                        'development focus %s has no branch.') % dev_focus)
260
 
        elif lp_branch.sourcepackage is not None:
261
 
            target = lp_branch.sourcepackage.getBranch(pocket="Release")
262
 
            if target is None:
263
 
                raise errors.BzrError(gettext(
264
 
                                      'source package %s has no branch.') %
265
 
                                      lp_branch.sourcepackage)
266
 
        else:
267
 
            raise errors.BzrError(gettext(
268
 
                        '%s has no associated product or source package.') %
269
 
                                  lp_branch.bzr_identity)
270
 
        return LaunchpadBranch(target, target.bzr_identity)
 
221
        if lp_branch.project is None:
 
222
            raise errors.BzrError('%s has no product.' %
 
223
                                  lp_branch.bzr_identity)
 
224
        dev_focus = lp_branch.project.development_focus.branch
 
225
        if dev_focus is None:
 
226
            raise errors.BzrError('%s has no development focus.' %
 
227
                                  lp_branch.bzr_identity)
 
228
        return LaunchpadBranch(dev_focus, dev_focus.bzr_identity)
271
229
 
272
230
    def update_lp(self):
273
231
        """Update the Launchpad copy of this branch."""
277
235
        try:
278
236
            if self.lp.last_scanned_id is not None:
279
237
                if self.bzr.last_revision() == self.lp.last_scanned_id:
280
 
                    trace.note(gettext('%s is already up-to-date.') %
 
238
                    trace.note('%s is already up-to-date.' %
281
239
                               self.lp.bzr_identity)
282
240
                    return
283
241
                graph = self.bzr.repository.get_graph()
284
242
                if not graph.is_ancestor(self.lp.last_scanned_id,
285
243
                                         self.bzr.last_revision()):
286
244
                    raise errors.DivergedBranches(self.bzr, self.push_bzr)
287
 
                trace.note(gettext('Pushing to %s') % self.lp.bzr_identity)
 
245
                trace.note('Pushing to %s' % self.lp.bzr_identity)
288
246
            self.bzr.push(self.push_bzr)
289
247
        finally:
290
248
            self.bzr.unlock()
301
259
        return self.bzr.repository.revision_tree(lca)
302
260
 
303
261
 
304
 
def canonical_url(object):
305
 
    """Return the canonical URL for a branch."""
306
 
    scheme, netloc, path, params, query, fragment = urlparse.urlparse(
307
 
        str(object.self_link))
308
 
    path = '/'.join(path.split('/')[2:])
309
 
    netloc = netloc.replace('api.', 'code.')
310
 
    return urlparse.urlunparse((scheme, netloc, path, params, query,
311
 
                                fragment))
 
262
def load_branch(launchpad, branch):
 
263
    """Return the launchpadlib Branch object corresponding to 'branch'.
 
264
 
 
265
    :param launchpad: The root `Launchpad` object from launchpadlib.
 
266
    :param branch: A `bzrlib.branch.Branch`.
 
267
    :raise NotLaunchpadBranch: If we cannot determine the Launchpad URL of
 
268
        `branch`.
 
269
    :return: A launchpadlib Branch object.
 
270
    """
 
271
    # XXX: This duplicates the "What are possible URLs for the branch that
 
272
    # Launchpad might recognize" logic found in cmd_lp_open.
 
273
 
 
274
    # XXX: This makes multiple roundtrips to Launchpad for what is
 
275
    # conceptually a single operation -- get me the branches that match these
 
276
    # URLs. Unfortunately, Launchpad's support for such operations is poor, so
 
277
    # we have to allow multiple roundtrips.
 
278
    for url in branch.get_public_branch(), branch.get_push_location():
 
279
        lp_branch = launchpad.branches.getByUrl(url=url)
 
280
        if lp_branch:
 
281
            return lp_branch
 
282
    raise NotLaunchpadBranch(url)