def list_pull_requests(repository=None, creator=None, include_links=False, reviewer=None, source_branch=None, status=None, target_branch=None, project=None, skip=None, top=None, organization=None, detect=None): """List pull requests. :param repository: Name or ID of the repository. :type repository: str :param creator: Limit results to pull requests created by this user. :type creator: str :param include_links: Include _links for each pull request. :type include_links: bool :param reviewer: Limit results to pull requests where this user is a reviewer. :type reviewer: str :param source_branch: Limit results to pull requests that originate from this source branch. :type source_branch: str :param status: Limit results to pull requests with this status. :type status: str :param target_branch: Limit results to pull requests that target this branch. :type target_branch: str :param skip: Number of pull requests to skip. :type skip: int :param top: Maximum number of pull requests to list. :type top: int :rtype: list of :class:`VssJsonCollectionWrapper <v5_0.git.models.VssJsonCollectionWrapper>` """ organization, project, repository = resolve_instance_project_and_repo( detect=detect, organization=organization, project=project, repo=repository) search_criteria = GitPullRequestSearchCriteria( creator_id=resolve_identity_as_id(creator, organization), include_links=include_links, reviewer_id=resolve_identity_as_id(reviewer, organization), source_ref_name=resolve_git_ref_heads(source_branch), status=status, target_ref_name=resolve_git_ref_heads(target_branch)) client = get_git_client(organization) if repository is None: pr_list = client.get_pull_requests_by_project( project=project, search_criteria=search_criteria, skip=skip, top=top) else: pr_list = client.get_pull_requests(project=project, repository_id=repository, search_criteria=search_criteria, skip=skip, top=top) return pr_list
def list_memberships(id, relationship='members', organization=None, detect=None): # pylint: disable=redefined-builtin """List memberships for a group or user. :param id: Group descriptor or User Email whose membership details are required. :type id: str :rtype: [GraphMembership] """ organization = resolve_instance(detect=detect, organization=organization) subject_descriptor = id client = get_graph_client(organization) if '@' in id or '.' not in id: id = resolve_identity_as_id(id, organization) subject_descriptor = get_descriptor_from_storage_key(id, client) direction = 'down' if relationship == 'memberof': direction = 'up' membership_list = client.list_memberships(subject_descriptor=subject_descriptor, direction=direction) lookup_keys = [] for members in membership_list: if relationship == 'memberof': key = GraphSubjectLookupKey(members.container_descriptor) else: key = GraphSubjectLookupKey(members.member_descriptor) lookup_keys.append(key) subject_lookup = GraphSubjectLookup(lookup_keys=lookup_keys) members_details = client.lookup_subjects(subject_lookup=subject_lookup) return members_details
def update_user_entitlement(user, license_type, organization=None, detect=None): """Update license type for a user. :param user: Email ID or ID of the user. :type user: str :param license_type: License type for the user. :type license_type: str :rtype: UserEntitlementsPatchResponse """ patch_document = [] value = {} value['accountLicenseType'] = license_type patch_document.append( _create_patch_operation('replace', '/accessLevel', value)) organization = resolve_instance(detect=detect, organization=organization) if '@' in user: user = resolve_identity_as_id(user, organization) client = get_member_entitlement_management_client(organization) user_entitlement_update = client.update_user_entitlement( document=patch_document, user_id=user) if user_entitlement_update.is_success is False and \ user_entitlement_update.operation_results[0].errors[0] is not None: raise CLIError( user_entitlement_update.operation_results[0].errors[0]['value']) return user_entitlement_update.user_entitlement
def add_membership(member_id, group_id, organization=None, detect=None): """Add membership. :param member_id: Descriptor of the group or Email Id of the user to be added. User should already be a part of the organization. Use `az devops user add` command to add an user to organization. :type member_id: str :param group_id: Descriptor of the group to which member needs to be added. :type group_id: str :rtype: :class:`<GraphMembership> <azure.devops.v5_0.graph.models.GraphMembership>` """ organization = resolve_instance(detect=detect, organization=organization) client = get_graph_client(organization) subject_descriptor = member_id if '@' in member_id or '.' not in member_id: member_id = resolve_identity_as_id(member_id, organization) subject_descriptor = get_descriptor_from_storage_key(member_id, client) membership_details = client.add_membership( subject_descriptor=subject_descriptor, container_descriptor=group_id) lookup_keys = [] container = GraphSubjectLookupKey(membership_details.container_descriptor) subject = GraphSubjectLookupKey(membership_details.member_descriptor) lookup_keys.append(container) lookup_keys.append(subject) subject_lookup = GraphSubjectLookup(lookup_keys=lookup_keys) membership_details = client.lookup_subjects(subject_lookup=subject_lookup) return membership_details
def build_list(definition_ids=None, branch=None, organization=None, project=None, detect=None, top=None, result=None, status=None, reason=None, tags=None, requested_for=None): """List build results. :param definition_ids: IDs (space separated) of definitions to list builds for. :type definition_ids: list of int :param branch: Filter by builds for this branch. :type branch: str :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ :type organization: str :param project: Name or ID of the team project. :type project: str :param detect: Automatically detect organization and project. Default is "on". :type detect: str :param top: Maximum number of builds to list. :type top: int :param result: Limit to builds with this result. :type result: str :param status: Limit to builds with this status. :type status: str :param reason: Limit to builds with this reason. :type reason: str :param tags: Limit to builds with each of the specified tags. Space separated. :type tags: list of str :param requested_for: Limit to builds requested for this user or group. :type requested_for: str :rtype: :class:`<Build> <build.v4_0.models.Build>` """ try: organization, project = resolve_instance_and_project( detect=detect, organization=organization, project=project) client = get_build_client(organization) if definition_ids is not None and definition_ids: definition_ids = list(set(definition_ids)) # make distinct if tags is not None and tags: tags = list(set(tags)) # make distinct builds = client.get_builds(definitions=definition_ids, project=project, branch_name=resolve_git_ref_heads(branch), top=top, result_filter=result, status_filter=status, reason_filter=reason, tag_filters=tags, requested_for=resolve_identity_as_id( requested_for, organization)) return builds except VstsServiceError as ex: raise CLIError(ex)
def _resolve_reviewers_as_ids(reviewers, organization): """Takes a list containing identity names, emails, and ids, and returns a list of IdentityRefWithVote objects. """ resolved_reviewers = None if reviewers is not None and reviewers: resolved_reviewers = [] for reviewer in reviewers: resolved_reviewers.append(resolve_identity_as_id(reviewer, organization)) return resolved_reviewers
def delete_user_entitlement(user, organization=None, detect=None): """Remove user from an organization. :param user: Email ID or ID of the user. :type user: str """ organization = resolve_instance(detect=detect, organization=organization) if '@' in user: user = resolve_identity_as_id(user, organization) client = get_member_entitlement_management_client(organization) delete_user_entitlement_details = client.delete_user_entitlement(user_id=user) return delete_user_entitlement_details
def _resolve_reviewers_as_refs(reviewers, organization): """Takes a list containing identity names, emails, and ids, and return a list of IdentityRefWithVote objects. :rtype: list of :class:`IdentityRefWithVote <v5_0.git.models.IdentityRefWithVote>` """ resolved_reviewers = None if reviewers is not None and reviewers: resolved_reviewers = [] for reviewer in reviewers: resolved_reviewers.append(IdentityRefWithVote(id=resolve_identity_as_id(reviewer, organization))) return resolved_reviewers
def get_user_entitlement(user, organization=None, detect=None): """Show user details. :param user: Email ID or ID of the user. :type user: str :rtype: UserEntitlement """ organization = resolve_instance(detect=detect, organization=organization) if '@' in user: user = resolve_identity_as_id(user, organization) client = get_member_entitlement_management_client(organization) user_entitlement_details = client.get_user_entitlement(user_id=user) return user_entitlement_details
def pipeline_run_list(pipeline_ids=None, branch=None, organization=None, project=None, detect=None, top=None, query_order=None, result=None, status=None, reason=None, tags=None, requested_for=None): """ List the pipeline runs in a project. :param pipeline_ids: IDs (space separated) of definitions to list builds for. For multiple pipeline ids: --pipeline-ids 1 2 :type pipeline_ids: list of int :param branch: Filter by builds for this branch. :type branch: str :param top: Maximum number of builds to list. :type top: int :param query_order: Order of pipeline runs. :type query_order: str :param result: Limit to builds with this result. :type result: str :param status: Limit to builds with this status. :type status: str :param reason: Limit to builds with this reason. :type reason: str :param tags: Limit to builds with each of the specified tags. Space separated. :type tags: list of str :param requested_for: Limit to builds requested for this user or group. :type requested_for: str """ organization, project = resolve_instance_and_project( detect=detect, organization=organization, project=project) client = get_build_client(organization) if pipeline_ids is not None and pipeline_ids: pipeline_ids = list(set(pipeline_ids)) # make distinct if tags is not None and tags: tags = list(set(tags)) # make distinct query_order = _resolve_runs_query_order(query_order) builds = client.get_builds(definitions=pipeline_ids, project=project, branch_name=resolve_git_ref_heads(branch), top=top, result_filter=result, status_filter=status, reason_filter=reason, tag_filters=tags, query_order=query_order, requested_for=resolve_identity_as_id( requested_for, organization)) return builds
def resolveIdentityMailsToIds(mailList, organization): logger.debug('mail list %s ', mailList) if not mailList or (not mailList.strip()): return None idList = [] for mail in mailList.split(';'): mailStripped = mail.strip() logger.debug('trying to resolve %s', mailStripped) identityId = resolve_identity_as_id(mailStripped, organization) logger.debug('got id as %s', identityId) idList.append(identityId) return idList
def build_list(definition_ids=None, branch=None, organization=None, project=None, detect=None, top=None, result=None, status=None, reason=None, tags=None, requested_for=None): """List build results. :param definition_ids: IDs (space separated) of definitions to list builds for. :type definition_ids: list of int :param branch: Filter by builds for this branch. :type branch: str :param top: Maximum number of builds to list. :type top: int :param result: Limit to builds with this result. :type result: str :param status: Limit to builds with this status. :type status: str :param reason: Limit to builds with this reason. :type reason: str :param tags: Limit to builds with each of the specified tags. Space separated. :type tags: list of str :param requested_for: Limit to builds requested for this user or group. :type requested_for: str :rtype: :class:`<Build> <build.v4_0.models.Build>` """ organization, project = resolve_instance_and_project( detect=detect, organization=organization, project=project) client = get_build_client(organization) if definition_ids is not None and definition_ids: definition_ids = list(set(definition_ids)) # make distinct if tags is not None and tags: tags = list(set(tags)) # make distinct builds = client.get_builds(definitions=definition_ids, project=project, branch_name=resolve_git_ref_heads(branch), top=top, result_filter=result, status_filter=status, reason_filter=reason, tag_filters=tags, requested_for=resolve_identity_as_id( requested_for, organization)) return builds
def vote_pull_request(id, vote, organization=None, detect=None): # pylint: disable=redefined-builtin """Vote on a pull request. :param id: ID of the pull request. :type id: int :param vote: New vote value for the pull request. :type vote: int :rtype: :class:`IdentityRefWithVote <v5_0.git.models.IdentityRefWithVote>` """ organization = resolve_instance(detect=detect, organization=organization) client = get_git_client(organization) pr = client.get_pull_request_by_id(id) resolved_reviewer = IdentityRefWithVote(id=resolve_identity_as_id(ME, organization)) resolved_reviewer.vote = _convert_vote_to_int(vote) created_reviewer = client.create_pull_request_reviewer(project=pr.repository.project.id, repository_id=pr.repository.id, pull_request_id=id, reviewer_id=resolved_reviewer.id, reviewer=resolved_reviewer) return created_reviewer
def remove_membership(member_id, group_id, organization=None, detect=None): """Remove membership. :param member_id: Descriptor of the group or Email Id of the user to be removed. :type member_id: str :param group_id: Descriptor of the group from which member needs to be removed. :type group_id: str """ organization = resolve_instance(detect=detect, organization=organization) client = get_graph_client(organization) subject_descriptor = member_id if '@' in member_id or '.' not in member_id: member_id = resolve_identity_as_id(member_id, organization) subject_descriptor = get_descriptor_from_storage_key(member_id, client) try: client.check_membership_existence(subject_descriptor=subject_descriptor, container_descriptor=group_id) membership_details = client.remove_membership(subject_descriptor=subject_descriptor, container_descriptor=group_id) except AzureDevOpsClientRequestError as ex: logger.debug(ex, exc_info=True) raise CLIError("Membership doesn't exists.") return membership_details
def update_pull_request( id, title=None, description=None, auto_complete=None, # pylint: disable=redefined-builtin squash=None, delete_source_branch=None, bypass_policy=None, draft=None, bypass_policy_reason=None, merge_commit_message=None, organization=None, detect=None, transition_work_items=None): """Update a pull request. :param id: ID of the pull request. :type id: int :param title: New title for the pull request. :type title: str :param description: New description for the pull request. Can include markdown. Each value sent to this arg will be a new line. For example: --description "First Line" "Second Line" :type description: list of str :param auto_complete: Set the pull request to complete automatically when all policies have passed and the source branch can be merged into the target branch. :type auto_complete: bool :param squash: Squash the commits in the source branch when merging into the target branch. :type squash: bool :param delete_source_branch: Delete the source branch after the pull request has been completed and merged into the target branch. :type delete_source_branch: bool :param bypass_policy: Bypass required policies (if any) and completes the pull request once it can be merged. :type bypass_policy: bool :param bypass_policy_reason: Reason for bypassing the required policies. :type bypass_policy_reason: str :param draft: Publish the PR or convert to draft mode. :type draft: bool :param merge_commit_message: Message displayed when commits are merged. :type merge_commit_message: str :param transition_work_items: Transition any work items linked to the pull request into the next logical state. (e.g. Active -> Resolved) :type transition_work_items: bool :rtype: :class:`GitPullRequest <v5_0.git.models.GitPullRequest>` """ organization = resolve_instance(detect=detect, organization=organization) client = get_git_client(organization) existing_pr = client.get_pull_request_by_id(id) if description is not None: multi_line_description = '\n'.join(description) else: multi_line_description = None pr = GitPullRequest(title=title, description=multi_line_description) if (bypass_policy is not None or # pylint: disable=too-many-boolean-expressions bypass_policy_reason is not None or squash is not None or merge_commit_message is not None or delete_source_branch is not None or transition_work_items is not None): completion_options = existing_pr.completion_options if completion_options is None: completion_options = GitPullRequestCompletionOptions() if bypass_policy is not None: completion_options.bypass_policy = bypass_policy if bypass_policy_reason is not None: completion_options.bypass_reason = bypass_policy_reason if delete_source_branch is not None: completion_options.delete_source_branch = delete_source_branch if squash is not None: completion_options.squash_merge = squash if merge_commit_message is not None: completion_options.merge_commit_message = merge_commit_message if transition_work_items is not None: completion_options.transition_work_items = transition_work_items pr.completion_options = completion_options if auto_complete is not None: if auto_complete: pr.auto_complete_set_by = IdentityRef( id=resolve_identity_as_id(ME, organization)) else: pr.auto_complete_set_by = IdentityRef(id=EMPTY_UUID) if draft is not None: pr.is_draft = draft pr = client.update_pull_request( git_pull_request_to_update=pr, project=existing_pr.repository.project.name, repository_id=existing_pr.repository.name, pull_request_id=id) return pr
def create_pull_request(project=None, repository=None, source_branch=None, target_branch=None, title=None, description=None, auto_complete=False, squash=False, delete_source_branch=False, bypass_policy=False, bypass_policy_reason=None, merge_commit_message=None, reviewers=None, work_items=None, draft=None, open=False, organization=None, detect=None, transition_work_items=False): # pylint: disable=redefined-builtin """Create a pull request. :param project: Name or ID of the team project. :type project: str :param repository: Name or ID of the repository to create the pull request in. :type repository: str :param source_branch: Name of the source branch. Example: "dev". :type source_branch: str :param target_branch: Name of the target branch. If not specified, defaults to the default branch of the target repository. :type target_branch: str :param title: Title for the new pull request. :type title: str :param draft: Use this flag to create the pull request in draft/work in progress mode. :type draft: bool :param description: Description for the new pull request. Can include markdown. Each value sent to this arg will be a new line. For example: --description "First Line" "Second Line" :type description: list of str :param auto_complete: Set the pull request to complete automatically when all policies have passed and the source branch can be merged into the target branch. :type auto_complete: bool :param squash: Squash the commits in the source branch when merging into the target branch. :type squash: bool :param delete_source_branch: Delete the source branch after the pull request has been completed and merged into the target branch. :type delete_source_branch: bool :param bypass_policy: Bypass required policies (if any) and completes the pull request once it can be merged. :type bypass_policy: bool :param bypass_policy_reason: Reason for bypassing the required policies. :type bypass_policy_reason: str :param merge_commit_message: Message displayed when commits are merged. :type merge_commit_message: str :param reviewers: Additional users or groups to include as reviewers on the new pull request. Space separated. :type reviewers: list of str :param work_items: IDs of the work items to link to the new pull request. Space separated. :type work_items: list of str :param open: Open the pull request in your web browser. :type open: bool :param transition_work_items: Transition any work items linked to the pull request into the next logical state. (e.g. Active -> Resolved) :type transition_work_items: bool :rtype: :class:`GitPullRequest <v5_0.git.models.GitPullRequest>` """ organization, project, repository = resolve_instance_project_and_repo( detect=detect, organization=organization, project=project, repo=repository) source_branch, target_branch = _get_branches_for_pull_request( organization, project, repository, source_branch, target_branch, detect) client = get_git_client(organization) multi_line_description = None if description is not None: multi_line_description = '\n'.join(description) pr = GitPullRequest(description=multi_line_description, source_ref_name=source_branch, target_ref_name=target_branch) if draft is not None: pr.is_draft = draft if title is not None: pr.title = title else: pr.title = 'Merge ' + source_branch + ' to ' + target_branch pr.source_ref_name = resolve_git_ref_heads(source_branch) pr.target_ref_name = resolve_git_ref_heads(target_branch) if pr.source_ref_name == pr.target_ref_name: raise CLIError( 'The source branch, "{}", can not be the same as the target branch.' .format(pr.source_ref_name)) pr.reviewers = _resolve_reviewers_as_refs(reviewers, organization) if work_items is not None and work_items: resolved_work_items = [] for work_item in work_items: resolved_work_items.append(ResourceRef(id=work_item)) pr.work_item_refs = resolved_work_items pr = client.create_pull_request(git_pull_request_to_create=pr, project=project, repository_id=repository) title_from_commit = None if title is None: # if title wasn't specified and only one commit, we will set the PR title to the comment of that commit commits = client.get_pull_request_commits( repository_id=repository, pull_request_id=pr.pull_request_id, project=project) if len(commits) == 1: title_from_commit = commits[0].comment set_completion_options = (bypass_policy or bypass_policy_reason is not None or squash or merge_commit_message is not None or delete_source_branch or transition_work_items) if auto_complete or set_completion_options or title_from_commit is not None: pr_for_update = GitPullRequest() if auto_complete: # auto-complete will not get set on create, so a subsequent update is required. pr_for_update.auto_complete_set_by = IdentityRef( id=resolve_identity_as_id(ME, organization)) if set_completion_options: completion_options = GitPullRequestCompletionOptions() completion_options.bypass_policy = bypass_policy completion_options.bypass_reason = bypass_policy_reason completion_options.delete_source_branch = delete_source_branch completion_options.squash_merge = squash completion_options.merge_commit_message = merge_commit_message completion_options.transition_work_items = transition_work_items pr_for_update.completion_options = completion_options if title_from_commit is not None: pr_for_update.title = title_from_commit pr = client.update_pull_request( git_pull_request_to_update=pr_for_update, project=pr.repository.project.id, repository_id=pr.repository.id, pull_request_id=pr.pull_request_id) if open: _open_pull_request(pr, organization) return pr