Пример #1
0
    def create_new(self) -> None:
        task_type = self.render(self.config.type)

        document = []
        if "System.Tags" not in self.config.ado_fields:
            document.append(
                JsonPatchOperation(op="Add",
                                   path="/fields/System.Tags",
                                   value="Onefuzz"))

        for field in self.config.ado_fields:
            value = self.render(self.config.ado_fields[field])
            if field == "System.Tags":
                value += ";Onefuzz"
            document.append(
                JsonPatchOperation(op="Add",
                                   path="/fields/%s" % field,
                                   value=value))

        entry = self.client.create_work_item(document=document,
                                             project=self.project,
                                             type=task_type)

        if self.config.comment:
            comment = self.render(self.config.comment)
            self.client.add_comment(
                CommentCreate(comment),
                self.project,
                entry.id,
            )
Пример #2
0
    def render_new(self) -> Tuple[str, List[JsonPatchOperation]]:
        task_type = self.render(self.config.type)
        document = []
        if "System.Tags" not in self.config.ado_fields:
            document.append(
                JsonPatchOperation(op="Add",
                                   path="/fields/System.Tags",
                                   value="Onefuzz"))

        for field in self.config.ado_fields:
            value = self.render(self.config.ado_fields[field])
            if field == "System.Tags":
                value += ";Onefuzz"
            document.append(
                JsonPatchOperation(op="Add",
                                   path="/fields/%s" % field,
                                   value=value))
        return (task_type, document)
Пример #3
0
    def update_existing(self, item: WorkItem) -> None:
        if self.config.on_duplicate.comment:
            comment = self.render(self.config.on_duplicate.comment)
            self.client.add_comment(
                CommentCreate(comment),
                self.project,
                item.id,
            )

        document = []
        for field in self.config.on_duplicate.increment:
            value = int(item.fields[field]) if field in item.fields else 0
            value += 1
            document.append(
                JsonPatchOperation(
                    op="Replace", path="/fields/%s" % field, value=str(value)
                )
            )

        for field in self.config.on_duplicate.ado_fields:
            field_value = self.render(self.config.on_duplicate.ado_fields[field])
            document.append(
                JsonPatchOperation(
                    op="Replace", path="/fields/%s" % field, value=field_value
                )
            )

        if item.fields["System.State"] in self.config.on_duplicate.set_state:
            document.append(
                JsonPatchOperation(
                    op="Replace",
                    path="/fields/System.State",
                    value=self.config.on_duplicate.set_state[
                        item.fields["System.State"]
                    ],
                )
            )

        if document:
            self.client.update_work_item(document, item.id, project=self.project)
Пример #4
0
def set_field(document, field, value):
    document.append(
        JsonPatchOperation(from_=None, op="add", path=field, value=value))
Пример #5
0
def main():

    # extract arguments
    parser = argparse.ArgumentParser(
        description='push completed status from parent work items to children')
    parser.add_argument("-o",
                        "--org",
                        required=True,
                        dest="url",
                        help="Azure DevOps Organization URL")
    parser.add_argument("-p",
                        "--project",
                        required=True,
                        dest="project",
                        help="Azure DevOps Project")
    parser.add_argument("-t",
                        "--pat",
                        required=True,
                        dest="pat",
                        help="Azure DevOps Personal Access Token")
    parser.add_argument(
        "--parent-type",
        required=True,
        dest="parent_type",
        help="work item parent type to filter for (Bug,Feature,...)")
    parser.add_argument(
        "--child-type",
        required=True,
        dest="child_type",
        help=
        "work item child type to filter for (Task,Product Backlog Item,...)")
    parser.add_argument(
        "--age",
        required=False,
        dest="age",
        default=120,
        type=int,
        help="age in days when last change of work item happened")
    parser.add_argument("--update",
                        required=False,
                        action='store_true',
                        dest="update",
                        help="commit update to Azure DevOps")
    args = parser.parse_args()

    # create a connection to the org
    credentials = BasicAuthentication('', args.pat)
    connection = Connection(base_url=args.url, creds=credentials)

    # get a client
    wit_client = connection.clients.get_work_item_tracking_client()

    # determine potential Completed+Removed states for the parent/child work item type
    wi_types = wit_client.get_work_item_types(args.project)
    parent_completed_states = [
        s.name
        for s in [t for t in wi_types if t.name == args.parent_type][0].states
        if s.category == 'Completed'
    ]
    parent_removed_states = [
        s.name
        for s in [t for t in wi_types if t.name == args.parent_type][0].states
        if s.category == 'Removed'
    ]
    child_completed_states = [
        s.name
        for s in [t for t in wi_types if t.name == args.child_type][0].states
        if s.category == 'Completed'
    ]
    child_removed_states = [
        s.name
        for s in [t for t in wi_types if t.name == args.child_type][0].states
        if s.category == 'Removed'
    ]

    # query relations
    wiql = Wiql(query=f"""SELECT *
            FROM workitemLinks
            WHERE [Source].[System.TeamProject] = '{args.project}'
            AND [Source].[System.WorkItemType] = '{args.parent_type}'
            AND [Target].[System.WorkItemType] = '{args.child_type}'
            AND [Source].[System.ChangedDate] >= @today - {args.age}
            AND [System.Links.LinkType] = 'Child'
            MODE (MustContain)
        """)

    wi_relations = wit_client.query_by_wiql(wiql, top=1000).work_item_relations
    print(f'Results: {len(wi_relations)}')

    # process relations
    if wi_relations:

        for wir in wi_relations:
            if wir.source and wir.target:

                # for each source (parent) / target (child) pair check completed state
                wis = wit_client.get_work_item(wir.source.id)
                wit = wit_client.get_work_item(wir.target.id)

                if wis.fields[
                        'System.State'] in parent_completed_states or wis.fields[
                            'System.State'] in parent_removed_states:
                    print(
                        f"{wis.fields['System.WorkItemType']} {wir.source.id} ({wis.fields['System.State']}) -> {wit.fields['System.WorkItemType']} {wir.target.id} ({wit.fields['System.State']})"
                    )

                    operations = []

                    if wis.fields[
                            'System.State'] in parent_completed_states and not wit.fields[
                                'System.State'] in child_completed_states and not wit.fields[
                                    'System.State'] in child_removed_states:
                        print(f" =>{child_completed_states[0]}")
                        operations.append(
                            JsonPatchOperation(
                                op='replace',
                                path=f'/fields/System.State',
                                value=child_completed_states[0]))

                    if wis.fields[
                            'System.State'] in parent_removed_states and not wit.fields[
                                'System.State'] in child_completed_states and not wit.fields[
                                    'System.State'] in child_removed_states:
                        print(f" =>{child_removed_states[0]}")
                        operations.append(
                            JsonPatchOperation(op='replace',
                                               path=f'/fields/System.State',
                                               value=child_removed_states[0]))

                    if len(operations) > 0 and args.update:
                        resp = wit_client.update_work_item(document=operations,
                                                           id=wir.target.id)
                        print(resp)
