/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,
7336.2.1 by Martin
Split non-ini config methods to bedding
40
    bedding,
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',
7302.1.1 by Colin Watson
Handle renaming of launchpad.dev to launchpad.test.
81
    'dev': 'launchpad.test',
7240.5.3 by Jelmer Vernooij
Move LAUNCHPAD_DOMAINS constant.
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."""
7336.2.1 by Martin
Split non-ini config methods to bedding
91
    return osutils.pathjoin(bedding.cache_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')
7371.1.2 by Jelmer Vernooij
Fix Launchpad cache directory test as well.
135
    try:
136
        cache_directory = get_cache_directory()
137
    except EnvironmentError:
138
        cache_directory = None
7240.1.1 by Jelmer Vernooij
Require a newer version of launchpadlib, set correct consumer name.
139
    return Launchpad.login_with(
7240.5.1 by Jelmer Vernooij
Avoid LaunchpadService when connecting to API.
140
        'breezy', base_url, cache_directory, timeout=timeout,
6538.2.2 by Aaron Bentley
Look up merge proposals by exact revision-id.
141
        proxy_info=proxy_info, version=version)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
142
143
7240.5.1 by Jelmer Vernooij
Avoid LaunchpadService when connecting to API.
144
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
145
class LaunchpadBranch(object):
4969.2.10 by Aaron Bentley
Cleanup and docs.
146
    """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
147
148
    def __init__(self, lp_branch, bzr_url, bzr_branch=None, check_update=True):
4969.2.10 by Aaron Bentley
Cleanup and docs.
149
        """Constructor.
150
151
        :param lp_branch: The Launchpad branch.
152
        :param bzr_url: The URL of the Bazaar branch.
153
        :param bzr_branch: An instance of the Bazaar branch.
154
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
155
        self.bzr_url = bzr_url
156
        self._bzr = bzr_branch
157
        self._push_bzr = None
4969.2.14 by Aaron Bentley
Restore update functionality.
158
        self._check_update = check_update
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
159
        self.lp = lp_branch
160
161
    @property
162
    def bzr(self):
4969.2.10 by Aaron Bentley
Cleanup and docs.
163
        """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
164
        if self._bzr is None:
165
            self._bzr = branch.Branch.open(self.bzr_url)
166
        return self._bzr
167
168
    @property
169
    def push_bzr(self):
4969.2.10 by Aaron Bentley
Cleanup and docs.
170
        """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
171
        if self._push_bzr is None:
172
            self._push_bzr = branch.Branch.open(self.lp.bzr_identity)
173
        return self._push_bzr
174
175
    @staticmethod
176
    def plausible_launchpad_url(url):
4969.2.10 by Aaron Bentley
Cleanup and docs.
177
        """Is 'url' something that could conceivably be pushed to LP?
178
179
        :param url: A URL that may refer to a Launchpad branch.
180
        :return: A boolean.
181
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
182
        if url is None:
183
            return False
184
        if url.startswith('lp:'):
185
            return True
7058.4.31 by Jelmer Vernooij
Fix escaping of backslash.
186
        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
187
                           '://bazaar.*.launchpad.net')
188
        return bool(regex.match(url))
189
190
    @staticmethod
191
    def candidate_urls(bzr_branch):
4969.2.10 by Aaron Bentley
Cleanup and docs.
192
        """Iterate through related URLs that might be Launchpad URLs.
193
194
        :param bzr_branch: A Bazaar branch to find URLs from.
195
        :return: a generator of URL strings.
196
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
197
        url = bzr_branch.get_public_branch()
198
        if url is not None:
199
            yield url
200
        url = bzr_branch.get_push_location()
201
        if url is not None:
202
            yield url
5657.1.1 by Max Bowsher
Fix bzr lp-mirror to work on command line branch URLs and branches
203
        url = bzr_branch.get_parent()
204
        if url is not None:
205
            yield url
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
206
        yield bzr_branch.base
207
208
    @staticmethod
209
    def tweak_url(url, launchpad):
4969.2.10 by Aaron Bentley
Cleanup and docs.
210
        """Adjust a URL to work with staging, if needed."""
7254.1.1 by Jelmer Vernooij
Defer imports.
211
        if str(launchpad._root_uri) == uris.STAGING_SERVICE_ROOT:
5615.2.1 by Jelmer Vernooij
Support the 'qastaging' instance of Launchpad.
212
            return url.replace('bazaar.launchpad.net',
213
                               'bazaar.staging.launchpad.net')
6538.2.1 by Aaron Bentley
Update to require launchpadlib 1.6.0
214
        elif str(launchpad._root_uri) == lookup_service_root('qastaging'):
5615.2.1 by Jelmer Vernooij
Support the 'qastaging' instance of Launchpad.
215
            return url.replace('bazaar.launchpad.net',
216
                               'bazaar.qastaging.launchpad.net')
217
        return url
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
218
219
    @classmethod
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
220
    def from_bzr(cls, launchpad, bzr_branch, create_missing=True):
4969.2.10 by Aaron Bentley
Cleanup and docs.
221
        """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
