Пример #1
0
def issue_close_done():
    """
    Closes any issue that is still open in the done column
    """
    parser = argparse.ArgumentParser(description=issue_close_done.__doc__)

    parser.add_argument('project', help='the project name')
    parser.add_argument('--column',
                        default='done',
                        help='the column to close issues in, default `done`')

    args = parser.parse_args()

    session = GithubSession()

    project_data = session.get_project(args.project)

    column_name = args.column.lower()
    column_data = session.get_column(project_data, column_name)

    for card in session.get_cards(column_data):
        issue_data = session.request('get', card['content_url']).json()

        if issue_data['state'] == 'closed':
            print('.', end='')

            continue

        issue_number = issue_data['number']

        print(f'\nclosing issue {issue_number}')

        session.comment('closing issue in done column', number=issue_number)
        session.update_issue(number=issue_number, state='closed')
Пример #2
0
def issue_close_done():
    """
    Closes any issue that is still open in the done column
    """
    parser = argparse.ArgumentParser(description=issue_close_done.__doc__)

    parser.add_argument("project", help="the project name")
    parser.add_argument(
        "--column", default="done", help="the column to close issues in, default `done`"
    )

    args = parser.parse_args()

    session = GithubSession()

    project_data = session.get_project(args.project)

    column_name = args.column.lower()
    column_data = session.get_column(project_data, column_name)

    for card in session.get_cards(column_data):
        issue_data = session.request("get", card["content_url"]).json()

        if issue_data["state"] == "closed":
            print(".", end="")

            continue

        issue_number = issue_data["number"]

        print(f"\nclosing issue {issue_number}")

        session.comment("closing issue in done column", number=issue_number)
        session.update_issue(number=issue_number, state="closed")
Пример #3
0
def backlog_milestone():
    """
    Moves issue cards within the given miletone from icebox to the backlog column
    """
    parser = argparse.ArgumentParser()
    parser.add_argument("project", help="name of the project")
    parser.add_argument("milestone", help="name of the milestone")

    args = parser.parse_args()

    session = GithubSession()

    project_data = session.get_project(args.project)

    milestone_data = session.get_milestone(args.milestone)
    milestone_title = milestone_data["title"]

    backlog_data = session.get_column(project_data, "backlog")
    icebox_data = session.get_column(project_data, "icebox")

    results = session.search(f'repo:openslate/openslate milestone:"{milestone_title}"')
    for search_data in results["items"]:
        issue_data = get_issue(search_data["number"]).issue
        issue_card = session.get_card(project_data, issue_data)

        if issue_card["column_url"] == icebox_data["url"]:
            session.move_card(issue_card, backlog_data)

        print(".", end="")
Пример #4
0
def backlog_milestone():
    """
    Moves issue cards within the given miletone from icebox to the backlog column
    """
    parser = argparse.ArgumentParser()
    parser.add_argument('project', help=f'name of the project')
    parser.add_argument('milestone', help='name of the milestone')

    args = parser.parse_args()

    session = GithubSession()

    project_data = session.get_project(args.project)

    milestone_data = session.get_milestone(args.milestone)
    milestone_title = milestone_data['title']

    backlog_data = session.get_column(project_data, 'backlog')
    icebox_data = session.get_column(project_data, 'icebox')

    results = session.search(
        f'repo:openslate/openslate milestone:"{milestone_title}"')
    for search_data in results['items']:
        issue_data = get_issue(search_data['number']).issue
        issue_card = session.get_card(project_data, issue_data)

        if issue_card['column_url'] == icebox_data['url']:
            session.move_card(issue_card, backlog_data)

        print('.', end='')
Пример #5
0
def milestone_labels(argv=None):
    """
    creates labels out of milestones
    """
    argv = argv or sys.argv[1:]

    parser = argparse.ArgumentParser()

    parser.add_argument('color', help='color to make the labels')

    args = parser.parse_args(argv)

    session = GithubSession()

    labels = session.get_labels()

    labels_by_name = dict([(label['name'], label) for label in labels])

    for milestone in session.get_milestones():
        label_name = f'epic:{milestone["title"]}'

        if label_name in labels_by_name:
            continue

        labels_by_name[label_name] = session.create_label(label_name, args.color)

    return labels_by_name
