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