~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/plugins/launchpad/__init__.py

  • Committer: Ross Lagerwall
  • Date: 2012-08-07 06:32:51 UTC
  • mto: (6437.63.5 2.5)
  • mto: This revision was merged to the branch mainline in revision 6558.
  • Revision ID: rosslagerwall@gmail.com-20120807063251-x9p03ghg2ws8oqjc
Add bzrlib/locale to .bzrignore

bzrlib/locale is generated with ./setup.py build_mo which is in turn called
by ./setup.py build

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
 
43
43
# see http://wiki.bazaar.canonical.com/Specs/BranchRegistrationTool
44
44
 
 
45
from bzrlib.lazy_import import lazy_import
 
46
lazy_import(globals(), """
 
47
from bzrlib import (
 
48
    ui,
 
49
    trace,
 
50
    )
 
51
from bzrlib.i18n import gettext
 
52
""")
 
53
 
45
54
from bzrlib import (
46
55
    branch as _mod_branch,
47
 
    config as _mod_config,
 
56
    bzrdir,
48
57
    lazy_regex,
49
58
    # Since we are a built-in plugin we share the bzrlib version
50
 
    trace,
51
59
    version_info,
52
60
    )
53
61
from bzrlib.commands import (
54
 
    plugin_cmds,
 
62
    Command,
 
63
    register_command,
55
64
    )
56
65
from bzrlib.directory_service import directories
 
66
from bzrlib.errors import (
 
67
    BzrCommandError,
 
68
    InvalidRevisionSpec,
 
69
    InvalidURL,
 
70
    NoPublicBranch,
 
71
    NotBranchError,
 
72
    )
57
73
from bzrlib.help_topics import topic_registry
58
 
 
59
 
for klsname, aliases in [
60
 
    ("cmd_register_branch", []),
61
 
    ("cmd_launchpad_open", ["lp-open"]),
62
 
    ("cmd_launchpad_login", ["lp-login"]),
63
 
    ("cmd_launchpad_mirror", ["lp-mirror"]),
64
 
    ("cmd_lp_propose_merge", ["lp-submit", "lp-propose"]),
65
 
    ("cmd_lp_find_proposal", [])]:
66
 
    plugin_cmds.register_lazy(klsname, aliases,
67
 
        "bzrlib.plugins.launchpad.cmds")
 
74
from bzrlib.option import (
 
75
        Option,
 
76
        ListOption,
 
77
)
 
78
 
 
79
 
 
80
class cmd_register_branch(Command):
 
81
    __doc__ = """Register a branch with launchpad.net.
 
82
 
 
83
    This command lists a bzr branch in the directory of branches on
 
84
    launchpad.net.  Registration allows the branch to be associated with
 
85
    bugs or specifications.
 
86
 
 
87
    Before using this command you must register the project to which the
 
88
    branch belongs, and create an account for yourself on launchpad.net.
 
89
 
 
90
    arguments:
 
91
        public_url: The publicly visible url for the branch to register.
 
92
                    This must be an http or https url (which Launchpad can read
 
93
                    from to access the branch). Local file urls, SFTP urls, and
 
94
                    bzr+ssh urls will not work.
 
95
                    If no public_url is provided, bzr will use the configured
 
96
                    public_url if there is one for the current branch, and
 
97
                    otherwise error.
 
98
 
 
99
    example:
 
100
        bzr register-branch http://foo.com/bzr/fooproject.mine \\
 
101
                --project fooproject
 
102
    """
 
103
    takes_args = ['public_url?']
 