Пример #6
0
def issue_icebox():
    """
    Find issues not in any project and add them to the roadmap icebox
    """
    parser = argparse.ArgumentParser()

    parser.add_argument("project", help="the project name")
    parser.add_argument("--icebox-column", default="icebox")

    args = parser.parse_args()

    icebox_column = args.icebox_column

    session = GithubSession()

    results = session.search("repo:openslate/openslate is:issue is:open no:project")

    # print(json.dumps(results, indent=4))

    for issue_data in results["items"]:
        issue = get_issue(issue_data["number"])

        project = issue.get_project(args.project)
        column = issue.get_column(project, icebox_column)

        try:
            issue.create_card(column, issue_data)
        except Exception as exc:
            print(json.dumps(issue_data, indent=4))
            print(f"Error: unable to process issue exc={exc}")
Пример #7
0
def milestones():

    session = GithubSession()

    display_pairs = sorted(
        [(m.get("number"), m.get("title")) for m in session.get_milestones()],
        key=lambda x: x[1],
    )

    for pair in display_pairs:
        print(f"{pair[0]} {pair[1]}")
Пример #8
0
def projects_label(args):
    """
    Labels the cards in the project's column

    A label for the project is added to the card

    optionally, a team label is added if a team is given
    """
    session = GithubSession()

    label_datas = list(session.get_labels())

    team = args.team
    team_label_data = None
    if team:
        team_label = utils.get_label(team, prefix="team")
        team_label_data = [x for x in label_datas if x["name"] == team_label][0]

    # get the project label
    project_label = utils.get_label(args.name, prefix="project")
    project_label_data = [x for x in label_datas if x["name"] == project_label][0]

    print(f"label cards in project {args.name} column {args.column}")

    project_board = session.get_project(args.name)
    project_backlog_grooming = session.get_column(project_board, "backlog grooming")

    cards = list(session.get_cards(project_backlog_grooming))
    for card_data in cards:
        issue_number = utils.get_issue_number_from_card_data(card_data)

        print(issue_number)

        # add the project label
        session.add_label(project_label_data, number=issue_number)

        if team_label_data:
            session.add_label(team_label_data, number=issue_number)
Пример #9
0
def projects_copy_column(args):
    """
    Copies backlog grooming column from one board to another

    For example, the following command will look for a board named
    "TEAM - DE" and will copy all the cards in the "backlog grooming"
    column over to the board named "kanban board":

    projects 'TEAM - DE' backlog 'kanban board'
    """
    session = GithubSession()

    column = args.column

    print(f"copy {column} from {args.name} to {args.kanban_board}")

    core_engineering_board = session.get_project(args.name)
    core_engineering_backlog_grooming = session.get_column(
        core_engineering_board, column
    )

    kanban_board = session.get_project(args.kanban_board)
    kanban_board_backlog_grooming = session.get_column(kanban_board, column)

    cards = list(session.get_cards(core_engineering_backlog_grooming))

    for card_data in cards:
        issue_number = utils.get_issue_number_from_card_data(card_data)

        try:
            print(issue_number)

            issue_data = session.get_issue(issue_number)
            session.create_card(kanban_board_backlog_grooming, issue_data)
        except Exception as exc:
            print(f"unable to move {issue_number}")
Пример #10
0
def issue_closed():
    """
    Finds issues that are closed in all project columns (except `done`) and moves them to `done`
    """
    parser = argparse.ArgumentParser(description=issue_closed.__doc__)

    parser.add_argument("project", help="the project name")
    parser.add_argument(
        "--column",
        default="done",
        help="the column closed issues should go to, default `done`",
    )

    args = parser.parse_args()

    column = args.column.lower()

    session = GithubSession()

    project = session.get_project(args.project)

    for column_data in session.get_columns(project):
        column_name = column_data["name"].lower()
        if column_name == column:
            continue

        print(f"\nlooking at column {column_name}")

        # print(json.dumps(column_data, indent=4))

        for card in session.get_cards(column_data):
            # print(json.dumps(card, indent=4))

            issue_data = session.request("get", card["content_url"]).json()

            if issue_data["state"] != "closed":
                print(".", end="")

                continue

            issue_number = issue_data["number"]

            print(f"\nmoving issue {issue_number} to {column}")

            issue_column(
                [
                    "issue_column",
                    args.project,
                    issue_number,
                    column,
                    "--position=bottom",
                ]
            )
Пример #11
0
def label_milestone_issues():
    """
    Labels all issues in a milestone with that milestone's respective label
    """
    session = GithubSession()

    labels = list(session.get_labels())
    labels_by_name = dict([(x['name'], x) for x in labels])

    milestones = list(session.get_milestones())

    for milestone in milestones:
        label_data = labels_by_name[f'epic:{milestone["title"].strip()}']

        for issue in session.get_issues(milestone=milestone["number"], state='all'):
            session.add_label(label_data, number=issue['number'])
