149
157
help='Labels to apply.'),
150
158
Option('no-allow-lossy',
151
159
help='Allow fallback to lossy push, if necessary.'),
160
Option('allow-collaboration',
161
help='Allow collaboration from target branch maintainer(s)'),
162
Option('allow-empty',
163
help='Do not prevent empty merge proposals.'),
164
Option('overwrite', help="Overwrite existing commits."),
153
166
takes_args = ['submit_branch?']
157
170
def run(self, submit_branch=None, directory='.', hoster=None,
158
171
reviewers=None, name=None, no_allow_lossy=False, description=None,
159
labels=None, prerequisite=None, commit_message=None, wip=False):
172
labels=None, prerequisite=None, commit_message=None, wip=False,
173
allow_collaboration=False, allow_empty=False, overwrite=False):
160
174
tree, branch, relpath = (
161
175
controldir.ControlDir.open_containing_tree_or_branch(directory))
162
176
if submit_branch is None:
164
178
if submit_branch is None:
165
179
submit_branch = branch.get_parent()
166
180
if submit_branch is None:
167
raise errors.BzrCommandError(
181
raise errors.CommandError(
168
182
gettext("No target location specified or remembered"))
170
target = _mod_branch.Branch.open(submit_branch)
183
target = _mod_branch.Branch.open(submit_branch)
185
_check_already_merged(branch, target)
171
186
if hoster is None:
172
187
hoster = _mod_propose.get_hoster(target)
176
191
name = branch_name(branch)
177
192
remote_branch, public_branch_url = hoster.publish_derived(
178
branch, target, name=name, allow_lossy=not no_allow_lossy)
193
branch, target, name=name, allow_lossy=not no_allow_lossy,
179
195
branch.set_push_location(remote_branch.user_url)
180
196
branch.set_submit_branch(target.user_url)
181
197
note(gettext('Published branch to %s') % public_branch_url)
232
248
self.outf.write(gettext('Merge proposal: %s\n') % mp.url)
235
class cmd_github_login(Command):
236
__doc__ = """Log into GitHub.
238
When communicating with GitHub, some commands need to authenticate to
242
takes_args = ['username?']
244
def run(self, username=None):
245
from github import Github, GithubException
246
from breezy.config import AuthenticationConfig
247
authconfig = AuthenticationConfig()
249
username = authconfig.get_user(
250
'https', 'github.com', prompt=u'GitHub username', ask=True)
251
password = authconfig.get_password('https', 'github.com', username)
252
client = Github(username, password)
253
user = client.get_user()
255
authorization = user.create_authorization(
256
scopes=['user', 'repo', 'delete_repo'], note='Breezy',
257
note_url='https://github.com/breezy-team/breezy')
258
except GithubException as e:
259
errs = e.data.get('errors', [])
261
err_code = errs[0].get('code')
262
if err_code == u'already_exists':
263
raise errors.BzrCommandError('token already exists')
264
raise errors.BzrCommandError(e.data['message'])
265
# TODO(jelmer): This should really use something in
266
# AuthenticationConfig
267
from .github import store_github_token
268
store_github_token(scheme='https', host='github.com',
269
token=authorization.token)
272
class cmd_gitlab_login(Command):
273
__doc__ = """Log into a GitLab instance.
275
This command takes a GitLab instance URL (e.g. https://gitlab.com)
276
as well as an optional private token. Private tokens can be created via the
281
Log into GNOME's GitLab (prompts for a token):
283
brz gitlab-login https://gitlab.gnome.org/
285
Log into Debian's salsa, using a token created earlier:
287
brz gitlab-login https://salsa.debian.org if4Theis6Eich7aef0zo
290
takes_args = ['url', 'private_token?']
293
Option('name', help='Name for GitLab site in configuration.',
296
"Don't check that the token is valid."),
299
def run(self, url, private_token=None, name=None, no_check=False):
300
from breezy import ui
301
from .gitlabs import store_gitlab_token
304
name = urlutils.parse_url(url)[3].split('.')[-2]
305
except (ValueError, IndexError):
306
raise errors.BzrCommandError(
307
'please specify a site name with --name')
308
if private_token is None:
309
note("Please visit %s to obtain a private token.",
310
urlutils.join(url, "profile/personal_access_tokens"))
311
private_token = ui.ui_factory.get_password(u'Private token')
313
from breezy.transport import get_transport
314
from .gitlabs import GitLab
315
GitLab(get_transport(url), private_token=private_token)
316
store_gitlab_token(name=name, url=url, private_token=private_token)
319
251
class cmd_my_merge_proposals(Command):
320
252
__doc__ = """List all merge proposals owned by the logged-in user.
334
267
all='All merge proposals',
335
268
open='Open merge proposals',
336
269
merged='Merged merge proposals',
337
closed='Closed merge proposals')]
339
def run(self, status='open', verbose=False):
340
for name, hoster_cls in _mod_propose.hosters.items():
341
for instance in hoster_cls.iter_instances():
270
closed='Closed merge proposals'),
273
help='Use the hoster.',
274
lazy_registry=('breezy.propose', 'hosters')),
277
def run(self, status='open', verbose=False, hoster=None, base_url=None):
279
for instance in _mod_propose.iter_hoster_instances(hoster=hoster):
280
if base_url is not None and instance.base_url != base_url:
342
283
for mp in instance.iter_my_proposals(status=status):
343
284
self.outf.write('%s\n' % mp.url)
346
'(Merging %s into %s)\n' %
347
(mp.get_source_branch_url(),
348
mp.get_target_branch_url()))
286
source_branch_url = mp.get_source_branch_url()
287
if source_branch_url:
289
'(Merging %s into %s)\n' %
291
mp.get_target_branch_url()))
294
'(Merging into %s)\n' %
295
mp.get_target_branch_url())
349
296
description = mp.get_description()
351
298
self.outf.writelines(
353
300
for l in description.splitlines()])
354
301
self.outf.write('\n')
302
except _mod_propose.HosterLoginRequired as e:
303
warning('Skipping %r, login required.', instance)
357
306
class cmd_land_merge_proposal(Command):
364
313
def run(self, url, message=None):
365
314
proposal = _mod_propose.get_proposal_by_url(url)
366
315
proposal.merge(commit_message=message)
318
class cmd_hosters(Command):
319
__doc__ = """List all known hosting sites and user details."""
324
for instance in _mod_propose.iter_hoster_instances():
325
current_user = instance.get_current_user()
326
if current_user is not None:
327
current_user_url = instance.get_user_url(current_user)
328
if current_user_url is not None:
330
gettext('%s (%s) - user: %s (%s)\n') % (
331
instance.name, instance.base_url,
332
current_user, current_user_url))
335
gettext('%s (%s) - user: %s\n') % (
336
instance.name, instance.base_url,
340
gettext('%s (%s) - not logged in\n') % (
341
instance.name, instance.base_url))