~bzr-pqm/bzr/bzr.dev

5557.1.7 by John Arbash Meinel
Merge in the bzr.dev 5582
1
# Copyright (C) 2006-2011 Canonical Ltd
0.4.1 by Martin Pool
Start lp-register command
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.4.1 by Martin Pool
Start lp-register command
16
5459.3.1 by Neil Martinsen-Burrell
add more detailed help for Launchpad plugin
17
"""Launchpad.net integration plugin for Bazaar.
18
19
This plugin provides facilities for working with Bazaar branches that are
20
hosted on Launchpad (http://launchpad.net).  It provides a directory service 
5459.3.2 by Neil Martinsen-Burrell
address James Westbys review comments
21
for referring to Launchpad branches using the "lp:" prefix.  For example,
22
lp:bzr refers to the Bazaar's main development branch and
23
lp:~username/project/branch-name can be used to refer to a specific branch.
24
25
This plugin provides a bug tracker so that "bzr commit --fixes lp:1234" will
26
record that revision as fixing Launchpad's bug 1234.
27
28
The plugin also provides the following commands:
29
30
    launchpad-login: Show or set the Launchpad user ID
31
    launchpad-open: Open a Launchpad branch page in your web browser
32
    lp-propose-merge: Propose merging a branch on Launchpad
5459.3.1 by Neil Martinsen-Burrell
add more detailed help for Launchpad plugin
33
    register-branch: Register a branch with launchpad.net
34
    launchpad-mirror: Ask Launchpad to mirror a branch now
35
36
"""
0.4.1 by Martin Pool
Start lp-register command
37
0.4.17 by Martin Pool
Allow xmlrpc service url to be overridden by $BZR_LP_XMLRPC_URL
38
# The XMLRPC server address can be overridden by setting the environment
5053.1.1 by Martin Pool
Merge 2.1 back to trunk
39
# variable $BZR_LP_XMLRPC_URL
0.4.17 by Martin Pool
Allow xmlrpc service url to be overridden by $BZR_LP_XMLRPC_URL
40
5560.2.1 by Vincent Ladeuil
Fix the remaining references to http://bazaar-vcs.org (except the explicitly historical ones).
41
# see http://wiki.bazaar.canonical.com/Specs/BranchRegistrationTool
0.4.9 by Martin Pool
Don't transmit non-standard xmlrpc <nil> value.
42
4031.2.11 by John Arbash Meinel
Turn a bunch of imports into lazy imports.
43
from bzrlib.lazy_import import lazy_import
44
lazy_import(globals(), """
45
from bzrlib import (
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
46
    ui,
4031.2.11 by John Arbash Meinel
Turn a bunch of imports into lazy imports.
47
    trace,
48
    )
49
""")
50
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
51
from bzrlib import (
52
    branch as _mod_branch,
53
    bzrdir,
54
    lazy_regex,
6024.3.8 by John Arbash Meinel
Move 'place' logic onto LatestPublication.
55
    # Since we are a built-in plugin we share the bzrlib version
5050.79.6 by John Arbash Meinel
small bits of cleanup, and add timing information.
56
    version_info,
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
57
    )
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
58
from bzrlib.commands import (
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
59
    Command,
60
    register_command,
61
    )
3251.4.1 by Aaron Bentley
Convert LP transport into directory service
62
from bzrlib.directory_service import directories
4031.2.5 by Jonathan Lange
Refactor the code so we don't do too many remote requests.
63
from bzrlib.errors import (
64
    BzrCommandError,
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
65
    InvalidRevisionSpec,
4031.2.5 by Jonathan Lange
Refactor the code so we don't do too many remote requests.
66
    InvalidURL,
67
    NoPublicBranch,
68
    NotBranchError,
69
    )
