/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.431.1 by Jelmer Vernooij
Start work on propose command.
1
# Copyright (C) 2018 Jelmer Vernooij <jelmer@jelmer.uk>
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
17
"""Propose command implementations."""
18
0.434.1 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
20
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
21
from io import StringIO
22
0.431.1 by Jelmer Vernooij
Start work on propose command.
23
from ... import (
24
    branch as _mod_branch,
25
    controldir,
26
    errors,
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
27
    log as _mod_log,
28
    missing as _mod_missing,
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
29
    msgeditor,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
30
    urlutils,
0.431.1 by Jelmer Vernooij
Start work on propose command.
31
    )
32
from ...i18n import gettext
33
from ...commands import Command
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
34
from ...option import (
35
    ListOption,
0.432.2 by Jelmer Vernooij
Publish command sort of works.
36
    Option,
0.431.6 by Jelmer Vernooij
Initial gitlab support works.
37
    RegistryOption,
38
    )
39
from ...sixish import text_type
0.432.2 by Jelmer Vernooij
Publish command sort of works.
40
from ...trace import note
0.431.1 by Jelmer Vernooij
Start work on propose command.
41
from . import (
42
    propose as _mod_propose,
43
    )
44
45
0.432.2 by Jelmer Vernooij
Publish command sort of works.
46
def branch_name(branch):
47
    if branch.name:
48
        return branch.name
49
    return urlutils.basename(branch.user_url)
50
51
0.431.18 by Jelmer Vernooij
Rename 'brz publish' to 'brz publish-derived'.
52
class cmd_publish_derived(Command):
53
    __doc__ = """Publish a derived branch.
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
54
0.431.18 by Jelmer Vernooij
Rename 'brz publish' to 'brz publish-derived'.
55
    Try to create a public copy of a local branch on a hosting site,
56
    derived from the specified base branch.
0.432.2 by Jelmer Vernooij
Publish command sort of works.
57
58
    Reasonable defaults are picked for owner name, branch name and project
59
    name, but they can also be overridden from the command-line.
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
60
    """
61
0.432.2 by Jelmer Vernooij
Publish command sort of works.
62
    takes_options = [
7211.13.7 by Jelmer Vernooij
Fix formatting.
63
        'directory',
64
        Option('owner', help='Owner of the new remote branch.', type=str),
65
        Option('project', help='Project name for the new remote branch.',
66
               type=str),
67
        Option('name', help='Name of the new remote branch.', type=str),
68
        Option('no-allow-lossy',
69
               help='Allow fallback to lossy push, if necessary.'),
70
        Option('overwrite', help="Overwrite existing commits."),
71
        ]
0.432.2 by Jelmer Vernooij
Publish command sort of works.
72
    takes_args = ['submit_branch?']
73
74
    def run(self, submit_branch=None, owner=None, name=None, project=None,
0.431.52 by Jelmer Vernooij
Add --overwrite flag to 'brz publish'.
75
            no_allow_lossy=False, overwrite=False, directory='.'):
0.432.2 by Jelmer Vernooij
Publish command sort of works.
76
        local_branch = _mod_branch.Branch.open_containing(directory)[0]
77
        self.add_cleanup(local_branch.lock_write().unlock)
78
        if submit_branch is None:
79
            submit_branch = local_branch.get_submit_branch()
80
            note(gettext('Using submit branch %s') % submit_branch)
0.431.22 by Jelmer Vernooij
Add Hoster.get_derived_branch.
81
        if submit_branch is None:
82
            submit_branch = local_branch.get_parent()
83
            note(gettext('Using parent branch %s') % submit_branch)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
84
        submit_branch = _mod_branch.Branch.open(submit_branch)
85
        if name is None:
86
            name = branch_name(local_branch)
87
        hoster = _mod_propose.get_hoster(submit_branch)
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
88
        remote_branch, public_url = hoster.publish_derived(
7211.13.7 by Jelmer Vernooij
Fix formatting.
89
            local_branch, submit_branch, name=name, project=project,
90
            owner=owner, allow_lossy=not no_allow_lossy,
91
            overwrite=overwrite)
0.432.2 by Jelmer Vernooij
Publish command sort of works.
92
        local_branch.set_push_location(remote_branch.user_url)
93
        local_branch.set_public_branch(public_url)
94
        note(gettext("Pushed to %s") % public_url)
0.432.1 by Jelmer Vernooij
Initial work on hoster support.
95
96
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
97
def summarize_unmerged(local_branch, remote_branch, target,
98
                       prerequisite_branch=None):
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
99
    """Generate a text description of the unmerged revisions in branch.
100
101
    :param branch: The proposed branch
102
    :param target: Target branch
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
103
    :param prerequisite_branch: Optional prerequisite branch
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
104
    :return: A string
105
    """
