|
29 | 29 | {self} [--path=<path>] [-v...] <target> (gist|snippet) fetch <gist> [<gist_file>]
|
30 | 30 | {self} [--path=<path>] [-v...] <target> (gist|snippet) create [--secret] <description> [<gist_path> <gist_path>...]
|
31 | 31 | {self} [--path=<path>] [-v...] <target> (gist|snippet) delete <gist> [-f]
|
| 32 | + {self} [--path=<path>] [-v...] <target> issue (list|ls) [--filter=<filter>] |
| 33 | + {self} [--path=<path>] [-v...] <target> issue (list|ls) [<action>|<issue_id>] |
| 34 | + {self} [--path=<path>] [-v...] <target> issue get <action> [--filter=<filter>] [<issue_id> <issue_id>...] |
| 35 | + {self} [--path=<path>] [-v...] <target> issue set <action> <value> [--filter=<filter>] [<issue_id> <issue_id>...] |
| 36 | + {self} [--path=<path>] [-v...] <target> issue unset <action> [--filter=<filter>] [<issue_id> <issue_id>...] |
| 37 | + {self} [--path=<path>] [-v...] <target> issue toggle <action> <value> [--filter=<filter>] [<issue_id> <issue_id>...] |
| 38 | + {self} [--path=<path>] [-v...] <target> issue edit [<issue_id>] |
| 39 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> (list|ls) [--filter=<filter>] |
| 40 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> (list|ls) [<action>|<issue_id>] |
| 41 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> get <action> [--filter=<filter>] [<issue_id> <issue_id>...] |
| 42 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> set <action> [<value>] [--filter=<filter>] [<issue_id> <issue_id>...] |
| 43 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> unset <action> [<value>] [--filter=<filter>] [<issue_id> <issue_id>...] |
| 44 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> toggle <action> [<value>] [--filter=<filter>] [<issue_id> <issue_id>...] |
| 45 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> edit [<issue_id>] |
| 46 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> add <action> <value> |
| 47 | + {self} [--path=<path>] [-v...] <target> issue <user>/<repo> delete [-f] <action> |
32 | 48 | {self} [--path=<path>] [-v...] <target> config [--config=<gitconfig>]
|
33 | 49 | {self} [-v...] config [--config=<gitconfig>]
|
34 | 50 | {self} --help
|
|
44 | 60 | list Lists the repositories for a given user
|
45 | 61 | gist Manages gist files
|
46 | 62 | request Handles requests for merge
|
| 63 | + issue Handles issues |
47 | 64 | open Open the given or current repository in a browser
|
48 | 65 | config Run authentication process and configure the tool
|
49 | 66 |
|
|
92 | 109 | <title> Title to give to the request for merge
|
93 | 110 | -m,--message=<message> Description for the request for merge
|
94 | 111 |
|
| 112 | +Options for issues: |
| 113 | + get Gets a value for the given action listed below |
| 114 | + set Sets a value for the given action listed below |
| 115 | + unset Unsets a value for the given action listed below |
| 116 | + toggle Toggles a value for the given action listed below |
| 117 | + <action> Action: label, milestone or mark |
| 118 | + <value> Value for what shall be set |
| 119 | + --filter=<filter> Filters the list of issues [Default: ] |
| 120 | + <issue_id> Issue's number |
| 121 | +
|
95 | 122 | Configuration options:
|
96 | 123 | alias Name to use for the git remote
|
97 | 124 | fqdn URL of the repository
|
|
146 | 173 |
|
147 | 174 | EXTRACT_URL_RE = re.compile('[^:]*(://|@)[^/]*/')
|
148 | 175 |
|
| 176 | +def blue(s): |
| 177 | + return '\033[94m{}\033[0m'.format(s) |
| 178 | +def green(s): |
| 179 | + return '\033[92m{}\033[0m'.format(s) |
| 180 | +def red(s): |
| 181 | + return '\033[91m{}\033[0m'.format(s) |
| 182 | + |
149 | 183 | def confirm(what, where):
|
150 | 184 | '''
|
151 | 185 | Method to show a CLI based confirmation message, waiting for a yes/no answer.
|
@@ -262,6 +296,14 @@ def set_branch(self, branch):
|
262 | 296 |
|
263 | 297 | self.branch = branch
|
264 | 298 |
|
| 299 | + @store_parameter('<action>') |
| 300 | + def set_action(self, action): |
| 301 | + self.action = action |
| 302 | + |
| 303 | + @store_parameter('<issue_id>') |
| 304 | + def set_issue_action(self, issue_id): |
| 305 | + self.issues = issue_id |
| 306 | + |
265 | 307 | @store_parameter('<repo>')
|
266 | 308 | def set_target_repo(self, repo):
|
267 | 309 | self.target_repo = repo
|
@@ -492,6 +534,185 @@ def do_gist_delete(self):
|
492 | 534 | log.info('Successfully deleted gist!')
|
493 | 535 | return 0
|
494 | 536 |
|
| 537 | + '''Issues''' |
| 538 | + |
| 539 | + @register_action('issue', 'ls') |
| 540 | + @register_action('issue', 'list') |
| 541 | + def do_issue_list(self): |
| 542 | + service = self.get_service() |
| 543 | + if self.action: |
| 544 | + if self.action in ('milestones', 'milestone', 'm'): |
| 545 | + milestones = service.issue_milestone_list(self.user_name, self.repo_name) |
| 546 | + print(blue(next(milestones)), file=sys.stderr) |
| 547 | + for milestone in milestones: |
| 548 | + print(milestone) |
| 549 | + return 0 |
| 550 | + elif self.action in ('labels', 'label', 'l'): |
| 551 | + labels = service.issue_label_list(self.user_name, self.repo_name) |
| 552 | + print(blue(next(labels)), file=sys.stderr) |
| 553 | + for label in labels: |
| 554 | + print(label) |
| 555 | + return 0 |
| 556 | + elif self.action in ('mark', 'm'): |
| 557 | + print('opened\nclosed\nread') |
| 558 | + return 0 |
| 559 | + else: |
| 560 | + issue = service.issue_grab(self.user_name, self.repo_name, self.action) |
| 561 | + print('\n'.join([ |
| 562 | + 'Issue #{} ({}) by @{}'.format( |
| 563 | + issue['id'], |
| 564 | + green(issue['state']) if issue['state'] == 'open' else red(issue['state']), |
| 565 | + issue['poster']), |
| 566 | + 'Created at:\t{} {}'.format( |
| 567 | + issue['creation'], |
| 568 | + '' if not issue['state'] == 'closed' else 'and closed at: {} by @{}'.format( |
| 569 | + issue['closed_at'], issue['closed_by'] |
| 570 | + ) |
| 571 | + ), |
| 572 | + 'Assigned:\t{}'.format('@{}'.format(issue['assignee']) or 'ø'), |
| 573 | + 'Milestone:\t{}'.format(issue['milestone']), |
| 574 | + 'Labels:\t\t{}'.format(', '.join(issue['labels'])), |
| 575 | + 'URI:\t\t{}'.format(issue['uri']), |
| 576 | + 'Title:\t\t{}'.format(issue['title']), |
| 577 | + 'Body:', '', |
| 578 | + issue['body'], |
| 579 | + ]) |
| 580 | + ) |
| 581 | + else: |
| 582 | + |
| 583 | + |
| 584 | + def format_issue(issue): |
| 585 | + if issue[0] == None: |
| 586 | + status_icon = ' ' |
| 587 | + elif not issue[5]: |
| 588 | + status_icon = green('📖') if issue[0] else red('📕') |
| 589 | + else: |
| 590 | + status_icon = green('📦') if issue[0] else red('📦') |
| 591 | + number = issue[1].rjust(3) |
| 592 | + labels = issue[2][:20].ljust(20) + ("…" if len(issue[2]) > 20 else "") |
| 593 | + title = issue[3][:60].ljust(60) + ("…" if len(issue[3]) > 60 else "") |
| 594 | + uri = issue[4] |
| 595 | + return '{} {}\t{}\t{}\t{}'.format(status_icon, number, labels, title, uri) |
| 596 | + |
| 597 | + issues = service.issue_list(self.user_name, self.repo_name, self.filter or '') |
| 598 | + print(blue(format_issue(next(issues))), file=sys.stderr) |
| 599 | + for issue in issues: |
| 600 | + print(format_issue(issue)) |
| 601 | + return 0 |
| 602 | + |
| 603 | + def check_issues_parameter(self): |
| 604 | + if self.issues == [] and self.filter == '': |
| 605 | + if self.value: |
| 606 | + self.issues = [self.value] |
| 607 | + self.value = None |
| 608 | + else: |
| 609 | + raise ArgumentError("Need at least one issue or a --filter parameter") |
| 610 | + if len(self.issues) == 1 and self.issues[0] in ('*', 'all'): |
| 611 | + self.issues = [] |
| 612 | + |
| 613 | + @register_action('issue', 'get') |
| 614 | + def do_issue_get(self): |
| 615 | + service = self.get_service() |
| 616 | + if len(self.issues) == 1 and self.issues[0] == '-': |
| 617 | + self.user_name, self.repo_name, self.issues = service.issue_extract_from_file(sys.stdin) |
| 618 | + issue_data = service.issue_get(self.user_name, self.repo_name, self.action, self.filter or '', self.issues) |
| 619 | + print(blue(next(issue_data)), file=sys.stderr) |
| 620 | + for data in issue_data: |
| 621 | + print('{}'.format(data)) |
| 622 | + |
| 623 | + @register_action('issue', 'set') |
| 624 | + def do_issue_set(self): |
| 625 | + self.check_issues_parameter() |
| 626 | + service = self.get_service() |
| 627 | + if len(self.issues) == 1 and self.issues[0] == '-': |
| 628 | + self.user_name, self.repo_name, self.issues = service.issue_extract_from_file(sys.stdin) |
| 629 | + rv = 1 |
| 630 | + if all(service.issue_set(self.user_name, self.repo_name, self.action, self.value, self.filter or '', self.issues)): |
| 631 | + rv = 0 |
| 632 | + self.do_issue_get() |
| 633 | + return rv |
| 634 | + |
| 635 | + @register_action('issue', 'unset') |
| 636 | + def do_issue_unset(self): |
| 637 | + self.check_issues_parameter() |
| 638 | + service = self.get_service() |
| 639 | + if len(self.issues) == 1 and self.issues[0] == '-': |
| 640 | + self.user_name, self.repo_name, self.issues = service.issue_extract_from_file(sys.stdin) |
| 641 | + rv = 1 |
| 642 | + if all(service.issue_unset(self.user_name, self.repo_name, self.action, self.value, self.filter or '', self.issues)): |
| 643 | + rv = 0 |
| 644 | + self.do_issue_get() |
| 645 | + return rv |
| 646 | + |
| 647 | + @register_action('issue', 'toggle') |
| 648 | + def do_issue_toggle(self): |
| 649 | + self.check_issues_parameter() |
| 650 | + service = self.get_service() |
| 651 | + if len(self.issues) == 1 and self.issues[0] == '-': |
| 652 | + self.user_name, self.repo_name, self.issues = service.issue_extract_from_file(sys.stdin) |
| 653 | + rv = 1 |
| 654 | + if all(service.issue_toggle(self.user_name, self.repo_name, self.action, self.value, self.filter or '', self.issues)): |
| 655 | + rv = 0 |
| 656 | + self.do_issue_get() |
| 657 | + return rv |
| 658 | + |
| 659 | + @register_action('issue', 'edit') |
| 660 | + def do_issue_edit(self): |
| 661 | + do_ask=False |
| 662 | + if len(self.issues) == 1 and self.issues[0] == '-': |
| 663 | + self.user_name, self.repo_name, self.issues = service.issue_extract_from_file(sys.stdin) |
| 664 | + do_ask=True |
| 665 | + |
| 666 | + def edit_issue(title, body): |
| 667 | + from tempfile import NamedTemporaryFile |
| 668 | + from subprocess import call |
| 669 | + with NamedTemporaryFile( |
| 670 | + prefix='git-repo-issue-', |
| 671 | + suffix='.md', |
| 672 | + mode='w+b') as issue_file: |
| 673 | + issue_file.write('Title: {}\n\nBody:\n{}\n'.format(title, body).encode('utf-8')) |
| 674 | + issue_file.flush() |
| 675 | + call("{} {}".format(os.environ['EDITOR'], issue_file.name), shell=True) |
| 676 | + issue_file.seek(0) |
| 677 | + updated_issue = issue_file.read().decode('utf-8') |
| 678 | + try: |
| 679 | + _, updated_issue = updated_issue.split('Title: ') |
| 680 | + title, body, *tail = updated_issue.split('\n\nBody:\n') |
| 681 | + body = ''.join([body]+tail) |
| 682 | + except Exception: |
| 683 | + raise ResourceError("Format of the modified issue cannot be parsed.") |
| 684 | + |
| 685 | + print('New issue\'s details:') |
| 686 | + print('Title: {}'.format(title)) |
| 687 | + print('Body:\n{}'.format(body)) |
| 688 | + if do_ask and input('Do you confirm it\'s ok? [Yn] ').lower().startswith('n'): |
| 689 | + return None |
| 690 | + return {'title': title, 'body': body} |
| 691 | + |
| 692 | + service = self.get_service() |
| 693 | + if service.issue_edit(self.user_name, self.repo_name, self.issues[0], edit_issue): |
| 694 | + return 0 |
| 695 | + return 1 |
| 696 | + |
| 697 | + @register_action('issue', 'add') |
| 698 | + def do_issue_action_add(self): |
| 699 | + service = self.get_service() |
| 700 | + if service.issue_action_add(self.user_name, self.repo_name, self.action, self.value): |
| 701 | + return 0 |
| 702 | + return 1 |
| 703 | + |
| 704 | + @register_action('issue', 'del') |
| 705 | + def do_issue_action_delete(self): |
| 706 | + service = self.get_service() |
| 707 | + if not self.force: # pragma: no cover |
| 708 | + if not confirm('Action {} will be removed'.format(self.action), self.repo_slug): |
| 709 | + return 0 |
| 710 | + if service.issue_action_del(self.user_name, self.repo_name, self.action, self.value): |
| 711 | + return 0 |
| 712 | + return 1 |
| 713 | + |
| 714 | + '''Configuration''' |
| 715 | + |
495 | 716 | @register_action('config')
|
496 | 717 | def do_config(self):
|
497 | 718 | from getpass import getpass
|
|
0 commit comments