2245.8.6 by Martin Pool
Documentation under 'help launchpad'
70
from bzrlib.help_topics import topic_registry
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
71
from bzrlib.option import (
4969.2.10 by Aaron Bentley
Cleanup and docs.
72
        Option,
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
73
        ListOption,
74
)
0.4.4 by Martin Pool
Start forming xmlrpc requests
75
76
0.4.2 by Martin Pool
Rename command to 'register-branch'
77
class cmd_register_branch(Command):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
78
    __doc__ = """Register a branch with launchpad.net.
0.4.1 by Martin Pool
Start lp-register command
79
80
    This command lists a bzr branch in the directory of branches on
2400.2.4 by Robert Collins
(robertc) Typo in the help for ``register-branch`` fixed. (Robert Collins, #96770)
81
    launchpad.net.  Registration allows the branch to be associated with
0.4.1 by Martin Pool
Start lp-register command
82
    bugs or specifications.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
83
4416.7.1 by Neil Martinsen-Burrell
Fix 238764 refer to projects rather than products in launchpad plugin
84
    Before using this command you must register the project to which the
0.4.1 by Martin Pool
Start lp-register command
85
    branch belongs, and create an account for yourself on launchpad.net.
0.4.3 by Martin Pool
More command line processing
86
87
    arguments:
3200.2.3 by Robert Collins
Tweak wording.
88
        public_url: The publicly visible url for the branch to register.
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
89
                    This must be an http or https url (which Launchpad can read
90
                    from to access the branch). Local file urls, SFTP urls, and
91
                    bzr+ssh urls will not work.
92
                    If no public_url is provided, bzr will use the configured
3200.2.3 by Robert Collins
Tweak wording.
93
                    public_url if there is one for the current branch, and
94
                    otherwise error.
0.4.3 by Martin Pool
More command line processing
95
96
    example:
4416.7.1 by Neil Martinsen-Burrell
Fix 238764 refer to projects rather than products in launchpad plugin
97
        bzr register-branch http://foo.com/bzr/fooproject.mine \\
98
                --project fooproject
0.4.1 by Martin Pool
Start lp-register command
99
    """
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
100
    takes_args = ['public_url?']
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
101
    takes_options = [
4416.7.1 by Neil Martinsen-Burrell
Fix 238764 refer to projects rather than products in launchpad plugin
102
         Option('project',
103
                'Launchpad project short name to associate with the branch.',
0.4.15 by Martin Pool
(register-branch) Add command-line options
104
                unicode),
4416.7.3 by Neil Martinsen-Burrell
Retain --product options for register-branch, but deprecate it in favor of --project
105
         Option('product',
6015.21.1 by Vincent Ladeuil
Merge 2.3 into 2.4 (including fix for #614713)
106
                'Launchpad product short name to associate with the branch.',
4416.7.3 by Neil Martinsen-Burrell
Retain --product options for register-branch, but deprecate it in favor of --project
107
                unicode,
108
                hidden=True),
0.4.15 by Martin Pool
(register-branch) Add command-line options
109
         Option('branch-name',
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
110
                'Short name for the branch; '
111
                'by default taken from the last component of the url.',
0.4.15 by Martin Pool
(register-branch) Add command-line options
112
                unicode),
113
         Option('branch-title',
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
114
                'One-sentence description of the branch.',
0.4.15 by Martin Pool
(register-branch) Add command-line options
115
                unicode),
116
         Option('branch-description',
2598.1.5 by Martin Pool
Fix one more option message.
117
                'Longer description of the purpose or contents of the branch.',
0.4.15 by Martin Pool
(register-branch) Add command-line options
118
                unicode),
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
119
         Option('author',
120
                "Branch author's email address, if not yourself.",
0.4.16 by Martin Pool
(register-branch) Add --author option and respect --dry-run
121
                unicode),
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
122
         Option('link-bug',
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
123
                'The bug this branch fixes.',
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
124
                int),
0.4.15 by Martin Pool
(register-branch) Add command-line options
125
         Option('dry-run',
2598.1.1 by Martin Pool
Add test for and documentation of option style, fix up existing options to comply
126
                'Prepare the request but don\'t actually send it.')
0.4.15 by Martin Pool
(register-branch) Add command-line options
127
        ]
128
129
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
130
    def run(self,
131
            public_url=None,
4416.7.1 by Neil Martinsen-Burrell
Fix 238764 refer to projects rather than products in launchpad plugin
132
            project='',
4416.7.3 by Neil Martinsen-Burrell
Retain --product options for register-branch, but deprecate it in favor of --project
133
            product=None,
0.4.15 by Martin Pool
(register-branch) Add command-line options
134
            branch_name='',
135
            branch_title='',
136
            branch_description='',
0.4.16 by Martin Pool
(register-branch) Add --author option and respect --dry-run
137
            author='',
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
138
            link_bug=None,
0.4.15 by Martin Pool
(register-branch) Add command-line options
139
            dry_run=False):
2898.3.8 by James Henstridge
Get rid of relative imports in Launchpad plugin.
140
        from bzrlib.plugins.launchpad.lp_registration import (
4505.6.4 by Jonathan Lange
Remove unnecessary import.
141
            BranchRegistrationRequest, BranchBugLinkRequest,
4505.6.10 by Jonathan Lange
Move lp_registration imports into commands.
142
            DryRunLaunchpadService, LaunchpadService)
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
143
        if public_url is None:
144
            try:
4031.2.11 by John Arbash Meinel
Turn a bunch of imports into lazy imports.
145
                b = _mod_branch.Branch.open_containing('.')[0]
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
146
            except NotBranchError:
147
                raise BzrCommandError('register-branch requires a public '
148
                    'branch url - see bzr help register-branch.')
149
            public_url = b.get_public_branch()
150
            if public_url is None:
151
                raise NoPublicBranch(b)
4416.7.3 by Neil Martinsen-Burrell
Retain --product options for register-branch, but deprecate it in favor of --project
152
        if product is not None:
153
            project = product
154
            trace.note('--product is deprecated; please use --project.')
155
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
156
157
        rego = BranchRegistrationRequest(branch_url=public_url,
0.4.15 by Martin Pool
(register-branch) Add command-line options
158
                                         branch_name=branch_name,
159
                                         branch_title=branch_title,
160
                                         branch_description=branch_description,
4416.7.1 by Neil Martinsen-Burrell
Fix 238764 refer to projects rather than products in launchpad plugin
161
                                         product_name=project,
0.4.16 by Martin Pool
(register-branch) Add --author option and respect --dry-run
162
                                         author_email=author,
0.4.15 by Martin Pool
(register-branch) Add command-line options
163
                                         )
3200.2.1 by Robert Collins
* The ``register-branch`` command will now use the public url of the branch
164
        linko = BranchBugLinkRequest(branch_url=public_url,
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
165
                                     bug_id=link_bug)
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
166
        if not dry_run:
167
            service = LaunchpadService()
168
            # This gives back the xmlrpc url that can be used for future
169
            # operations on the branch.  It's not so useful to print to the
170
            # user since they can't do anything with it from a web browser; it
171
            # might be nice for the server to tell us about an html url as
172
            # well.
173
        else:
174
            # Run on service entirely in memory
175
            service = DryRunLaunchpadService()
0.4.19 by test at canonical
add possibility to link to a bug when registering a branch. factor out some common functionality from BranchRegistrationRequest.
176
        service.gather_user_credentials()
4505.6.21 by Jonathan Lange
Clean up flakes.
177
        rego.submit(service)
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
178
        if link_bug:
4505.6.21 by Jonathan Lange
Clean up flakes.
179
            linko.submit(service)
1668.1.12 by Martin Pool
(launchpad plugin) Improved --dry-run that uses a dummy xmlrpc service.
180
        print 'Branch registered.'
0.4.1 by Martin Pool
Start lp-register command
181
0.4.2 by Martin Pool
Rename command to 'register-branch'
182
register_command(cmd_register_branch)
2245.8.4 by Martin Pool
lp:/// indirection works
183
2898.3.3 by James Henstridge
Add launchpad-login command.
184
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
185
class cmd_launchpad_open(Command):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
186
    __doc__ = """Open a Launchpad branch page in your web browser."""
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
187
188
    aliases = ['lp-open']
3955.3.7 by Jonathan Lange
Test the launchpad-open command. Fix up some minor bugs.
189
    takes_options = [
190
        Option('dry-run',
191
               'Do not actually open the browser. Just say the URL we would '
192
               'use.'),
193
        ]
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
194
    takes_args = ['location?']
195
4031.2.5 by Jonathan Lange
Refactor the code so we don't do too many remote requests.
196
    def _possible_locations(self, location):
197
        """Yield possible external locations for the branch at 'location'."""
198
        yield location
199
        try:
4867.2.1 by Neil Martinsen-Burrell
launchpad-open works from a subdirectory of a branch
200
            branch = _mod_branch.Branch.open_containing(location)[0]
4031.2.5 by Jonathan Lange
Refactor the code so we don't do too many remote requests.
201
        except NotBranchError:
202
            return
203
        branch_url = branch.get_public_branch()
204
        if branch_url is not None:
205
            yield branch_url
206
        branch_url = branch.get_push_location()
207
        if branch_url is not None:
208
            yield branch_url
209
210
    def _get_web_url(self, service, location):
4505.6.10 by Jonathan Lange
Move lp_registration imports into commands.
211
        from bzrlib.plugins.launchpad.lp_registration import (
212
            NotLaunchpadBranch)
4031.2.5 by Jonathan Lange
Refactor the code so we don't do too many remote requests.
213
        for branch_url in self._possible_locations(location):
214
            try:
215
                return service.get_web_url_from_branch_url(branch_url)
216
            except (NotLaunchpadBranch, InvalidURL):
217
                pass
218
        raise NotLaunchpadBranch(branch_url)
219
3955.3.7 by Jonathan Lange
Test the launchpad-open command. Fix up some minor bugs.
220
    def run(self, location=None, dry_run=False):
4505.6.10 by Jonathan Lange
Move lp_registration imports into commands.
221
        from bzrlib.plugins.launchpad.lp_registration import (
222
            LaunchpadService)
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
223
        if location is None:
224
            location = u'.'
4505.6.21 by Jonathan Lange
Clean up flakes.
225
        web_url = self._get_web_url(LaunchpadService(), location)
4031.2.11 by John Arbash Meinel
Turn a bunch of imports into lazy imports.
226
        trace.note('Opening %s in web browser' % web_url)
3955.3.7 by Jonathan Lange
Test the launchpad-open command. Fix up some minor bugs.
227
        if not dry_run:
4511.1.1 by Alexander Belchenko
launchpad plugin: import webbrowse should be explicit and never lazy, otherwise bzr.exe lacks this module.
228
            import webbrowser   # this import should not be lazy
229
                                # otherwise bzr.exe lacks this module
3955.3.7 by Jonathan Lange
Test the launchpad-open command. Fix up some minor bugs.
230
            webbrowser.open(web_url)
3955.3.5 by Jonathan Lange
Add an untested plugin, make the error handling a little nicer.
231
232
register_command(cmd_launchpad_open)
233
234
2898.3.3 by James Henstridge
Add launchpad-login command.
235
class cmd_launchpad_login(Command):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
236
    __doc__ = """Show or set the Launchpad user ID.
2898.3.3 by James Henstridge
Add launchpad-login command.
237
238
    When communicating with Launchpad, some commands need to know your
239
    Launchpad user ID.  This command can be used to set or show the
240
    user ID that Bazaar will use for such communication.
241
242
    :Examples:
243
      Show the Launchpad ID of the current user::
244
245
          bzr launchpad-login
246
2898.3.9 by James Henstridge
* Add a simple NEWS item for the command.
247
      Set the Launchpad ID of the current user to 'bob'::
2898.3.3 by James Henstridge
Add launchpad-login command.
248
2898.3.9 by James Henstridge
* Add a simple NEWS item for the command.
249
          bzr launchpad-login bob
2898.3.3 by James Henstridge
Add launchpad-login command.
250
    """
251
    aliases = ['lp-login']
252
    takes_args = ['name?']
253
    takes_options = [
4505.1.2 by Jonathan Lange
Add many more tests, fix the actual bug.
254
        'verbose',
2898.3.4 by James Henstridge
Cleanups from mini-review by Tim.
255
        Option('no-check',
256
               "Don't check that the user name is valid."),
2898.3.3 by James Henstridge
Add launchpad-login command.
257
        ]
258
4505.1.2 by Jonathan Lange
Add many more tests, fix the actual bug.
259
    def run(self, name=None, no_check=False, verbose=False):
4505.6.13 by Jonathan Lange
Clarify the comment.
260
        # This is totally separate from any launchpadlib login system.
2898.3.8 by James Henstridge
Get rid of relative imports in Launchpad plugin.
261
        from bzrlib.plugins.launchpad import account
2898.3.3 by James Henstridge
Add launchpad-login command.
262
        check_account = not no_check
263
264
        if name is None:
265
            username = account.get_lp_login()
266
            if username:
2898.3.4 by James Henstridge
Cleanups from mini-review by Tim.
267
                if check_account:
268
                    account.check_lp_login(username)
4505.1.3 by Jonathan Lange
Add some extra untested verbosity.
269
                    if verbose:
270
                        self.outf.write(
271
                            "Launchpad user ID exists and has SSH keys.\n")
2898.3.3 by James Henstridge
Add launchpad-login command.
272
                self.outf.write(username + '\n')
273
            else:
274
                self.outf.write('No Launchpad user ID configured.\n')
2898.3.9 by James Henstridge
* Add a simple NEWS item for the command.
275
                return 1
2898.3.3 by James Henstridge
Add launchpad-login command.
276
        else:
4216.2.1 by Michael Hudson
well this is it
277
            name = name.lower()
2898.3.3 by James Henstridge
Add launchpad-login command.
278
            if check_account:
279
                account.check_lp_login(name)
4505.1.3 by Jonathan Lange
Add some extra untested verbosity.
280
                if verbose:
281
                    self.outf.write(
282
                        "Launchpad user ID exists and has SSH keys.\n")
2898.3.3 by James Henstridge
Add launchpad-login command.
283
            account.set_lp_login(name)
4505.1.2 by Jonathan Lange
Add many more tests, fix the actual bug.
284
            if verbose:
285
                self.outf.write("Launchpad user ID set to '%s'.\n" % (name,))
2898.3.3 by James Henstridge
Add launchpad-login command.
286
287
register_command(cmd_launchpad_login)
288
289
4505.6.9 by Jonathan Lange
Add some XXX comments based on the review.
290
# XXX: cmd_launchpad_mirror is untested
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
291
class cmd_launchpad_mirror(Command):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
292
    __doc__ = """Ask Launchpad to mirror a branch now."""
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
293
294
    aliases = ['lp-mirror']
295
    takes_args = ['location?']
296
297
    def run(self, location='.'):
4505.6.25 by Jonathan Lange
Add a test to check what happens if launchpadlib not available.
298
        from bzrlib.plugins.launchpad import lp_api
4505.6.10 by Jonathan Lange
Move lp_registration imports into commands.
299
        from bzrlib.plugins.launchpad.lp_registration import LaunchpadService
5657.1.2 by Max Bowsher
Also make lp-mirror use open_containing, so it still works when in a subdirectory of a branch.
300
        branch, _ = _mod_branch.Branch.open_containing(location)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
301
        service = LaunchpadService()
302
        launchpad = lp_api.login(service)
5657.1.1 by Max Bowsher
Fix bzr lp-mirror to work on command line branch URLs and branches
303
        lp_branch = lp_api.LaunchpadBranch.from_bzr(launchpad, branch,
304
                create_missing=False)
305
        lp_branch.lp.requestMirror()
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
306
307
4505.6.8 by Jonathan Lange
Always register the lp-mirror command. Only try to load launchpadlib if
308
register_command(cmd_launchpad_mirror)
4505.6.6 by Jonathan Lange
Add a command to mirror Launchpad branches now.
309
310
4969.2.16 by Aaron Bentley
Updates from review.
311
class cmd_lp_propose_merge(Command):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
312
    __doc__ = """Propose merging a branch on Launchpad.
4969.2.16 by Aaron Bentley
Updates from review.
313
314
    This will open your usual editor to provide the initial comment.  When it
4969.2.19 by Aaron Bentley
Rename submit to propose everywhere.
315
    has created the proposal, it will open it in your default web browser.
4969.2.16 by Aaron Bentley
Updates from review.
316
317
    The branch will be proposed to merge into SUBMIT_BRANCH.  If SUBMIT_BRANCH
318
    is not supplied, the remembered submit branch will be used.  If no submit
319
    branch is remembered, the development focus will be used.
320
321
    By default, the SUBMIT_BRANCH's review team will be requested to review
322
    the merge proposal.  This can be overriden by specifying --review (-R).
323
    The parameter the launchpad account name of the desired reviewer.  This
324
    may optionally be followed by '=' and the review type.  For example:
325
326
      bzr lp-propose-merge --review jrandom --review review-team=qa
327
328
    This will propose a merge,  request "jrandom" to perform a review of
329
    unspecified type, and request "review-team" to perform a "qa" review.
330
    """
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
331
332
    takes_options = [Option('staging',
333
                            help='Propose the merge on staging.'),
334
                     Option('message', short_name='m', type=unicode,
335
                            help='Commit message.'),
5244.1.3 by Robert Collins
Allow setting new proposals as approved immediately.
336
                     Option('approve',
337
                            help='Mark the proposal as approved immediately.'),
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
338
                     ListOption('review', short_name='R', type=unicode,
339
                            help='Requested reviewer and optional type.')]
340
341
    takes_args = ['submit_branch?']
342
4969.2.16 by Aaron Bentley
Updates from review.
343
    aliases = ['lp-submit', 'lp-propose']
344
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
345
    def run(self, submit_branch=None, review=None, staging=False,
5244.1.3 by Robert Collins
Allow setting new proposals as approved immediately.
346
            message=None, approve=False):
4969.2.19 by Aaron Bentley
Rename submit to propose everywhere.
347
        from bzrlib.plugins.launchpad import lp_propose
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
348
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
349
            '.')