104
    takes_options = [
 
105
         Option('project',
 
106
                'Launchpad project short name to associate with the branch.',
 
107
                unicode),
 
108
         Option('product',
 
109
                'Launchpad product short name to associate with the branch.',
 
110
                unicode,
 
111
                hidden=True),
 
112
         Option('branch-name',
 
113
                'Short name for the branch; '
 
114
                'by default taken from the last component of the url.',
 
115
                unicode),
 
116
         Option('branch-title',
 
117
                'One-sentence description of the branch.',
 
118
                unicode),
 
119
         Option('branch-description',
 
120
                'Longer description of the purpose or contents of the branch.',
 
121
                unicode),
 
122
         Option('author',
 
123
                "Branch author's email address, if not yourself.",
 
124
                unicode),
 
125
         Option('link-bug',
 
126
                'The bug this branch fixes.',
 
127
                int),
 
128
         Option('dry-run',
 
129
                'Prepare the request but don\'t actually send it.')
 
130
        ]
 
131
 
 
132
 
 
133
    def run(self,
 
134
            public_url=None,
 
135
            project='',
 
136
            product=None,
 
137
            branch_name='',
 
138
            branch_title='',
 
139
            branch_description='',
 
140
            author='',
 
141
            link_bug=None,
 
142
            dry_run=False):
 
143
        from bzrlib.plugins.launchpad.lp_registration import (
 
144
            BranchRegistrationRequest, BranchBugLinkRequest,
 
145
            DryRunLaunchpadService, LaunchpadService)
 
146
        if public_url is None:
 
147
            try:
 
148
                b = _mod_branch.Branch.open_containing('.')[0]
 
149
            except NotBranchError:
 
150
                raise BzrCommandError(gettext(
 
151
                            'register-branch requires a public '
 
152
                            'branch url - see bzr help register-branch.'))
 
153
            public_url = b.get_public_branch()
 
154
            if public_url is None:
 
155
                raise NoPublicBranch(b)
 
156
        if product is not None:
 
157
            project = product
 
158
            trace.note(gettext(
 
159
                '--product is deprecated; please use --project.'))
 
160
 
 
161
 
 
162
        rego = BranchRegistrationRequest(branch_url=public_url,
 
163
                                         branch_name=branch_name,
 
164
                                         branch_title=branch_title,
 
165
                                         branch_description=branch_description,
 
166
                                         product_name=project,
 
167
                                         author_email=author,
 
168
                                         )
 
169
        linko = BranchBugLinkRequest(branch_url=public_url,
 
170
                                     bug_id=link_bug)
 
171
        if not dry_run:
 
172
            service = LaunchpadService()
 
173
            # This gives back the xmlrpc url that can be used for future
 
174
            # operations on the branch.  It's not so useful to print to the
 
175
            # user since they can't do anything with it from a web browser; it
 
176
            # might be nice for the server to tell us about an html url as
 
177
            # well.
 
178
        else:
 
179
            # Run on service entirely in memory
 
180
            service = DryRunLaunchpadService()
 
181
        service.gather_user_credentials()
 
182
        rego.submit(service)
 
183
        if link_bug:
 
184
            linko.submit(service)
 
185
        print 'Branch registered.'
 
186
 
 
187
register_command(cmd_register_branch)
 
188
 
 
189
 
 
190
class cmd_launchpad_open(Command):
 
191
    __doc__ = """Open a Launchpad branch page in your web browser."""
 
192
 
 
193
    aliases = ['lp-open']
 
194
    takes_options = [
 
195
        Option('dry-run',
 
196
               'Do not actually open the browser. Just say the URL we would '
 
197
               'use.'),
 
198
        ]
 
199
    takes_args = ['location?']
 
200
 
 
201
    def _possible_locations(self, location):
 
202
        """Yield possible external locations for the branch at 'location'."""
 
203
        yield location
 
204
        try:
 
205
            branch = _mod_branch.Branch.open_containing(location)[0]
 
206
        except NotBranchError:
 
207
            return
 
208
        branch_url = branch.get_public_branch()
 
209
        if branch_url is not None:
 
210
            yield branch_url
 
211
        branch_url = branch.get_push_location()
 
212
        if branch_url is not None:
 
213
            yield branch_url
 
214
 
 
215
    def _get_web_url(self, service, location):
 