0.431.55 by Jelmer Vernooij
Cope with lossy pushes better in `brz propose` texts.
106
    log_format = _mod_log.log_formatter_registry.get_default(local_branch)
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
107
    to_file = StringIO()
108
    lf = log_format(to_file=to_file, show_ids=False, show_timezone='original')
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
109
    if prerequisite_branch:
110
        local_extra = _mod_missing.find_unmerged(
111
            remote_branch, prerequisite_branch, restrict='local')[0]
112
    else:
113
        local_extra = _mod_missing.find_unmerged(
114
            remote_branch, target, restrict='local')[0]
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
115
0.431.55 by Jelmer Vernooij
Cope with lossy pushes better in `brz propose` texts.
116
    if remote_branch.supports_tags():
117
        rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
118
    else:
119
        rev_tag_dict = {}
120
121
    for revision in _mod_missing.iter_log_revisions(
0.431.55 by Jelmer Vernooij
Cope with lossy pushes better in `brz propose` texts.
122
            local_extra, local_branch.repository, False, rev_tag_dict):
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
123
        lf.log_revision(revision)
124
    return to_file.getvalue()
125
126
0.431.1 by Jelmer Vernooij
Start work on propose command.
127
class cmd_propose_merge(Command):
128
    __doc__ = """Propose a branch for merging.
129
130
    This command creates a merge proposal for the local
131
    branch to the target branch. The format of the merge
132
    proposal depends on the submit branch.
133
    """
134
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
135
    takes_options = [
7211.13.7 by Jelmer Vernooij
Fix formatting.
136
        'directory',
137
        RegistryOption(
138
            'hoster',
139
            help='Use the hoster.',
140
            lazy_registry=('breezy.plugins.propose.propose', 'hosters')),
141
        ListOption('reviewers', short_name='R', type=text_type,
142
                   help='Requested reviewers.'),
143
        Option('name', help='Name of the new remote branch.', type=str),
144
        Option('description', help='Description of the change.', type=str),
145
        Option('prerequisite', help='Prerequisite branch.', type=str),
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
146
        Option(
147
            'commit-message',
148
            help='Set commit message for merge, if supported', type=str),
7211.13.7 by Jelmer Vernooij
Fix formatting.
149
        ListOption('labels', short_name='l', type=text_type,
150
                   help='Labels to apply.'),
151
        Option('no-allow-lossy',
152
               help='Allow fallback to lossy push, if necessary.'),
153
        ]
0.431.1 by Jelmer Vernooij
Start work on propose command.
154
    takes_args = ['submit_branch?']
155
156
    aliases = ['propose']
157
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
158
    def run(self, submit_branch=None, directory='.', hoster=None,
159
            reviewers=None, name=None, no_allow_lossy=False, description=None,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
160
            labels=None, prerequisite=None, commit_message=None):
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
161
        tree, branch, relpath = (
162
            controldir.ControlDir.open_containing_tree_or_branch(directory))
0.431.1 by Jelmer Vernooij
Start work on propose command.
163
        if submit_branch is None:
164
            submit_branch = branch.get_submit_branch()
165
        if submit_branch is None:
0.432.7 by Jelmer Vernooij
propose works \o/
166
            submit_branch = branch.get_parent()
167
        if submit_branch is None:
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
168
            raise errors.BzrCommandError(
169
                gettext("No target location specified or remembered"))
0.431.1 by Jelmer Vernooij
Start work on propose command.
170
        else:
171
            target = _mod_branch.Branch.open(submit_branch)
0.432.7 by Jelmer Vernooij
propose works \o/
172
        if hoster is None:
173
            hoster = _mod_propose.get_hoster(target)
0.431.5 by Jelmer Vernooij
Initial work on gitlab support.
174
        else:
0.432.7 by Jelmer Vernooij
propose works \o/
175
            hoster = hoster.probe(target)
176
        if name is None:
177
            name = branch_name(branch)
0.431.20 by Jelmer Vernooij
publish -> publish_derived.
178
        remote_branch, public_branch_url = hoster.publish_derived(
7211.13.7 by Jelmer Vernooij
Fix formatting.
179
            branch, target, name=name, allow_lossy=not no_allow_lossy)
0.431.37 by Jelmer Vernooij
add a find-merge-proposal command.
180
        branch.set_push_location(remote_branch.user_url)