Пример #6
0
def main():

    # extract arguments
    parser = argparse.ArgumentParser(
        description='push characteristics from parent work items to children')
    parser.add_argument("-o",
                        "--org",
                        required=True,
                        dest="url",
                        help="Azure DevOps Organization URL")
    parser.add_argument("-p",
                        "--project",
                        required=True,
                        dest="project",
                        help="Azure DevOps Project")
    parser.add_argument("-t",
                        "--pat",
                        required=True,
                        dest="pat",
                        help="Azure DevOps Personal Access Token")
    parser.add_argument(
        "--parent-type",
        required=True,
        dest="parent_type",
        help="work item parent type to filter for (Bug,Feature,...)")
    parser.add_argument(
        "--child-type",
        required=True,
        dest="child_type",
        help=
        "work item child type to filter for (Task,Product Backlog Item,...)")
    parser.add_argument(
        "--field-list",
        required=True,
        dest="field_list",
        help=
        "comma separated list of field names to compare e.g. System.AreaPath,System.IterationPath"
    )
    parser.add_argument(
        "--age",
        required=False,
        dest="age",
        default=120,
        type=int,
        help="age in days when last change of work item happened")
    parser.add_argument("--update",
                        required=False,
                        action='store_true',
                        dest="update",
                        help="commit update to Azure DevOps")
    args = parser.parse_args()

    # create a connection to the org
    credentials = BasicAuthentication('', args.pat)
    connection = Connection(base_url=args.url, creds=credentials)

    # get a client
    wit_client = connection.clients.get_work_item_tracking_client()

    # query relations
    wiql = Wiql(query=f"""SELECT *
            FROM workitemLinks
            WHERE [Source].[System.TeamProject] = '{args.project}'
            AND [Source].[System.WorkItemType] = '{args.parent_type}'
            AND [Target].[System.WorkItemType] = '{args.child_type}'
            AND [Source].[System.ChangedDate] >= @today - {args.age}
            AND [System.Links.LinkType] = 'Child'
            MODE (MustContain)
        """)

    wi_relations = wit_client.query_by_wiql(wiql, top=1000).work_item_relations
    print(f'Results: {len(wi_relations)}')

    # process relations
    if wi_relations:

        fields_to_check = args.field_list.split(',')

        for wir in wi_relations:
            if wir.source and wir.target:

                # for each source (parent) / target (child) pair check field list
                wis = wit_client.get_work_item(wir.source.id)
                wit = wit_client.get_work_item(wir.target.id)

                print(
                    f"{wis.fields['System.WorkItemType']} {wir.source.id} -> {wit.fields['System.WorkItemType']} {wir.target.id}"
                )

                operations = []

                for field in fields_to_check:
                    if not field in wis.fields or not field in wit.fields:
                        raise ValueError(f"field {field} unknown")
                    elif wis.fields[field] != wit.fields[field]:
                        print(f' =>{field}')
                        operations.append(
                            JsonPatchOperation(op='replace',
                                               path=f'/fields/{field}',
                                               value=wis.fields[field]))

                if len(operations) > 0 and args.update:
                    resp = wit_client.update_work_item(document=operations,
                                                       id=wir.target.id)
                    print(resp)