/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
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
26
import re
6791.2.3 by Jelmer Vernooij
Fix more imports.
27
try:
28
    from urllib.parse import (
29
        urlparse,
30
        urlunparse,
31
        )
32
except ImportError:  # python < 3
33
    from urlparse import (
34
        urlparse,
35
        urlunparse,
36
        )
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
37
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
38
from ... import (
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
39
    branch,
4505.6.24 by Jonathan Lange
Move cache directory to the Bazaar configuration directory.
40
    config,
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
41
    errors,
4505.6.16 by Jonathan Lange
Work on Windows, I think.
42
    osutils,
4969.2.11 by Aaron Bentley
Clean up imports.
43
    trace,
44
    transport,
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
45
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
46
from ...i18n import gettext
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
47
7282.1.1 by Jelmer Vernooij
Add clearer error when launchpadlib is missing.
48
49
class LaunchpadlibMissing(errors.DependencyNotPresent):
50
51
    _fmt = ("launchpadlib is required for Launchpad API access. "
52
            "Please install the launchpadlib package.")
53
54
    def __init__(self, e):
55
        super(LaunchpadlibMissing, self).__init__(
56
            'launchpadlib', e)
57
4505.6.25 by Jonathan Lange
Add a test to check what happens if launchpadlib not available.
58
try:
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
59
    import launchpadlib
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
60
except ImportError as e:
7282.1.1 by Jelmer Vernooij
Add clearer error when launchpadlib is missing.
61
    raise LaunchpadlibMissing(e)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
62
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
63
from launchpadlib.launchpad import (
64
    Launchpad,
65
    )
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
66
from launchpadlib import uris
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
67
68
# Declare the minimum version of launchpadlib that we need in order to work.
7240.1.1 by Jelmer Vernooij
Require a newer version of launchpadlib, set correct consumer name.
69
MINIMUM_LAUNCHPADLIB_VERSION = (1, 6, 3)
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
70
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
71
7240.5.7 by Jelmer Vernooij
Move DEFAULT_INSTANCE.
72
# We use production as the default because edge has been deprecated circa
73
# 2010-11 (see bug https://bugs.launchpad.net/bzr/+bug/583667)
74
DEFAULT_INSTANCE = 'production'
75
7240.5.3 by Jelmer Vernooij
Move LAUNCHPAD_DOMAINS constant.
76
LAUNCHPAD_DOMAINS = {
77
    'production': 'launchpad.net',
78
    'staging': 'staging.launchpad.net',
79
    'qastaging': 'qastaging.launchpad.net',
80
    'demo': 'demo.launchpad.net',
81
    'dev': 'launchpad.dev',
82
    }
83
84
LAUNCHPAD_BAZAAR_DOMAINS = [
85
    'bazaar.%s' % domain
86
    for domain in LAUNCHPAD_DOMAINS.values()]
87
88
4505.6.15 by Jonathan Lange
Baby steps: Move the cache directory stuff into a function.
89
def get_cache_directory():
90
    """Return the directory to cache launchpadlib objects in."""
4505.6.24 by Jonathan Lange
Move cache directory to the Bazaar configuration directory.
91
    return osutils.pathjoin(config.config_dir(), 'launchpad')
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
92
93
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
94
def parse_launchpadlib_version(version_number):
95
    """Parse a version number of the style used by launchpadlib."""
96
    return tuple(map(int, version_number.split('.')))
97
98
99
def check_launchpadlib_compatibility():
100
    """Raise an error if launchpadlib has the wrong version number."""
101
    installed_version = parse_launchpadlib_version(launchpadlib.__version__)
102
    if installed_version < MINIMUM_LAUNCHPADLIB_VERSION:
6672.1.2 by Jelmer Vernooij
Remove breezy.api.
103
        raise errors.DependencyNotPresent(
104
            'launchpadlib',
105
            'At least launchpadlib %s is required, but installed version is %s'
106
            % (MINIMUM_LAUNCHPADLIB_VERSION, installed_version))
4505.6.27 by Jonathan Lange
Add some tests to check for version compatibility. Drop tests for
107
108
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
109
def lookup_service_root(service_root):
110
    try:
111
        return uris.lookup_service_root(service_root)
112
    except ValueError:
113
        if service_root != 'qastaging':
114
            raise
115
        staging_root = uris.lookup_service_root('staging')
116
        return staging_root.replace('staging', 'qastaging')
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
117
118
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
119
class NoLaunchpadBranch(errors.BzrError):
120
    _fmt = 'No launchpad branch could be found for branch "%(url)s".'
121
122
    def __init__(self, branch):
123
        errors.BzrError.__init__(self, branch=branch, url=branch.base)
124
125
7240.5.1 by Jelmer Vernooij
Avoid LaunchpadService when connecting to API.
126
def connect_launchpad(base_url, timeout=None, proxy_info=None,
127
                      version=Launchpad.DEFAULT_VERSION):
128
    """Log in to the Launchpad API.
129
130
    :return: The root `Launchpad` object from launchpadlib.
131
    """
6598.1.2 by Paul Gear
Move defaulting of proxy_info inside login
132
    if proxy_info is None:
7254.1.1 by Jelmer Vernooij
Defer imports.
133
        import httplib2
6598.1.2 by Paul Gear
Move defaulting of proxy_info inside login
134
        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
135
    cache_directory = get_cache_directory()
7240.1.1 by Jelmer Vernooij
Require a newer version of launchpadlib, set correct consumer name.
136
    return Launchpad.login_with(
7240.5.1 by Jelmer Vernooij
Avoid LaunchpadService when connecting to API.
137
        'breezy', base_url, cache_directory, timeout=timeout,
6538.2.2 by Aaron Bentley
Look up merge proposals by exact revision-id.
138
        proxy_info=proxy_info, version=version)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
139
140
7240.5.1 by Jelmer Vernooij
Avoid LaunchpadService when connecting to API.
141
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
142
class LaunchpadBranch(object):
4969.2.10 by Aaron Bentley
Cleanup and docs.
143
    """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
144
145
    def __init__(self, lp_branch, bzr_url, bzr_branch=None, check_update=True):
4969.2.10 by Aaron Bentley
Cleanup and docs.
146
        """Constructor.
147
148
        :param lp_branch: The Launchpad branch.
149
        :param bzr_url: The URL of the Bazaar branch.
150
        :param bzr_branch: An instance of the Bazaar branch.
151
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
152
        self.bzr_url = bzr_url
153
        self._bzr = bzr_branch
154
        self._push_bzr = None
4969.2.14 by Aaron Bentley
Restore update functionality.
155
        self._check_update = check_update
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
156
        self.lp = lp_branch
157
158
    @property
159
    def bzr(self):
4969.2.10 by Aaron Bentley
Cleanup and docs.
160
        """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
161
        if self._bzr is None:
162
            self._bzr = branch.Branch.open(self.bzr_url)
163
        return self._bzr
164
165
    @property
166
    def push_bzr(self):
4969.2.10 by Aaron Bentley
Cleanup and docs.
167
        """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
168
        if self._push_bzr is None:
169
            self._push_bzr = branch.Branch.open(self.lp.bzr_identity)
170
        return self._push_bzr
171
172
    @staticmethod
173
    def plausible_launchpad_url(url):
4969.2.10 by Aaron Bentley
Cleanup and docs.
174
        """Is 'url' something that could conceivably be pushed to LP?
175
176
        :param url: A URL that may refer to a Launchpad branch.
177
        :return: A boolean.
178
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
179
        if url is None:
180
            return False
181
        if url.startswith('lp:'):
182
            return True
7058.4.31 by Jelmer Vernooij
Fix escaping of backslash.
183
        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
184
                           '://bazaar.*.launchpad.net')
185
        return bool(regex.match(url))
186
187
    @staticmethod
188
    def candidate_urls(bzr_branch):
4969.2.10 by Aaron Bentley
Cleanup and docs.
189
        """Iterate through related URLs that might be Launchpad URLs.
190
191
        :param bzr_branch: A Bazaar branch to find URLs from.
192
        :return: a generator of URL strings.
193
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
194
        url = bzr_branch.get_public_branch()
195
        if url is not None:
196
            yield url
197
        url = bzr_branch.get_push_location()
198
        if url is not None:
199
            yield url
5657.1.1 by Max Bowsher
Fix bzr lp-mirror to work on command line branch URLs and branches
200
        url = bzr_branch.get_parent()
201
        if url is not None:
202
            yield url
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
203
        yield bzr_branch.base
204
205
    @staticmethod
206
    def tweak_url(url, launchpad):
4969.2.10 by Aaron Bentley
Cleanup and docs.
207
        """Adjust a URL to work with staging, if needed."""
7254.1.1 by Jelmer Vernooij
Defer imports.
208
        if str(launchpad._root_uri) == uris.STAGING_SERVICE_ROOT:
5615.2.1 by Jelmer Vernooij
Support the 'qastaging' instance of Launchpad.
209
            return url.replace('bazaar.launchpad.net',
210
                               'bazaar.staging.launchpad.net')
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
211
        elif str(launchpad._root_uri) == lookup_service_root('qastaging'):
5615.2.1 by Jelmer Vernooij
Support the 'qastaging' instance of Launchpad.
212
            return url.replace('bazaar.launchpad.net',
213
                               'bazaar.qastaging.launchpad.net')
214
        return url
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
215
216
    @classmethod
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
217
    def from_bzr(cls, launchpad, bzr_branch, create_missing=True):
4969.2.10 by Aaron Bentley
Cleanup and docs.
218
        """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
219
        check_update = True
220
        for url in cls.candidate_urls(bzr_branch):
221
            url = cls.tweak_url(url, launchpad)
222
            if not cls.plausible_launchpad_url(url):
223
                continue
224
            lp_branch = launchpad.branches.getByUrl(url=url)
225
            if lp_branch is not None:
226
                break
227
        else:
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
228
            if not create_missing:
229
                raise NoLaunchpadBranch(bzr_branch)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
230
            lp_branch = cls.create_now(launchpad, bzr_branch)
231
            check_update = False
232
        return cls(lp_branch, bzr_branch.base, bzr_branch, check_update)
233
234
    @classmethod
235
    def create_now(cls, launchpad, bzr_branch):
4969.2.10 by Aaron Bentley
Cleanup and docs.
236
        """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
237
        url = cls.tweak_url(bzr_branch.get_push_location(), launchpad)
238
        if not cls.plausible_launchpad_url(url):
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
239
            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
240
                                  bzr_branch.base)