216
        from bzrlib.plugins.launchpad.lp_registration import (
 
217
            NotLaunchpadBranch)
 
218
        for branch_url in self._possible_locations(location):
 
219
            try:
 
220
                return service.get_web_url_from_branch_url(branch_url)
 
221
            except (NotLaunchpadBranch, InvalidURL):
 
222
                pass
 
223
        raise NotLaunchpadBranch(branch_url)
 
224
 
 
225
    def run(self, location=None, dry_run=False):
 
226
        from bzrlib.plugins.launchpad.lp_registration import (
 
227
            LaunchpadService)
 
228
        if location is None:
 
229
            location = u'.'
 
230
        web_url = self._get_web_url(LaunchpadService(), location)
 
231
        trace.note(gettext('Opening %s in web browser') % web_url)
 
232
        if not dry_run:
 
233
            import webbrowser   # this import should not be lazy
 
234
                                # otherwise bzr.exe lacks this module
 
235
            webbrowser.open(web_url)
 
236
 
 
237
register_command(cmd_launchpad_open)
 
238
 
 
239
 
 
240
class cmd_launchpad_login(Command):
 
241
    __doc__ = """Show or set the Launchpad user ID.
 
242
 
 
243
    When communicating with Launchpad, some commands need to know your
 
244
    Launchpad user ID.  This command can be used to set or show the
 
245
    user ID that Bazaar will use for such communication.
 
246
 
 
247
    :Examples:
 
248
      Show the Launchpad ID of the current user::
 
249
 
 
250
          bzr launchpad-login
 
251
 
 
252
      Set the Launchpad ID of the current user to 'bob'::
 
253
 
 
254
          bzr launchpad-login bob
 
255
    """
 
256
    aliases = ['lp-login']
 
257
    takes_args = ['name?']
 
258
    takes_options = [
 
259
        'verbose',
 
260
        Option('no-check',
 
261
               "Don't check that the user name is valid."),
 
262
        ]
 
263
 
 
264
    def run(self, name=None, no_check=False, verbose=False):
 
265
        # This is totally separate from any launchpadlib login system.
 
266
        from bzrlib.plugins.launchpad import account
 
267
        check_account = not no_check
 
268
 
 
269
        if name is None:
 
270
            username = account.get_lp_login()
 
271
            if username:
 
272
                if check_account:
 
273
                    account.check_lp_login(username)
 
274
                    if verbose:
 
275
                        self.outf.write(gettext(
 
276
                            "Launchpad user ID exists and has SSH keys.\n"))
 
277
                self.outf.write(username + '\n')
 
278
            else:
 
279
                self.outf.write(gettext('No Launchpad user ID configured.\n'))
 
280
                return 1
 
281
        else:
 
282
            name = name.lower()
 
283
            if check_account:
 
284
                account.check_lp_login(name)
 
285
                if verbose:
 
286
                    self.outf.write(gettext(
 
287
                        "Launchpad user ID exists and has SSH keys.\n"))
 
288
            account.set_lp_login(name)
 
289
            if verbose:
 
290
                self.outf.write(gettext("Launchpad user ID set to '%s'.\n") %
 
291
                                                                        (name,))
 
292
 
 
293
register_command(cmd_launchpad_login)
 
294
 
 
295
 
 
296
# XXX: cmd_launchpad_mirror is untested
 
297
class cmd_launchpad_mirror(Command):
 
298
    __doc__ = """Ask Launchpad to mirror a branch now."""
 
299
 
 
300
    aliases = ['lp-mirror']
 
301
    takes_args = ['location?']
 
302
 
 
303
    def run(self, location='.'):
 
304
        from bzrlib.plugins.launchpad import lp_api
 
305
        from bzrlib.plugins.launchpad.lp_registration import LaunchpadService
 
306
        branch, _ = _mod_branch.Branch.open_containing(location)
 
307
        service = LaunchpadService()
 
308
        launchpad = lp_api.login(service)
 