350
        if review is None:
351
            reviews = None
352
        else:
353
            reviews = []
354
            for review in review:
355
                if '=' in review:
356
                    reviews.append(review.split('=', 2))
357
                else:
358
                    reviews.append((review, ''))
359
            if submit_branch is None:
360
                submit_branch = branch.get_submit_branch()
361
        if submit_branch is None:
362
            target = None
363
        else:
364
            target = _mod_branch.Branch.open(submit_branch)
4969.2.19 by Aaron Bentley
Rename submit to propose everywhere.
365
        proposer = lp_propose.Proposer(tree, branch, target, message,
5244.1.3 by Robert Collins
Allow setting new proposals as approved immediately.
366
                                       reviews, staging, approve=approve)
4969.2.19 by Aaron Bentley
Rename submit to propose everywhere.
367
        proposer.check_proposal()
368
        proposer.create_proposal()
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
369
370
4969.2.16 by Aaron Bentley
Updates from review.
371
register_command(cmd_lp_propose_merge)
4969.2.7 by Aaron Bentley
Add lp-submit, get working.
372
373
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
374
class cmd_lp_find_proposal(Command):
375
376
    __doc__ = """Find the proposal to merge this revision.
377
378
    Finds the merge proposal(s) that discussed landing the specified revision.
379
    This works only if the selected branch was the merge proposal target, and
380
    if the merged_revno is recorded for the merge proposal.  The proposal(s)
381
    are opened in a web browser.
382
383
    Any revision involved in the merge may be specified-- the revision in
384
    which the merge was performed, or one of the revisions that was merged.
385
386
    So, to find the merge proposal that reviewed line 1 of README::
387
388
      bzr lp-find-proposal -r annotate:README:1
389
    """