241
        bzr_branch.create_clone_on_transport(transport.get_transport(url))
242
        lp_branch = launchpad.branches.getByUrl(url=url)
243
        if lp_branch is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
244
            raise errors.BzrError(gettext('%s is not registered on Launchpad') %
7143.15.2 by Jelmer Vernooij
Run autopep8.
245
                                  url)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
246
        return lp_branch
247
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
248
    def get_target(self):
249
        """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
250
        lp_branch = self.lp
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
251
        if lp_branch.project is not None:
5616.1.2 by Vincent Ladeuil
Fix normal branch usage with lp-propose.
252
            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.
253
            if dev_focus is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
254
                raise errors.BzrError(gettext('%s has no development focus.') %
7143.15.2 by Jelmer Vernooij
Run autopep8.
255
                                      lp_branch.bzr_identity)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
256
            target = dev_focus.branch
257
            if target is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
258
                raise errors.BzrError(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
259
                    '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.
260
        elif lp_branch.sourcepackage is not None:
261
            target = lp_branch.sourcepackage.getBranch(pocket="Release")
262
            if target is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
263
                raise errors.BzrError(gettext(
264
                                      'source package %s has no branch.') %
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
265
                                      lp_branch.sourcepackage)
266
        else:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
267
            raise errors.BzrError(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
268
                '%s has no associated product or source package.') %
269
                lp_branch.bzr_identity)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
270
        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
271
272
    def update_lp(self):
4969.2.15 by Aaron Bentley
Update docs.
273
        """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
