def cmd_init(config: Config, args: List[str]) -> None: cli.arg_disallow_trailing(args) if ROOT.exists(): print(f'There is already an ishu project in {ROOT}') else: ROOT.mkdir(exist_ok=True) print(f'Created ishu project in {ROOT}')
def cmd_blocked_by(config: Config, args: List[str]) -> None: # Args blocked_id: IssueID blocking_id: IssueID # Parse args blocked_id = _arg_issue_id(args, config, restrict_to_own=True) blocking_id = _arg_issue_id(args, config) cli.arg_disallow_trailing(args) if blocked_id == blocking_id: error("an issue can't block itself") # Run command issue = Issue.load_from_id(blocked_id) other_issue = Issue.load_from_id(blocking_id) s_blocked_id = f'#{blocked_id.shorten(config)}' s_blocking_id = f'#{blocking_id.shorten(config)}' if blocking_id in issue.blocked_by: print(f'Issue {s_blocked_id} is already blocked by {s_blocking_id}, ' f'no changes were made.') elif blocked_id in other_issue.blocked_by: error(f'blocking loop detected! Issue {s_blocking_id} is already ' f'blocked by {s_blocked_id}!') else: issue.blocked_by.add(blocking_id) issue.save() print(f'Issue {s_blocked_id} marked as blocked by {s_blocking_id}.')
def cmd_info(config: Config, args: List[str]) -> None: # Args issue_id: IssueID # Parse args issue_id = _arg_issue_id(args, config) cli.arg_disallow_trailing(args) # Run command issue = Issue.load_from_id(issue_id) print(issue.info(config))
def cmd_configure(config: Config, args: List[str]) -> None: # Args list_settings = False get_setting: Optional[str] = None set_setting: Optional[Tuple[str, str]] = None # Parse args if not args: list_settings = True else: arg = args.pop(0) cli.arg_disallow_positional(arg) if arg in {'-l', '--list'}: list_settings = True elif arg in {'-g', '--get'}: try: get_setting = args.pop(0) except IndexError: error('--get needs an argument') elif arg in {'-s', '--set'}: try: set_setting = (args.pop(0), args.pop(0)) except IndexError: error('--set needs two arguments') else: cli.arg_unknown_optional(arg) cli.arg_disallow_trailing(args) # List settings if list_settings: print('Settings:') for key in sorted(Config.editable_settings): print(f' {key} = {config[key] if config else ""}') # Get settings elif get_setting: if get_setting not in Config.editable_settings: error(f'unknown setting: {get_setting}') else: print(f'{get_setting} = {config[get_setting]}') # Set settings elif set_setting: key, value = set_setting if key not in Config.editable_settings: error(f'unknown setting: {key}') try: updated_config: Config config[key] = value updated_config = config except InvalidConfigException as e: print(f'error in config value: {e!r}') else: print(f'{key} -> {value}') updated_config.save() print('Config saved')
def cmd_wontfix(config: Config, args: List[str]) -> None: # Args issue_id: IssueID comment: Optional[str] = None # Parse args issue_id = _arg_issue_id(args, config) if args: comment = args.pop(0) cli.arg_disallow_trailing(args) # Run command _change_status(config.user, issue_id, IssueStatus.WONTFIX, 'marked as wontfix', 'closed and marked as wontfix', comment_text=comment)
def cmd_comment(config: Config, args: List[str]) -> None: # Args issue_id: IssueID message: str # Parse args issue_id = _arg_issue_id(args, config) message = cli.arg_positional(args, 'message') cli.arg_disallow_trailing(args) # Run command comment = Comment(issue_id=issue_id, user=config.user, created=datetime.now(timezone.utc), message=message) comment.save() print('Comment added')
def cmd_unblock(config: Config, args: List[str]) -> None: # Args blocked_id: IssueID blocking_id: IssueID # Parse args blocked_id = _arg_issue_id(args, config, restrict_to_own=True) blocking_id = _arg_issue_id(args, config) cli.arg_disallow_trailing(args) if blocked_id == blocking_id: error("an issue can't block itself") # Run command issue = Issue.load_from_id(blocked_id) s_blocked_id = f'#{blocked_id.shorten(config)}' s_blocking_id = f'#{blocking_id.shorten(config)}' if blocking_id not in issue.blocked_by: print(f'Issue {s_blocked_id} is not blocked by {s_blocking_id}, ' f'no changes were made.') else: issue.blocked_by.remove(blocking_id) issue.save() print(f'Issue #{blocked_id.shorten(config)} no longer marked as ' f'blocked by #{blocking_id.shorten(config)}.')
def cmd_tag(config: Config, args: List[str]) -> None: # Args list_tags = False sort_by_usage = False add_tags: Optional[Set[str]] = None remove_tags: Optional[Set[str]] = None edit_tag: Optional[Tuple[str, str]] = None # Parse args if not args: list_tags = True else: arg = args.pop(0) cli.arg_disallow_positional(arg) if arg == '-lu': list_tags = True sort_by_usage = True elif arg in {'-l', '--list'}: list_tags = True if args and args[0] in {'-u', '--usage'}: args.pop(0) sort_by_usage = True elif arg in {'-a', '--add'}: add_tags = cli.arg_tags(args, '--add') elif arg in {'-r', '--remove'}: remove_tags = cli.arg_tags(args, '--remove') elif arg in {'-e', '--edit'}: edit_tag = (cli.arg_positional(args, 'old tag'), cli.arg_positional(args, 'new tag')) else: cli.arg_unknown_optional(arg) cli.arg_disallow_trailing(args) # Run command tag_registry: Set[str] if not TAGS_PATH.exists(): TAGS_PATH.write_text('[]') tag_registry = set() else: tag_registry = set(json.loads(TAGS_PATH.read_text())) old_tag_registry = frozenset(tag_registry) issues = load_issues() if list_tags: issue_tags = Counter(t for issue in issues for t in issue.tags) issue_tags.update({t: 0 for t in tag_registry if t not in issue_tags}) tag_list = [(name, str(count)) for name, count in sorted(sorted(issue_tags.most_common()), key=itemgetter(1), reverse=True)] if not sort_by_usage: tag_list.sort() unregistered_lines: Dict[int, Tuple[Union[str, Color], Union[str, Color]]] = { n: (RED, RESET) for n, (name, _) in enumerate(tag_list) if name not in tag_registry } if tag_list: print('\n'.join(format_table(tag_list, titles=('Tag name', 'Use count'), surround_rows=unregistered_lines))) unregistered_tags = set(issue_tags.keys()) - tag_registry if unregistered_tags: print(f'\n{RED}{len(unregistered_tags)} ' f'unregistered tags!{RESET}') elif add_tags: existing_tags = add_tags.intersection(tag_registry) new_tags = add_tags - tag_registry if existing_tags: print('Existing tags that weren\'t added:', ', '.join(sorted(existing_tags))) if new_tags: print('Added tags:', ', '.join(sorted(new_tags))) tag_registry.update(add_tags) elif remove_tags: matched_tags = remove_tags.intersection(tag_registry) unknown_tags = remove_tags - tag_registry # TODO: remove/add unregistered tags? if unknown_tags: print('Unknown tags that weren\'t removed:', ', '.join(sorted(unknown_tags))) if matched_tags: print('Tags to remove:', ', '.join(sorted(matched_tags))) for tag in matched_tags: matched_issues = [i for i in issues if tag in i.tags] if matched_issues: response = input(f'Tag {tag!r} is used in ' f'{len(matched_issues)} issues. ' f'Remove it from all of them? [y/N] ') if response.lower() not in {'y', 'yes'}: print('Aborted tag removal, nothing was changed.') break else: tag_registry.difference_update(matched_tags) count = 0 for issue in issues: if matched_tags.intersection(issue.tags): issue.tags.difference_update(matched_tags) issue.save() count += 1 print(f'Tags removed, {count} issues were modified.') elif edit_tag: old_name, new_name = edit_tag if old_name == new_name: error('old name and new name are identical') if old_name not in tag_registry: error(f'unknown tag: {old_name}') if new_name in tag_registry: error(f'new tag already exist: {new_name}') matched_issues = [i for i in issues if old_name in i.tags] if matched_issues: response = input(f'Tag {old_name!r} is used in ' f'{len(matched_issues)} issues. ' f'Rename it to {new_name!r} ' f'in all of them? [y/N] ') if response.lower() not in {'y', 'yes'}: print('Aborted tag edit, nothing was changed.') return else: for issue in matched_issues: issue.tags.remove(old_name) issue.tags.add(new_name) issue.save() tag_registry.remove(old_name) tag_registry.add(new_name) print(f'Tag {old_name!r} renamed to {new_name!r}.') if matched_issues: print(f'{len(matched_issues)} issues were modified.') # Save changes if needed if tag_registry != old_tag_registry: TAGS_PATH.write_text(json.dumps(sorted(tag_registry), indent=2))
def cmd_alias(config: Config, args: List[str]) -> None: # Args list_aliases = False get_alias: Optional[str] = None set_alias: Optional[Tuple[str, str]] = None remove_alias: Optional[str] = None # Parse args if not args: list_aliases = True else: arg = args.pop(0) cli.arg_disallow_positional(arg) if arg in {'-l', '--list'}: list_aliases = True elif arg in {'-g', '--get'}: try: get_alias = args.pop(0) except IndexError: error('--get needs an argument') elif arg in {'-r', '--remove'}: try: remove_alias = args.pop(0) except IndexError: error('--remove needs an argument') elif arg in {'-s', '--set'}: try: set_alias = (args.pop(0), args.pop(0)) except IndexError: error('--set needs two arguments') else: cli.arg_unknown_optional(arg) cli.arg_disallow_trailing(args) # List aliases if list_aliases: if config.aliases: print('Aliases:') for key, value in sorted(config.aliases.items()): print(f' {key} = {value}') else: print('No aliases') # Get aliases elif get_alias: if get_alias not in config.aliases: error(f'unknown alias: {get_alias}') else: print(f'{get_alias} = {config.aliases[get_alias]}') # Remove alias elif remove_alias: if remove_alias not in config.aliases: error(f'unknown alias: {remove_alias}') else: del config.aliases[remove_alias] config.save() print(f'Alias {remove_alias} removed') # Set alias elif set_alias: key, value = set_alias is_new = key not in config.aliases config.aliases[key] = value print(f'{key} -> {value}') config.save() if is_new: print('Alias created') else: print('Alias updated')