0.432.7 by Jelmer Vernooij
propose works \o/
181
        note(gettext('Published branch to %s') % public_branch_url)
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
182
        if prerequisite is not None:
183
            prerequisite_branch = _mod_branch.Branch.open(prerequisite)
184
        else:
185
            prerequisite_branch = None
0.432.7 by Jelmer Vernooij
propose works \o/
186
        proposal_builder = hoster.get_proposer(remote_branch, target)
0.432.10 by Jelmer Vernooij
More test fixes.
187
        if description is None:
188
            body = proposal_builder.get_initial_body()
189
            info = proposal_builder.get_infotext()
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
190
            info += "\n\n" + summarize_unmerged(
191
                branch, remote_branch, target, prerequisite_branch)
0.431.54 by Jelmer Vernooij
Include commit data in 'brz propose'.
192
            description = msgeditor.edit_commit_message(
193
                info, start_message=body)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
194
        try:
0.432.7 by Jelmer Vernooij
propose works \o/
195
            proposal = proposal_builder.create_proposal(
0.431.56 by Jelmer Vernooij
Add support for prerequisite branches.
196
                description=description, reviewers=reviewers,
7296.8.1 by Jelmer Vernooij
Add commit-message option to 'brz propose'.
197
                prerequisite_branch=prerequisite_branch, labels=labels,
198
                commit_message=commit_message)
0.431.2 by Jelmer Vernooij
Add launchpad implementation.
199
        except _mod_propose.MergeProposalExists as e:
200
            raise errors.BzrCommandError(gettext(
201
                'There is already a branch merge proposal: %s') % e.url)
0.432.10 by Jelmer Vernooij
More test fixes.
202
        note(gettext('Merge proposal created: %s') % proposal.url)
0.431.37 by Jelmer Vernooij
add a find-merge-proposal command.
203
204
205
class cmd_find_merge_proposal(Command):
206
    __doc__ = """Find a merge proposal.
207
208
    """
209
210
    takes_options = ['directory']
211
    takes_args = ['submit_branch?']
212
    aliases = ['find-proposal']
213
214
    def run(self, directory='.', submit_branch=None):
215
        tree, branch, relpath = controldir.ControlDir.open_containing_tree_or_branch(
216
            directory)
217
        public_location = branch.get_public_branch()
218
        if public_location:
219
            branch = _mod_branch.Branch.open(public_location)
220
        if submit_branch is None:
221
            submit_branch = branch.get_submit_branch()
222
        if submit_branch is None:
223
            submit_branch = branch.get_parent()
224
        if submit_branch is None:
0.431.55 by Jelmer Vernooij
Cope with lossy pushes better in `brz propose` texts.
225
            raise errors.BzrCommandError(
226
                gettext("No target location specified or remembered"))
0.431.37 by Jelmer Vernooij
add a find-merge-proposal command.
227
        else:
228
            target = _mod_branch.Branch.open(submit_branch)
229
        hoster = _mod_propose.get_hoster(branch)
0.431.67 by Jelmer Vernooij
Support multiple merge proposals per branch.
230
        for mp in hoster.iter_proposals(branch, target):
231
            self.outf.write(gettext('Merge proposal: %s\n') % mp.url)
0.431.47 by Jelmer Vernooij
Add github login command.
232
233
234
class cmd_github_login(Command):
235
    __doc__ = """Log into GitHub.
236
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
237
    When communicating with GitHub, some commands need to authenticate to
238
    GitHub.
0.431.47 by Jelmer Vernooij
Add github login command.
239
    """
240
241
    takes_args = ['username?']
242
243
    def run(self, username=None):
244
        from github import Github, GithubException
245
        from breezy.config import AuthenticationConfig
246
        authconfig = AuthenticationConfig()
247
        if username is None:
248
            username = authconfig.get_user(
7211.13.7 by Jelmer Vernooij
Fix formatting.
249
                'https', 'github.com', prompt=u'GitHub username', ask=True)
0.431.47 by Jelmer Vernooij
Add github login command.
250
        password = authconfig.get_password('https', 'github.com', username)
251
        client = Github(username, password)
252
        user = client.get_user()
253
        try:
254
            authorization = user.create_authorization(
7211.13.7 by Jelmer Vernooij
Fix formatting.
255
                scopes=['user', 'repo', 'delete_repo'], note='Breezy',
256
                note_url='https://github.com/breezy-team/breezy')
0.431.47 by Jelmer Vernooij
Add github login command.
257
        except GithubException as e:
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
258
            errs = e.data.get('errors', [])
259
            if errs:
260
                err_code = errs[0].get('code')