390
391
    takes_options = ['revision']
392
393
    def run(self, revision=None):
394
        from bzrlib.plugins.launchpad import lp_api
395
        import webbrowser
396
        b = _mod_branch.Branch.open_containing('.')[0]
397
        pb = ui.ui_factory.nested_progress_bar()
398
        b.lock_read()
399
        try:
400
            revno = self._find_merged_revno(revision, b, pb)
5546.2.4 by Aaron Bentley
Cleanup
401
            merged = self._find_proposals(revno, b, pb)
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
402
            if len(merged) == 0:
403
                raise BzrCommandError('No review found.')
404
            trace.note('%d proposals(s) found.' % len(merged))
405
            for mp in merged:
406
                webbrowser.open(lp_api.canonical_url(mp))
407
        finally:
408
            b.unlock()
409
            pb.finished()
410
411
    def _find_merged_revno(self, revision, b, pb):
412
        if revision is None:
413
            return b.revno()
414
        pb.update('Finding revision-id')
415
        revision_id = revision[0].as_revision_id(b)
416
        # a revno spec is necessarily on the mainline.
417
        if self._is_revno_spec(revision[0]):
418
            merging_revision = revision_id
419
        else:
420
            graph = b.repository.get_graph()
421
            pb.update('Finding merge')
422
            merging_revision = graph.find_lefthand_merger(
423
                revision_id, b.last_revision())