222
        check_update = True
223
        for url in cls.candidate_urls(bzr_branch):
224
            url = cls.tweak_url(url, launchpad)
225
            if not cls.plausible_launchpad_url(url):
226
                continue
227
            lp_branch = launchpad.branches.getByUrl(url=url)
228
            if lp_branch is not None:
229
                break
230
        else:
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
231
            if not create_missing:
232
                raise NoLaunchpadBranch(bzr_branch)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
233
            lp_branch = cls.create_now(launchpad, bzr_branch)
234
            check_update = False
235
        return cls(lp_branch, bzr_branch.base, bzr_branch, check_update)
236
237
    @classmethod
238
    def create_now(cls, launchpad, bzr_branch):
4969.2.10 by Aaron Bentley
Cleanup and docs.
239
        """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
240
        url = cls.tweak_url(bzr_branch.get_push_location(), launchpad)
241
        if not cls.plausible_launchpad_url(url):
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
242
            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
243
                                  bzr_branch.base)
244
        bzr_branch.create_clone_on_transport(transport.get_transport(url))
245
        lp_branch = launchpad.branches.getByUrl(url=url)
246
        if lp_branch is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
247
            raise errors.BzrError(gettext('%s is not registered on Launchpad') %
7143.15.2 by Jelmer Vernooij
Run autopep8.
248
                                  url)
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
249
        return lp_branch
250
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
251
    def get_target(self):
252
        """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
253
        lp_branch = self.lp
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
254
        if lp_branch.project is not None:
5616.1.2 by Vincent Ladeuil
Fix normal branch usage with lp-propose.
255
            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.
256
            if dev_focus is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
257
                raise errors.BzrError(gettext('%s has no development focus.') %
7143.15.2 by Jelmer Vernooij
Run autopep8.
258
                                      lp_branch.bzr_identity)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
259
            target = dev_focus.branch
260
            if target is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
261
                raise errors.BzrError(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
262
                    '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.
263
        elif lp_branch.sourcepackage is not None:
264
            target = lp_branch.sourcepackage.getBranch(pocket="Release")
265
            if target is None:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
266
                raise errors.BzrError(gettext(
267
                                      'source package %s has no branch.') %
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
268
                                      lp_branch.sourcepackage)
269
        else:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
270
            raise errors.BzrError(gettext(
7143.15.2 by Jelmer Vernooij
Run autopep8.
271
                '%s has no associated product or source package.') %
272
                lp_branch.bzr_identity)
5616.1.1 by Jelmer Vernooij
Support 'bzr lp-propose' without an explicit target branch for packaging branches.
273
        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
274
275
    def update_lp(self):
4969.2.15 by Aaron Bentley
Update docs.
276
        """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
277
        if not self._check_update:
278
            return
6754.8.4 by Jelmer Vernooij
Use new context stuff.
279
        with self.bzr.lock_read():
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
280
            if self.lp.last_scanned_id is not None:
281
                if self.bzr.last_revision() == self.lp.last_scanned_id:
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
282
                    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
283
                               self.lp.bzr_identity)
284
                    return
285
                graph = self.bzr.repository.get_graph()
7141.6.1 by Jelmer Vernooij
Encode revision id when passing it into graph.
286
                if not graph.is_ancestor(osutils.safe_utf8(self.lp.last_scanned_id),
4969.2.18 by Aaron Bentley
Fix divergence check.
287
                                         self.bzr.last_revision()):
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
288
                    raise errors.DivergedBranches(self.bzr, self.push_bzr)
6150.3.1 by Jonathan Riddell
gettext() in launchpad plugin
289
                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
290
            self.bzr.push(self.push_bzr)
291
292
    def find_lca_tree(self, other):
4969.2.10 by Aaron Bentley
Cleanup and docs.
293
        """Find the revision tree for the LCA of this branch and other.
294
295
        :param other: Another LaunchpadBranch
296
        :return: The RevisionTree of the LCA of this branch and other.
297
        """
4969.2.3 by Aaron Bentley
Move LaunchpadBranch to lp_api. Change the interface so that it uses launchpad
298
        graph = self.bzr.repository.get_graph(other.bzr.repository)
299
        lca = graph.find_unique_lca(self.bzr.last_revision(),
300
                                    other.bzr.last_revision())
301
        return self.bzr.repository.revision_tree(lca)
302
303
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
304
def canonical_url(object):
305
    """Return the canonical URL for a branch."""
6791.2.3 by Jelmer Vernooij
Fix more imports.
306
    scheme, netloc, path, params, query, fragment = urlparse(
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
307
        str(object.self_link))
308
    path = '/'.join(path.split('/')[2:])
309
    netloc = netloc.replace('api.', 'code.')
6791.2.3 by Jelmer Vernooij
Fix more imports.
310
    return urlunparse((scheme, netloc, path, params, query, fragment))