261
                if err_code == u'already_exists':
262
                    raise errors.BzrCommandError('token already exists')
0.431.47 by Jelmer Vernooij
Add github login command.
263
            raise errors.BzrCommandError(e.data['message'])
0.431.49 by Jelmer Vernooij
Store GitHub tokens in a magic file, for now.
264
        # TODO(jelmer): This should really use something in
265
        # AuthenticationConfig
266
        from .github import store_github_token
267
        store_github_token(scheme='https', host='github.com',
268
                           token=authorization.token)
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
269
270
271
class cmd_gitlab_login(Command):
272
    __doc__ = """Log into a GitLab instance.
273
274
    This command takes a GitLab instance URL (e.g. https://gitlab.com)
7241.1.2 by Jelmer Vernooij
Update help.
275
    as well as an optional private token. Private tokens can be created via the
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
276
    web UI.
277
278
    :Examples:
279
7241.1.2 by Jelmer Vernooij
Update help.
280
      Log into GNOME's GitLab (prompts for a token):
281
282
         brz gitlab-login https://gitlab.gnome.org/
283
284
      Log into Debian's salsa, using a token created earlier:
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
285
286
         brz gitlab-login https://salsa.debian.org if4Theis6Eich7aef0zo
287
    """
288
7241.1.1 by Jelmer Vernooij
Support interactive login for GitLab sites.
289
    takes_args = ['url', 'private_token?']
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
290
291
    takes_options = [
7211.13.7 by Jelmer Vernooij
Fix formatting.
292
        Option('name', help='Name for GitLab site in configuration.',
293
               type=str),
294
        Option('no-check',
295
               "Don't check that the token is valid."),
296
        ]
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
297
7241.1.1 by Jelmer Vernooij
Support interactive login for GitLab sites.
298
    def run(self, url, private_token=None, name=None, no_check=False):
299
        from breezy import ui
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
300
        from .gitlabs import store_gitlab_token
301
        if name is None:
302
            try:
303
                name = urlutils.parse_url(url)[3].split('.')[-2]
304
            except (ValueError, IndexError):
305
                raise errors.BzrCommandError(
306
                    'please specify a site name with --name')
7241.1.1 by Jelmer Vernooij
Support interactive login for GitLab sites.
307
        if private_token is None:
308
            note("Please visit %s to obtain a private token.",
309
                 urlutils.join(url, "profile/personal_access_tokens"))
310
            private_token = ui.ui_factory.get_password(u'Private token')
0.431.59 by Jelmer Vernooij
Add gitlab-login command.
311
        if not no_check:
312
            from gitlab import Gitlab
313
            gl = Gitlab(url=url, private_token=private_token)
314
            gl.auth()
315
        store_gitlab_token(name=name, url=url, private_token=private_token)
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
316
317
318
class cmd_my_merge_proposals(Command):
0.431.66 by Jelmer Vernooij
Add support for status argument.
319
    __doc__ = """List all merge proposals owned by the logged-in user.
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
320
321
    """
322
0.431.69 by Jelmer Vernooij
Make 'brz my-proposals' hidden for the moment.
323
    hidden = True
324
0.431.66 by Jelmer Vernooij
Add support for status argument.
325
    takes_options = [
326
        RegistryOption.from_kwargs(
327
            'status',
328
            title='Proposal Status',
329
            help='Only include proposals with specified status.',
330
            value_switches=True,
331
            enum_switch=True,
332
            all='All merge proposals',
333
            open='Open merge proposals',
334
            merged='Merged merge proposals',
335
            closed='Closed merge proposals')]
336
337
    def run(self, status='open'):
0.431.63 by Jelmer Vernooij
Add 'brz my-proposals' command.
338
        from .propose import hosters
339
        for name, hoster_cls in hosters.items():
340
            for instance in hoster_cls.iter_instances():
0.431.66 by Jelmer Vernooij
Add support for status argument.
341
                for mp in instance.iter_my_proposals(status=status):
0.431.65 by Jelmer Vernooij
Avoid print.
342
                    self.outf.write('%s\n' % mp.url)
7296.9.1 by Jelmer Vernooij
Add 'brz land' subcommand.
343
344
345
class cmd_land_merge_proposal(Command):
346
    __doc__ = """Land a merge proposal."""
347
348
    takes_args = ['url']
349
    takes_options = [
350
        Option('message', help='Commit message to use.', type=str)]
351
352
    def run(self, url, message=None):
353
        from .propose import get_proposal_by_url
354
        proposal = get_proposal_by_url(url)
355
        proposal.merge(commit_message=message)