424
            if merging_revision is None:
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
425
                raise InvalidRevisionSpec(revision[0].user_spec, b)
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
426
        pb.update('Finding revno')
427
        return b.revision_id_to_revno(merging_revision)
428
5546.2.4 by Aaron Bentley
Cleanup
429
    def _find_proposals(self, revno, b, pb):
430
        launchpad = lp_api.login(lp_registration.LaunchpadService())
431
        pb.update('Finding Launchpad branch')
432
        lpb = lp_api.LaunchpadBranch.from_bzr(launchpad, b,
433
                                              create_missing=False)
434
        pb.update('Finding proposals')
435
        return list(lpb.lp.getMergeProposals(status=['Merged'],
436
                                             merged_revnos=[revno]))
437
438
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
439
    @staticmethod
440
    def _is_revno_spec(spec):
441
        try:
5546.2.3 by Aaron Bentley
Tighten revno check, avoid creating branches on lp.
442
            int(spec.user_spec)
5546.2.1 by Aaron Bentley
Add lp-find-proposal.
443
        except ValueError:
444
            return False
445
        else:
446
            return True
447
448
449
register_command(cmd_lp_find_proposal)
450
451
3251.4.2 by Aaron Bentley
Clean up Launchpad directory service code
452
def _register_directory():
3251.4.3 by Aaron Bentley
More renames and cleanups
453
    directories.register_lazy('lp:', 'bzrlib.plugins.launchpad.lp_directory',
3251.4.2 by Aaron Bentley
Clean up Launchpad directory service code
454
                              'LaunchpadDirectory',
455
                              'Launchpad-based directory service',)
