/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
1
# Copyright (C) 2009-2012 Canonical Ltd
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Tools for dealing with the Launchpad API."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
20
4505.6.11 by Jonathan Lange
Flag lp_api as a difficult import.
21
# Importing this module will be expensive, since it imports launchpadlib and
22
# its dependencies. However, our plan is to only load this module when it is
23
# needed by a command that uses it.
24
4505.6.18 by Jonathan Lange
Another review comment to make a note of.
25
6598.1.1 by Paul Gear
Provide sensible default proxy_info
26
import httplib2
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
27
import re
6791.2.3 by Jelmer Vernooij
Fix more imports.
28
try:
29
    from urllib.parse import (
30
        urlparse,
31
        urlunparse,
32
        )
33
except ImportError:  # python < 3
34
    from urlparse import (
35
        urlparse,
36
        urlunparse,
37
        )
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
38
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
39
from ... import (
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
40
    branch,
4505.6.24 by Jonathan Lange
Move cache directory to the Bazaar configuration directory.
41
    config,
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
42
    errors,
4505.6.16 by Jonathan Lange
Work on Windows, I think.
43
    osutils,
4969.2.11 by Aaron Bentley
Clean up imports.
44
    trace,
45
    transport,
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
46
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
47
from ...i18n import gettext
48
from .lp_registration import (
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
49
    InvalidLaunchpadInstance,
50
    )
51
4505.6.25 by Jonathan Lange
Add a test to check what happens if launchpadlib not available.
52
try:
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
53
    import launchpadlib
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
54
except ImportError as e:
4505.6.25 by Jonathan Lange
Add a test to check what happens if launchpadlib not available.
55
    raise errors.DependencyNotPresent('launchpadlib', e)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
56
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
57
from launchpadlib.launchpad import (
58
    STAGING_SERVICE_ROOT,
59
    Launchpad,
60
    )
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
61
from launchpadlib import uris
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
62
63
# Declare the minimum version of launchpadlib that we need in order to work.
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
64
# 1.6.0 is the version of launchpadlib packaged in Ubuntu 10.04, the most
65
# recent Ubuntu LTS release supported on the desktop at the time of writing.
66
MINIMUM_LAUNCHPADLIB_VERSION = (1, 6, 0)
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
67
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
68
4505.6.15 by Jonathan Lange
Baby steps: Move the cache directory stuff into a function.
69
def get_cache_directory():
70
    """Return the directory to cache launchpadlib objects in."""
4505.6.24 by Jonathan Lange
Move cache directory to the Bazaar configuration directory.
71
    return osutils.pathjoin(config.config_dir(), 'launchpad')
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
72
73
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
74
def parse_launchpadlib_version(version_number):
75
    """Parse a version number of the style used by launchpadlib."""
76
    return tuple(map(int, version_number.split('.')))
77
78
79
def check_launchpadlib_compatibility():
80
    """Raise an error if launchpadlib has the wrong version number."""
81
    installed_version = parse_launchpadlib_version(launchpadlib.__version__)
82
    if installed_version < MINIMUM_LAUNCHPADLIB_VERSION:
6672.1.2 by Jelmer Vernooij
Remove breezy.api.
83
        raise errors.DependencyNotPresent(
84
            'launchpadlib',
85
            'At least launchpadlib %s is required, but installed version is %s'
86
            % (MINIMUM_LAUNCHPADLIB_VERSION, installed_version))
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
87
88
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
89
def lookup_service_root(service_root):
90
    try:
91
        return uris.lookup_service_root(service_root)
92
    except ValueError:
93
        if service_root != 'qastaging':
94
            raise
95
        staging_root = uris.lookup_service_root('staging')
96
        return staging_root.replace('staging', 'qastaging')
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
97
98
99
def _get_api_url(service):
100
    """Return the root URL of the Launchpad API.
101
4797.76.5 by Vincent Ladeuil
Fix edge references in lp_api and more comments.
102
    e.g. For the 'staging' Launchpad service, this function returns
103
    launchpadlib.launchpad.STAGING_SERVICE_ROOT.
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
104
105
    :param service: A `LaunchpadService` object.
106
    :return: A URL as a string.
107
    """
108
    if service._lp_instance is None:
109
        lp_instance = service.DEFAULT_INSTANCE
110
    else:
111
        lp_instance = service._lp_instance
112
    try:
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
113
        return lookup_service_root(lp_instance)
114
    except ValueError:
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
115
        raise InvalidLaunchpadInstance(lp_instance)
116
117
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
118
class NoLaunchpadBranch(errors.BzrError):
119
    _fmt = 'No launchpad branch could be found for branch "%(url)s".'
120
121
    def __init__(self, branch):
122
        errors.BzrError.__init__(self, branch=branch, url=branch.base)
123
124
6598.1.2 by Paul Gear
Move defaulting of proxy_info inside login
125
def login(service, timeout=None, proxy_info=None,
6538.2.3 by Aaron Bentley
Fix default version for login.
126
          version=Launchpad.DEFAULT_VERSION):
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
127
    """Log in to the Launchpad API.
128
129
    :return: The root `Launchpad` object from launchpadlib.
130
    """
6598.1.2 by Paul Gear
Move defaulting of proxy_info inside login
131
    if proxy_info is None:
132
        proxy_info = httplib2.proxy_info_from_environment('https')
4505.6.19 by Jonathan Lange
Delete swathes of code because we can rely on a version of launchpadlib
133
    cache_directory = get_cache_directory()
134
    launchpad = Launchpad.login_with(
135
        'bzr', _get_api_url(service), cache_directory, timeout=timeout,
6538.2.2 by Aaron Bentley
Look up merge proposals by exact revision-id.
136
        proxy_info=proxy_info, version=version)
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
137
    # XXX: Work-around a minor security bug in launchpadlib < 1.6.3, which
138
    # would create this directory with default umask.
6619.3.14 by Jelmer Vernooij
Convert some octal numbers to new notations.
139
    osutils.chmod_if_possible(cache_directory, 0o700)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
140
    return launchpad
141
142
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
143
class LaunchpadBranch(object):
4969.2.10 by Aaron Bentley
Cleanup and docs.
144
    """Provide bzr and lp API access to a Launchpad branch."""
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
145
146
    def __init__(self, lp_branch, bzr_url, bzr_branch=None, check_update=True):
4969.2.10 by Aaron Bentley
Cleanup and docs.
147
        """Constructor.
148
149
        :param lp_branch: The Launchpad branch.
150
        :param bzr_url: The URL of the Bazaar branch.
151
        :param bzr_branch: An instance of the Bazaar branch.
152
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
153
        self.bzr_url = bzr_url
154
        self._bzr = bzr_branch
155
        self._push_bzr = None
4969.2.14 by Aaron Bentley
Restore update functionality.
156
        self._check_update = check_update
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
157
        self.lp = lp_branch
158
159
    @property
160
    def bzr(self):
4969.2.10 by Aaron Bentley
Cleanup and docs.
161
        """Return the bzr branch for this branch."""
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
162
        if self._bzr is None:
163
            self._bzr = branch.Branch.open(self.bzr_url)
164
        return self._bzr
165
166
    @property
167
    def push_bzr(self):
4969.2.10 by Aaron Bentley
Cleanup and docs.
168
        """Return the push branch for this branch."""
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
169
        if self._push_bzr is None:
170
            self._push_bzr = branch.Branch.open(self.lp.bzr_identity)
171
        return self._push_bzr
172
173
    @staticmethod
174
    def plausible_launchpad_url(url):
4969.2.10 by Aaron Bentley
Cleanup and docs.
175
        """Is 'url' something that could conceivably be pushed to LP?
176
177
        :param url: A URL that may refer to a Launchpad branch.
178
        :return: A boolean.
179
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
180
        if url is None:
181
            return False
182
        if url.startswith('lp:'):
183
            return True
7058.4.31 by Jelmer Vernooij
Fix escaping of backslash.
184
        regex = re.compile('([a-z]*\\+)*(bzr\\+ssh|http)'
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
185
                           '://bazaar.*.launchpad.net')
186
        return bool(regex.match(url))
187
188
    @staticmethod
189
    def candidate_urls(bzr_branch):
4969.2.10 by Aaron Bentley
Cleanup and docs.
190
        """Iterate through related URLs that might be Launchpad URLs.
191
192
        :param bzr_branch: A Bazaar branch to find URLs from.
193
        :return: a generator of URL strings.
194
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
195
        url = bzr_branch.get_public_branch()
196
        if url is not None:
197
            yield url
198
        url = bzr_branch.get_push_location()
199
        if url is not None:
200
            yield url
5657.1.1 by Max Bowsher
Fix bzr lp-mirror to work on command line branch URLs and branches
201
        url = bzr_branch.get_parent()
202
        if url is not None:
203
            yield url
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
204
        yield bzr_branch.base
205
206
    @staticmethod
207
    def tweak_url(url, launchpad):
4969.2.10 by Aaron Bentley
Cleanup and docs.
208
        """Adjust a URL to work with staging, if needed."""
5615.2.1 by Jelmer Vernooij
Support the 'qastaging' instance of Launchpad.
209
        if str(launchpad._root_uri) == STAGING_SERVICE_ROOT:
210
            return url.replace('bazaar.launchpad.net',
211
                               'bazaar.staging.launchpad.net')
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
212
        elif str(launchpad._root_uri) == lookup_service_root('qastaging'):
5615.2.1 by Jelmer Vernooij
Support the 'qastaging' instance of Launchpad.
213
            return url.replace('bazaar.launchpad.net',
214
                               'bazaar.qastaging.launchpad.net')
215
        return url
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
216
217
    @classmethod
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
218
    def from_bzr(cls, launchpad, bzr_branch, create_missing=True):
4969.2.10 by Aaron Bentley
Cleanup and docs.
219
        """Find a Launchpad branch from a bzr branch."""
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
220
        check_update = True
221
        for url in cls.candidate_urls(bzr_branch):
222
            url = cls.tweak_url(url, launchpad)
223
            if not cls.plausible_launchpad_url(url):
224
                continue
225
            lp_branch = launchpad.branches.getByUrl(url=url)
226
            if lp_branch is not None:
227
                break
228
        else:
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
229
            if not create_missing:
230
                raise NoLaunchpadBranch(bzr_branch)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
231
            lp_branch = cls.create_now(launchpad, bzr_branch)
232
            check_update = False
233
        return cls(lp_branch, bzr_branch.base, bzr_branch, check_update)
234
235
    @classmethod
236
    def create_now(cls, launchpad, bzr_branch):
4969.2.10 by Aaron Bentley
Cleanup and docs.
237
        """Create a Bazaar branch on Launchpad for the supplied branch."""
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
238
        url = cls.tweak_url(bzr_branch.get_push_location(), launchpad)
239
        if not cls.plausible_launchpad_url(url):
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
240
            raise errors.BzrError(gettext('%s is not registered on Launchpad') %
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
241
                                  bzr_branch.base)
242
        bzr_branch.create_clone_on_transport(transport.get_transport(url))
243
        lp_branch = launchpad.branches.getByUrl(url=url)
244
        if lp_branch is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
245
            raise errors.BzrError(gettext('%s is not registered on Launchpad') %
7143.15.2 by Jelmer Vernooij
Run autopep8.
246
                                  url)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
247
        return lp_branch
248
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
249
    def get_target(self):
250
        """Return the 'LaunchpadBranch' for the target of this one."""
4969.2.5 by Aaron Bentley
It makes more sense to get the dev focus from an existing Launchpad branch
251
        lp_branch = self.lp
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
252
        if lp_branch.project is not None:
5616.1.2 by Vincent Ladeuil
Fix normal branch usage with lp-propose.
253
            dev_focus = lp_branch.project.development_focus
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
254
            if dev_focus is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
255
                raise errors.BzrError(gettext('%s has no development focus.') %
7143.15.2 by Jelmer Vernooij
Run autopep8.
256
                                      lp_branch.bzr_identity)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
257
            target = dev_focus.branch
258
            if target is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
259
                raise errors.BzrError(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
260
                    'development focus %s has no branch.') % dev_focus)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
261
        elif lp_branch.sourcepackage is not None:
262
            target = lp_branch.sourcepackage.getBranch(pocket="Release")
263
            if target is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
264
                raise errors.BzrError(gettext(
265
                                      'source package %s has no branch.') %
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
266
                                      lp_branch.sourcepackage)
267
        else:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
268
            raise errors.BzrError(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
269
                '%s has no associated product or source package.') %
270
                lp_branch.bzr_identity)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
271
        return LaunchpadBranch(target, target.bzr_identity)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
272
273
    def update_lp(self):
4969.2.15 by Aaron Bentley
Update docs.
274
        """Update the Launchpad copy of this branch."""
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
275
        if not self._check_update:
276
            return
6754.8.4 by Jelmer Vernooij
Use new context stuff.
277
        with self.bzr.lock_read():
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
278
            if self.lp.last_scanned_id is not None:
279
                if self.bzr.last_revision() == self.lp.last_scanned_id:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
280
                    trace.note(gettext('%s is already up-to-date.') %
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
281
                               self.lp.bzr_identity)
282
                    return
283
                graph = self.bzr.repository.get_graph()
7141.6.1 by Jelmer Vernooij
Encode revision id when passing it into graph.
284
                if not graph.is_ancestor(osutils.safe_utf8(self.lp.last_scanned_id),
4969.2.18 by Aaron Bentley
Fix divergence check.
285
                                         self.bzr.last_revision()):
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
286
                    raise errors.DivergedBranches(self.bzr, self.push_bzr)
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
287
                trace.note(gettext('Pushing to %s') % self.lp.bzr_identity)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
288
            self.bzr.push(self.push_bzr)
289
290
    def find_lca_tree(self, other):
4969.2.10 by Aaron Bentley
Cleanup and docs.
291
        """Find the revision tree for the LCA of this branch and other.
292
293
        :param other: Another LaunchpadBranch
294
        :return: The RevisionTree of the LCA of this branch and other.
295
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
296
        graph = self.bzr.repository.get_graph(other.bzr.repository)
297
        lca = graph.find_unique_lca(self.bzr.last_revision(),
298
                                    other.bzr.last_revision())
299
        return self.bzr.repository.revision_tree(lca)
300
301
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
302
def canonical_url(object):
303
    """Return the canonical URL for a branch."""
6791.2.3 by Jelmer Vernooij
Fix more imports.
304
    scheme, netloc, path, params, query, fragment = urlparse(
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
305
        str(object.self_link))
306
    path = '/'.join(path.split('/')[2:])
307
    netloc = netloc.replace('api.', 'code.')
6791.2.3 by Jelmer Vernooij
Fix more imports.
308
    return urlunparse((scheme, netloc, path, params, query, fragment))