274
        if not self._check_update:
275
            return
6754.8.4 by Jelmer Vernooij
Use new context stuff.
276
        with self.bzr.lock_read():
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
277
            if self.lp.last_scanned_id is not None:
278
                if self.bzr.last_revision() == self.lp.last_scanned_id:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
279
                    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
280
                               self.lp.bzr_identity)
281
                    return
282
                graph = self.bzr.repository.get_graph()
7141.6.1 by Jelmer Vernooij
Encode revision id when passing it into graph.
283
                if not graph.is_ancestor(osutils.safe_utf8(self.lp.last_scanned_id),
4969.2.18 by Aaron Bentley
Fix divergence check.
284
                                         self.bzr.last_revision()):
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
285
                    raise errors.DivergedBranches(self.bzr, self.push_bzr)
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
286
                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
287
            self.bzr.push(self.push_bzr)
288
289
    def find_lca_tree(self, other):
4969.2.10 by Aaron Bentley
Cleanup and docs.
290
        """Find the revision tree for the LCA of this branch and other.
291
292
        :param other: Another LaunchpadBranch
293
        :return: The RevisionTree of the LCA of this branch and other.
294
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
295
        graph = self.bzr.repository.get_graph(other.bzr.repository)
296
        lca = graph.find_unique_lca(self.bzr.last_revision(),
297
                                    other.bzr.last_revision())
298
        return self.bzr.repository.revision_tree(lca)
299
300
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
301
def canonical_url(object):
302
    """Return the canonical URL for a branch."""
6791.2.3 by Jelmer Vernooij
Fix more imports.
303
    scheme, netloc, path, params, query, fragment = urlparse(
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
304
        str(object.self_link))
305
    path = '/'.join(path.split('/')[2:])
306
    netloc = netloc.replace('api.', 'code.')
6791.2.3 by Jelmer Vernooij
Fix more imports.
307
    return urlunparse((scheme, netloc, path, params, query, fragment))