5462.4.1 by Barry Warsaw
Added support for ubuntu: and debianlp: schemes, accessing the relevant
456
    directories.register_lazy(
457
        'debianlp:', 'bzrlib.plugins.launchpad.lp_directory',
458
        'LaunchpadDirectory',
459
        'debianlp: shortcut')
460
    directories.register_lazy(
461
        'ubuntu:', 'bzrlib.plugins.launchpad.lp_directory',
462
        'LaunchpadDirectory',
463
        'ubuntu: shortcut')
464
3251.4.2 by Aaron Bentley
Clean up Launchpad directory service code
465
_register_directory()
2245.8.5 by Martin Pool
Add short-form lp:PRODUCT url form
466
6024.3.5 by John Arbash Meinel
Pull out code into helper functions, which allows us to test it.
467
# This is kept in __init__ so that we don't load lp_api_lite unless the branch
468
# actually matches. That way we can avoid importing extra dependencies like
469
# json.
470
_package_branch = lazy_regex.lazy_compile(
6024.3.4 by John Arbash Meinel
More tests, and a small fix for the regex.
471
    r'bazaar.launchpad.net.*?/'
6024.3.3 by John Arbash Meinel
Start at least testing the package_branch regex.
472
    r'(?P<user>~[^/]+/)?(?P<archive>ubuntu|debian)/(?P<series>[^/]+/)?'
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
473
    r'(?P<project>[^/]+)(?P<branch>/[^/]+)?'
474
    )