309
        lp_branch = lp_api.LaunchpadBranch.from_bzr(launchpad, branch,
 
310
                create_missing=False)
 
311
        lp_branch.lp.requestMirror()
 
312
 
 
313
 
 
314
register_command(cmd_launchpad_mirror)
 
315
 
 
316
 
 
317
class cmd_lp_propose_merge(Command):
 
318
    __doc__ = """Propose merging a branch on Launchpad.
 
319
 
 
320
    This will open your usual editor to provide the initial comment.  When it
 
321
    has created the proposal, it will open it in your default web browser.
 
322
 
 
323
    The branch will be proposed to merge into SUBMIT_BRANCH.  If SUBMIT_BRANCH
 
324
    is not supplied, the remembered submit branch will be used.  If no submit
 
325
    branch is remembered, the development focus will be used.
 
326
 
 
327
    By default, the SUBMIT_BRANCH's review team will be requested to review
 
328
    the merge proposal.  This can be overriden by specifying --review (-R).
 
329
    The parameter the launchpad account name of the desired reviewer.  This
 
330
    may optionally be followed by '=' and the review type.  For example:
 
331
 
 
332
      bzr lp-propose-merge --review jrandom --review review-team=qa
 
333
 
 
334
    This will propose a merge,  request "jrandom" to perform a review of
 
335
    unspecified type, and request "review-team" to perform a "qa" review.
 
336
    """
 
337
 
 
338
    takes_options = [Option('staging',
 
339
                            help='Propose the merge on staging.'),
 
340
                     Option('message', short_name='m', type=unicode,
 
341
                            help='Commit message.'),
 
342
                     Option('approve',
 
343
                            help='Mark the proposal as approved immediately.'),
 
344
                     ListOption('review', short_name='R', type=unicode,
 
345
                            help='Requested reviewer and optional type.')]
 
346
 
 
347
    takes_args = ['submit_branch?']
 
348
 
 
349
    aliases = ['lp-submit', 'lp-propose']
 
350
 
 
351
    def run(self, submit_branch=None, review=None, staging=False,
 
352
            message=None, approve=False):
 
353
        from bzrlib.plugins.launchpad import lp_propose
 
354
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
 
355
            '.')
 
356
        if review is None:
 
357
            reviews = None
 
358
        else:
 
359
            reviews = []
 
360
            for review in review:
 
361
                if '=' in review:
 
362
                    reviews.append(review.split('=', 2))
 
363
                else:
 
364
                    reviews.append((review, ''))
 
365
            if submit_branch is None:
 
366
                submit_branch = branch.get_submit_branch()
 
367
        if submit_branch is None:
 
368
            target = None
 
369
        else:
 
370
            target = _mod_branch.Branch.open(submit_branch)
 
371
        proposer = lp_propose.Proposer(tree, branch, target, message,
 
372
                                       reviews, staging, approve=approve)
 
373
        proposer.check_proposal()
 
374
        proposer.create_proposal()
 
375
 
 
376
 
 
377
register_command(cmd_lp_propose_merge)
 
378
 
 
379
 
 
380
class cmd_lp_find_proposal(Command):
 
381
 
 
382
    __doc__ = """Find the proposal to merge this revision.
 
383
 
 
384
    Finds the merge proposal(s) that discussed landing the specified revision.
 
385
    This works only if the selected branch was the merge proposal target, and
 
386
    if the merged_revno is recorded for the merge proposal.  The proposal(s)
 
387
    are opened in a web browser.
 
388
 
 
389
    Any revision involved in the merge may be specified-- the revision in
 
390
    which the merge was performed, or one of the revisions that was merged.
 
391
 
 
392
    So, to find the merge proposal that reviewed line 1 of README::
 
393
 
 
394
      bzr lp-find-proposal -r annotate:README:1
 
395
    """
 
396
 
 
397
    takes_options = ['revision']
 
398
 
 
399
    def run(self, revision=None):
 
400
        from bzrlib.plugins.launchpad import lp_api
 
