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