16
16
{self} [--path=<path>] [-v...] <target> add <user>/<repo> [<name>] [--tracking=<branch>] [-a]
17
17
{self} [--path=<path>] [-v...] <target> request (list|ls)
18
18
{self} [--path=<path>] [-v...] <target> request fetch <request> [-f]
19
- {self} [--path=<path>] [-v...] <target> request create <title> [--message=<message>]
20
- {self} [--path=<path>] [-v...] <target> request create <local_branch> <title> [--message=<message>]
21
- {self} [--path=<path>] [-v...] <target> request create <remote_branch> <local_branch> <title> [--message=<message>]
19
+ {self} [--path=<path>] [-v...] <target> request create [--title= <title>] [--message=<message>]
20
+ {self} [--path=<path>] [-v...] <target> request create <local_branch> [--title= <title>] [--message=<message>]
21
+ {self} [--path=<path>] [-v...] <target> request create <remote_branch> <local_branch> [--title= <title>] [--message=<message>]
22
22
{self} [--path=<path>] [-v...] <target> request <user>/<repo> (list|ls)
23
23
{self} [--path=<path>] [-v...] <target> request <user>/<repo> fetch <request> [-f]
24
- {self} [--path=<path>] [-v...] <target> request <user>/<repo> create <title> [--branch=<remote>] [--message=<message>]
25
- {self} [--path=<path>] [-v...] <target> request <user>/<repo> create <local_branch> <title> [--branch=<remote>] [--message=<message>]
26
- {self} [--path=<path>] [-v...] <target> request <user>/<repo> create <remote_branch> <local_branch> <title> [--branch=<remote>] [--message=<message>]
24
+ {self} [--path=<path>] [-v...] <target> request <user>/<repo> create [--title= <title>] [--branch=<remote>] [--message=<message>]
25
+ {self} [--path=<path>] [-v...] <target> request <user>/<repo> create <local_branch> [--title= <title>] [--branch=<remote>] [--message=<message>]
26
+ {self} [--path=<path>] [-v...] <target> request <user>/<repo> create <remote_branch> <local_branch> [--title= <title>] [--branch=<remote>] [--message=<message>]
27
27
{self} [--path=<path>] [-v...] <target> (gist|snippet) (list|ls) [<gist>]
28
28
{self} [--path=<path>] [-v...] <target> (gist|snippet) clone <gist>
29
29
{self} [--path=<path>] [-v...] <target> (gist|snippet) fetch <gist> [<gist_file>]
89
89
--secret Do not publicize gist when pushing
90
90
91
91
Options for request:
92
- <title> Title to give to the request for merge
92
+ -t,--title= <title> Title to give to the request for merge
93
93
-m,--message=<message> Description for the request for merge
94
94
95
95
Configuration options:
137
137
from .exceptions import ArgumentError , ResourceNotFoundError
138
138
from .services .service import RepositoryService
139
139
140
+ from .tools import print_tty , print_iter , loop_input , confirm
140
141
from .kwargparse import KeywordArgumentParser , store_parameter , register_action
141
142
142
143
from git import Repo , Git
143
- from git .exc import InvalidGitRepositoryError , NoSuchPathError
144
+ from git .exc import InvalidGitRepositoryError , NoSuchPathError , BadName
144
145
145
146
import re
146
147
147
148
EXTRACT_URL_RE = re .compile ('[^:]*(://|@)[^/]*/' )
148
149
149
- def confirm (what , where ):
150
- '''
151
- Method to show a CLI based confirmation message, waiting for a yes/no answer.
152
- "what" and "where" are used to better define the message.
153
- '''
154
- ans = input ('Are you sure you want to delete the '
155
- '{} {} from the service?\n [yN]> ' .format (what , where ))
156
- if 'y' in ans :
157
- ans = input ('Are you really sure? there\' s no coming back!\n '
158
- '[type \' burn!\' to proceed]> ' )
159
- if 'burn!' != ans :
160
- return False
161
- else :
162
- return False
163
- return True
164
-
165
150
166
151
class GitRepoRunner (KeywordArgumentParser ):
167
152
@@ -283,8 +268,7 @@ def store_gitconfig(self, val):
283
268
@register_action ('ls' )
284
269
@register_action ('list' )
285
270
def do_list (self ):
286
- service = self .get_service (False )
287
- service .list (self .user , self .long )
271
+ print_iter (self .get_service (False ).list (self .user , self .long ))
288
272
return 0
289
273
290
274
@register_action ('add' )
@@ -403,26 +387,70 @@ def do_open(self):
403
387
@register_action ('request' , 'list' )
404
388
def do_request_list (self ):
405
389
service = self .get_service (lookup_repository = self .repo_slug == None )
406
- log .info ('List of open requests to merge:' )
407
- log .info (" {}\t {}\t {}" .format ('id' , 'title' .ljust (60 ), 'URL' ))
408
- for pr in service .request_list (self .user_name , self .repo_name ):
409
- print ("{}\t {}\t {}" .format (pr [0 ].rjust (3 ), pr [1 ][:60 ].ljust (60 ), pr [2 ]))
390
+ print_tty ('List of open requests to merge:' )
391
+ print_iter (service .request_list (self .user_name , self .repo_name ))
410
392
return 0
411
393
412
394
@register_action ('request' , 'create' )
413
395
def do_request_create (self ):
396
+ def request_edition (repository , from_branch ):
397
+ try :
398
+ commit = repository .commit (from_branch )
399
+ title , * body = commit .message .split ('\n ' )
400
+ except BadName :
401
+ log .error ('Couldn\' t find local source branch {}' .format (from_branch ))
402
+ return None
403
+ from tempfile import NamedTemporaryFile
404
+ from subprocess import call
405
+ with NamedTemporaryFile (
406
+ prefix = 'git-repo-issue-' ,
407
+ suffix = '.md' ,
408
+ mode = 'w+b' ) as request_file :
409
+ request_file .write ((
410
+ '# Request for Merge Title ##########################\n '
411
+ '{}\n '
412
+ '\n '
413
+ '# Request for Merge Body ###########################\n '
414
+ '{}\n '
415
+ '####################################################\n '
416
+ '## Filled with commit:\n '
417
+ '## {}\n '
418
+ '####################################################\n '
419
+ '## * All lines starting with # will be ignored.\n '
420
+ '## * First non-ignored line is the title of the request.\n '
421
+ ).format (title , '\n ' .join (body ), commit .name_rev ).encode ('utf-8' ))
422
+ request_file .flush ()
423
+ rv = call ("{} {}" .format (os .environ ['EDITOR' ], request_file .name ), shell = True )
424
+ if rv != 0 :
425
+ raise ArgumentError ("Aborting request, as editor exited abnormally." )
426
+ request_file .seek (0 )
427
+ request_message = map (lambda l : l .decode ('utf-8' ),
428
+ filter (lambda l : not l .strip ().startswith (b'#' ), request_file .readlines ()))
429
+ try :
430
+ title = next (request_message )
431
+ body = '' .join (request_message )
432
+ except Exception :
433
+ raise ResourceError ("Format of the request message cannot be parsed." )
434
+
435
+ return title , body
436
+
437
+
414
438
service = self .get_service (resolve_targets = ('upstream' , '{service}' , 'origin' ))
439
+
415
440
new_request = service .request_create (self .user_name ,
416
441
self .repo_name ,
417
442
self .local_branch ,
418
443
self .remote_branch ,
419
444
self .title ,
420
445
self .message ,
421
- self .repo_slug != None )
446
+ self .repo_slug != None ,
447
+ request_edition )
422
448
log .info ('Successfully created request of `{local}` onto `{}:{remote}`, with id `{ref}`!' .format (
423
449
'/' .join ([self .user_name , self .repo_name ]),
424
450
** new_request )
425
451
)
452
+ if 'url' in new_request :
453
+ log .info ('available at: {url}' .format (** new_request ))
426
454
return 0
427
455
428
456
@register_action ('request' , 'fetch' )
@@ -442,16 +470,7 @@ def do_request_fetch(self):
442
470
@register_action ('snippet' , 'list' )
443
471
def do_gist_list (self ):
444
472
service = self .get_service (lookup_repository = False )
445
- if 'github' == service .name and self .gist_ref :
446
- log .info ("{:15}\t {:>7}\t {}" .format ('language' , 'size' , 'name' ))
447
- else :
448
- log .info ("{:56}\t {}" .format ('id' , 'title' .ljust (60 )))
449
- if self .gist_ref :
450
- for gist_file in service .gist_list (self .gist_ref ):
451
- print ("{:15}\t {:7}\t {}" .format (* gist_file ))
452
- else :
453
- for gist in service .gist_list ():
454
- print ( "{:56}\t {}" .format (gist [0 ], gist [1 ]))
473
+ print_iter (service .gist_list (self .gist_ref or None ))
455
474
return 0
456
475
457
476
@register_action ('gist' , 'clone' )
@@ -496,12 +515,6 @@ def do_gist_delete(self):
496
515
def do_config (self ):
497
516
from getpass import getpass
498
517
499
- def loop_input (* args , method = input , ** kwarg ):
500
- out = ''
501
- while len (out ) == 0 :
502
- out = method (* args , ** kwarg )
503
- return out
504
-
505
518
def setup_service (service ):
506
519
new_conf = dict (
507
520
fqdn = None ,
@@ -592,7 +605,8 @@ def cli(): #pragma: no cover
592
605
sys .exit (main (docopt (__doc__ .format (self = sys .argv [0 ].split ('/' )[- 1 ], version = __version__ ))))
593
606
finally :
594
607
# Whatever happens, make sure that the cursor reappears with some ANSI voodoo
595
- sys .stdout .write ('\033 [?25h' )
608
+ if sys .stdout .isatty ():
609
+ sys .stdout .write ('\033 [?25h' )
596
610
597
611
if __name__ == '__main__' : #pragma: no cover
598
612
cli ()
0 commit comments