401
        import webbrowser
 
402
        b = _mod_branch.Branch.open_containing('.')[0]
 
403
        pb = ui.ui_factory.nested_progress_bar()
 
404
        b.lock_read()
 
405
        try:
 
406
            revno = self._find_merged_revno(revision, b, pb)
 
407
            merged = self._find_proposals(revno, b, pb)
 
408
            if len(merged) == 0:
 
409
                raise BzrCommandError(gettext('No review found.'))
 
410
            trace.note(gettext('%d proposals(s) found.') % len(merged))
 
411
            for mp in merged:
 
412
                webbrowser.open(lp_api.canonical_url(mp))
 
413
        finally:
 
414
            b.unlock()
 
415
            pb.finished()
 
416
 
 
417
    def _find_merged_revno(self, revision, b, pb):
 
418
        if revision is None:
 
419
            return b.revno()
 
420
        pb.update(gettext('Finding revision-id'))
 
421
        revision_id = revision[0].as_revision_id(b)
 
422
        # a revno spec is necessarily on the mainline.
 
423
        if self._is_revno_spec(revision[0]):
 
424
            merging_revision = revision_id
 
425
        else:
 
426
            graph = b.repository.get_graph()
 
427
            pb.update(gettext('Finding merge'))
 
428
            merging_revision = graph.find_lefthand_merger(
 
429
                revision_id, b.last_revision())
 
430
            if merging_revision is None:
 
431
                raise InvalidRevisionSpec(revision[0].user_spec, b)
 
432
        pb.update(gettext('Finding revno'))
 
433
        return b.revision_id_to_revno(merging_revision)
 
434
 
 
435
    def _find_proposals(self, revno, b, pb):
 
436
        launchpad = lp_api.login(lp_registration.LaunchpadService())
 
437
        pb.update(gettext('Finding Launchpad branch'))
 
438
        lpb = lp_api.LaunchpadBranch.from_bzr(launchpad, b,
 
439
                                              create_missing=False)
 
440
        pb.update(gettext('Finding proposals'))
 
441
        return list(lpb.lp.getMergeProposals(status=['Merged'],
 
442
                                             merged_revnos=[revno]))
 
443
 
 
444
 
 
445
    @staticmethod
 
446
    def _is_revno_spec(spec):
 
447
        try:
 
448
            int(spec.user_spec)
 
449
        except ValueError:
 
450
            return False
 
451
        else:
 
452
            return True
 
453
 
 
454
 
 
455
register_command(cmd_lp_find_proposal)
68
456
 
69
457
 
70
458
def _register_directory():
119
507
    info = _get_package_branch_info(the_branch.base)
120
508
    if info is None:
121
509
        return
122
 
    c = the_branch.get_config_stack()
123
 
    verbosity = c.get('launchpad.packaging_verbosity')
124
 
    if not verbosity:
 
510
    c = the_branch.get_config()
 
511
    verbosity = c.get_user_option('launchpad.packaging_verbosity')
 
512
    if verbosity is not None:
 
513
        verbosity = verbosity.lower()
 
514
    if verbosity == 'off':
125
515
        trace.mutter('not checking %s because verbosity is turned off'
126
516
                     % (the_branch.base,))
127
517
        return
187
577
topic_registry.register('launchpad',
188
578
    _launchpad_help,
189
579
    'Using Bazaar with Launchpad.net')
190
 
 
191
 
_mod_config.option_registry.register(
192
 
    _mod_config.Option('launchpad.packaging_verbosity', default=True,
193
 
          from_unicode=_mod_config.bool_from_store,
194
 
          help="""\
195
 
Whether to warn if a UDD package import branch is accessed that is out of date.
196
 
 
197
 
Setting this option to 'off' will disable verbosity.
198
 
"""))
199
 
_mod_config.option_registry.register(
200
 
    _mod_config.Option('launchpad_username', default=None,
201
 
        help="The username to login with when conneting to Launchpad."))