Пример #12
0
def projects_columns(args):
    session = GithubSession()

    project_name = args.name.lower()

    column_name = args.column
    if column_name:
        column_name = column_name.lower()

    project = None
    for _project in session.projects:
        _name = _project["name"].lower()

        if project_name == _name:
            project = _project
            break
    else:
        raise ProjectError("cannot find project {project_name}")

    return args.action(args, session, column_name, project)
Пример #13
0
def issue_closed():
    """
    Finds issues that are closed in all project columns (except `done`) and moves them to `done`
    """
    parser = argparse.ArgumentParser(description=issue_closed.__doc__)

    parser.add_argument('project', help='the project name')
    parser.add_argument(
        '--column',
        default='done',
        help='the column closed issues should go to, default `done`')

    args = parser.parse_args()

    column = args.column.lower()

    session = GithubSession()

    project = session.get_project(args.project)

    for column_data in session.get_columns(project):
        column_name = column_data['name'].lower()
        if column_name == column:
            continue

        print(f'\nlooking at column {column_name}')

        # print(json.dumps(column_data, indent=4))

        for card in session.get_cards(column_data):
            # print(json.dumps(card, indent=4))

            issue_data = session.request('get', card['content_url']).json()

            if issue_data['state'] != 'closed':
                print('.', end='')

                continue

            issue_number = issue_data['number']

            print(f'\nmoving issue {issue_number} to {column}')

            issue_column([
                'issue_column', args.project, issue_number, column,
                '--position=bottom'
            ])
Пример #14
0
    def __init__(self, data):
        self.data = data

        self.session = GithubSession()
Пример #15
0
def issue_create():
    """
    Create an issue in GitHub and place it as a card in a project.
    """
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-a", "--assignees", default=[], nargs="*", help="users to assign to this issue"
    )
    parser.add_argument("-b", "--body", default=None, help="text body of the issue")
    parser.add_argument(
        "-c",
        "--column",
        default=DEFAULT_COLUMN_NAME,
        help="name of column to place card in",
    )
    parser.add_argument(
        "-i",
        "--interactive",
        action="store_true",
        default=DEFAULT_COLUMN_NAME,
        help="Edit issue title and body in vim",
    )
    parser.add_argument(
        "-l", "--labels", default=None, nargs="*", help="labels to add to the new issue"
    )
    parser.add_argument(
        "-m",
        "--milestone",
        default=None,
        help="milestone id to place this issue in. "
        "This should be an integer. "
        "Find milestone ids with the `milestones` command.",
    )
    parser.add_argument(
        "-p", "--project", default=SCRUM_BOARD_NAME, help="project to create issue in"
    )
    parser.add_argument("title", default=None, nargs="?", help="issue title")

    args = parser.parse_args()

    # only required arg for creating an issue. can be overridden in interactive mode
    title = args.title

    # this can be overridden in interactive mode
    body = args.body

    if args.interactive:
        with tempfile.NamedTemporaryFile("w") as fh:
            path = fh.name

            editor = os.environ.get("EDITOR", os.environ.get("VISUAL", "vi"))

            proc = getattr(sh, editor)

            proc(path, _fg=True)

            with open(path, "r") as rfh:

                # grab top line as title
                title = rfh.readline().replace("\n", "")

                # grab remaining lines as body
                body = "".join(rfh.readlines())

    session = GithubSession()

    additional_args = {
        "assignees": args.assignees,
        "body": body,
        "labels": args.labels,
        "milestone": args.milestone,
    }

    issue = session.create_issue(title, **additional_args)

    column_name = args.column
    project_name = args.project

    project = session.get_project(project_name)
    column = session.get_column(project, column_name)

    # finally, create the card
    session.create_card(column, issue)

    print(json.dumps(issue, indent=2))