6024.3.5 by John Arbash Meinel
Pull out code into helper functions, which allows us to test it.
475
476
def _get_package_branch_info(url):
477
    """Determine the packaging information for this URL.
478
479
    :return: If this isn't a packaging branch, return None. If it is, return
480
        (archive, series, project)
481
    """
6015.10.2 by John Arbash Meinel
Fix bug #816417, it seems older foreign branch implementations did not set Branch.base.
482
    if url is None:
483
        return None
6024.3.5 by John Arbash Meinel
Pull out code into helper functions, which allows us to test it.
484
    m = _package_branch.search(url)
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
485
    if m is None:
6015.10.2 by John Arbash Meinel
Fix bug #816417, it seems older foreign branch implementations did not set Branch.base.
486
        return None
6024.3.3 by John Arbash Meinel
Start at least testing the package_branch regex.
487
    archive, series, project, user = m.group('archive', 'series',
488
                                             'project', 'user')
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
489
    if series is not None:
5050.79.7 by John Arbash Meinel
clarify a comment.
490
        # series is optional, so the regex includes the extra '/', we don't
491
        # want to send that on (it causes Internal Server Errors.)
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
492
        series = series.strip('/')
6024.3.5 by John Arbash Meinel
Pull out code into helper functions, which allows us to test it.
493
    if user is not None:
494
        user = user.strip('~/')
495
        if user != 'ubuntu-branches':
496
            return None
497
    return archive, series, project
498
499
500
def _check_is_up_to_date(the_branch):
501
    info = _get_package_branch_info(the_branch.base)
502
    if info is None:
503
        return
6024.3.8 by John Arbash Meinel
Move 'place' logic onto LatestPublication.
504
    c = the_branch.get_config()
6024.3.10 by John Arbash Meinel
Try refactoring the code a bit per vila's suggestions.
505
    verbosity = c.get_user_option('launchpad.packaging_verbosity')
6024.3.8 by John Arbash Meinel
Move 'place' logic onto LatestPublication.
506
    if verbosity is not None:
