Beispiel #1
0
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}')
Beispiel #2
0
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}.')
Beispiel #3
0
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))
Beispiel #4
0
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')
Beispiel #5
0
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)
Beispiel #6
0
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')
Beispiel #7
0
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)}.')
Beispiel #8
0
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))
Beispiel #9
0
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')