Пример #16
0
def projects_clone(args):
    session = GithubSession()

    project = None
    new_project = None

    for _project in session.projects:
        _name = _project["name"].lower()

        if _name == args.name.lower():
            project = _project
        elif _name == args.new_name.lower():
            new_project = _project

        if project and new_project:
            break

    if not project:
        raise ProjectError(f"unable to find project {args.name}")

    # print(json.dumps(project, indent=4))
    # print(json.dumps(new_project, indent=4))

    # create the new project if it doesn't exist
    if not new_project:
        print(f"creating {args.new_name}")

        new_project = session.create_project(args.new_name, project["body"])

    # get the new project's columns and index them by name
    new_columns = {}
    for column in session.get_columns(new_project):
        new_columns[column["name"]] = column

    # go through all the columns in the old project and create them in the
    # new one if they don't already exist
    for column_data in session.get_columns(project):
        column_name = column_data["name"]
        new_column_data = new_columns.get(column_name)
        if not new_column_data:
            print(f"creating column {column_name}")

            new_column_data = session.create_column(new_project, column_name)

        # print(new_column_data)

        # when cloning cards is not desired, loop here
        if not args.cards:
            continue

        # get the new column's cards
        new_cards = dict(
            [(x["content_url"], x) for x in session.get_cards(new_column_data)]
        )

        # get the old column's cards
        old_cards = reversed(list(session.get_cards(column_data)))

        print(f"filling {column_name}")

        for old_card_data in old_cards:
            try:
                old_content_url = old_card_data["content_url"]
            except KeyError:
                print(f"skipping {old_card_data}")

                continue

            if old_content_url not in new_cards:
                try:
                    issue_data = session.request("get", old_content_url).json()
                except HTTPError as exc:
                    print(
                        f"Warning: unable to create card {old_content_url} in {column_name}"
                    )

                    continue
                else:
                    new_card = session.create_card(new_column_data, issue_data)

    # close the new project
    session.close_project(new_project)
Пример #17
0
def github_to_youtrack():
    parser = argparse.ArgumentParser()
    parser.add_argument("issue", help="the issue number to import into youtrack")
    parser.add_argument("--subsystem", help="the subsystem to attach the issue to")
    parser.add_argument(
        "--state",
        action="store",
        help="What state to set the issue to, by default it will be the YouTrack default",
    )
    parser.add_argument(
        "--story",
        dest="type",
        action="store_const",
        const="Story",
        default="Task",
        help="Sets type to Story",
    )
    parser.add_argument(
        "--type",
        default="Task",
        help="Sets the type of issue; by default it is set to Task",
    )

    args = parser.parse_args()

    issue_number = args.issue
    issue_type = args.type

    subsystem = args.subsystem
    if not subsystem:
        raise CommandError("subsystem not given")

    session = GithubSession()
    issue = session.get_issue(issue_number)
    body = f'{issue["body"]}\n\n[GitHub issue {issue_number}]({issue["html_url"]})'
    title = f'{issue["title"]} #{issue_number}'

    yt_session = youtrack.Session()

    extra_fields = []

    print(issue)

    points = get_points(issue["labels"])
    if points:
        extra_fields.append(
            {
                "$type": "SimpleIssueCustomField",
                "name": "Story points",
                "value": float(points),
            }
        )

    if args.state:
        extra_fields.append({"name": "State", "value": args.state})

    user_mapping = youtrack.get_user_mapping()

    github_assignee = issue["assignee"]
    if github_assignee:
        github_login = github_assignee["login"]
        yt_assignee = user_mapping.get(github_login)
        if yt_assignee:
            extra_fields.append(
                {
                    "$type": "SingleUserIssueCustomField",
                    "name": "Assignee",
                    "value": {"login": yt_assignee},
                }
            )

    try:
        response = yt_session.create_issue(
            issue_type, subsystem, title, body, extra_fields=extra_fields
        )
    except requests.HTTPError as exc:
        return exc.response.text
Пример #18
0
def projects_count(args):
    """
    Counts cards and points
    """
    session = GithubSession()

    print(f"counting {args.name}")

    board = session.get_project(args.name)

    tally = []

    columns = session.get_columns(board)
    for column in columns:
        print(column["name"], file=sys.stderr)

        cards = list(session.get_cards(column))

        total = Decimal(0)
        unpointed = 0
        num_cards = 0
        num_walk_ins = 0
        issues = []
        walk_ins = []
        walk_in_points = 0

        for card_data in cards:
            issue_number = utils.get_issue_number_from_card_data(card_data)
            if not issue_number:  # must be a note
                continue

            issue_data = session.get_issue(issue_number)
            labels = issue_data["labels"]

            num_cards += 1

            points = get_points(labels)
            if points:
                total += points
            else:
                unpointed += 1

            issue_data = {
                "issue_number": issue_number,
                "points": str(points),
                "unpointed": points is None,
                "walk_in": False,
            }

            if is_walk_in(labels):
                num_walk_ins += 1
                if points:
                    walk_in_points += points

                issue_data["walk_in"] = True

                walk_ins.append(issue_data)

            issues.append(issue_data)

        tally.append(
            {
                "column": column["name"],
                # 'issues': issues,
                "num_cards": num_cards,
                "num_walk_ins": num_walk_ins,
                "walk_in_points": str(walk_in_points),
                # 'walk_ins': walk_ins,
                "total_points": str(total),
                "unpointed": unpointed,
            }
        )

    print(json.dumps(tally, indent=4))