507
        verbosity = verbosity.lower()
508
    if verbosity == 'off':
509
        trace.mutter('not checking %s because verbosity is turned off'
510
                     % (the_branch.base,))
511
        return
6024.3.5 by John Arbash Meinel
Pull out code into helper functions, which allows us to test it.
512
    archive, series, project = info
513
    from bzrlib.plugins.launchpad import lp_api_lite
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
514
    latest_pub = lp_api_lite.LatestPublication(archive, series, project)
6024.3.8 by John Arbash Meinel
Move 'place' logic onto LatestPublication.
515
    lp_api_lite.report_freshness(the_branch, verbosity, latest_pub)
516
5050.79.5 by John Arbash Meinel
Add a Branch.open hook.
517
518
def _register_hooks():
519
    _mod_branch.Branch.hooks.install_named_hook('open',
520
        _check_is_up_to_date, 'package-branch-up-to-date')
521
522
523
_register_hooks()
524
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
525
def load_tests(basic_tests, module, loader):
526
    testmod_names = [
527
        'test_account',
528
        'test_register',
4505.6.25 by Jonathan Lange
Add a test to check what happens if launchpadlib not available.
529
        'test_lp_api',
5050.79.2 by John Arbash Meinel
Start refactoring lp_api_lite and making it testable.
530
        'test_lp_api_lite',
4776.2.2 by Vincent Ladeuil
Start testing the XMLRPC transport re-implemented on top of _urllib2_wrappers.
531
        'test_lp_directory',
532
        'test_lp_login',
533
        'test_lp_open',
534
        'test_lp_service',
535
        ]
536
    basic_tests.addTest(loader.loadTestsFromModuleNames(
537
            ["%s.%s" % (__name__, tmn) for tmn in testmod_names]))
538
    return basic_tests
2245.8.1 by Martin Pool
Start adding tests for launchpad indirection
539
2245.8.6 by Martin Pool
Documentation under 'help launchpad'
540
541
_launchpad_help = """Integration with Launchpad.net
542
543
Launchpad.net provides free Bazaar branch hosting with integrated bug and
544
specification tracking.
545
3031.1.1 by jml at canonical
Expand the documentation on lp:// URLs and mention the launchpad-login command.
546
The bzr client (through the plugin called 'launchpad') has special
2245.8.6 by Martin Pool
Documentation under 'help launchpad'
547
features to communicate with Launchpad:
548
3031.1.1 by jml at canonical
Expand the documentation on lp:// URLs and mention the launchpad-login command.
549
    * The launchpad-login command tells Bazaar your Launchpad user name. This
550
      is then used by the 'lp:' transport to download your branches using
551
      bzr+ssh://.
552
4258.1.1 by James Westby
Add "--fixes lp:" to the launchpad plugin documentation.
553
    * The 'lp:' transport uses Launchpad as a directory service: for example
554
      'lp:bzr' and 'lp:python' refer to the main branches of the relevant
555
      projects and may be branched, logged, etc. You can also use the 'lp:'
556
      transport to refer to specific branches, e.g. lp:~bzr/bzr/trunk.
557
558
    * The 'lp:' bug tracker alias can expand launchpad bug numbers to their
559
      URLs for use with 'bzr commit --fixes', e.g. 'bzr commit --fixes lp:12345'
560
      will record a revision property that marks that revision as fixing
4258.1.2 by Matt Nordhoff
Fix a typo in the launchpad plugin's help
561
      Launchpad bug 12345. When you push that branch to Launchpad it will
562
      automatically be linked to the bug report.
4258.1.1 by James Westby
Add "--fixes lp:" to the launchpad plugin documentation.
563
3031.1.1 by jml at canonical
Expand the documentation on lp:// URLs and mention the launchpad-login command.
564
    * The register-branch command tells Launchpad about the url of a
2245.8.6 by Martin Pool
Documentation under 'help launchpad'
565
      public branch.  Launchpad will then mirror the branch, display
3031.1.1 by jml at canonical
Expand the documentation on lp:// URLs and mention the launchpad-login command.
566
      its contents and allow it to be attached to bugs and other
2245.8.6 by Martin Pool
Documentation under 'help launchpad'
567
      objects.
568
569
For more information see http://help.launchpad.net/
570
"""
571
topic_registry.register('launchpad',
572
    _launchpad_help,
573
    'Using Bazaar with Launchpad.net')