From 0a619bf456d9696b1c849eedf81d431b9753e907 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 14:58:03 +0530 Subject: [PATCH 01/48] policy cut #1 --- azure-devops/azext_devops/dev/common/const.py | 5 + .../azext_devops/dev/repos/_format.py | 23 ++++ .../azext_devops/dev/repos/commands.py | 22 ++-- azure-devops/azext_devops/dev/repos/policy.py | 118 ++++++++++++++++++ 4 files changed, 158 insertions(+), 10 deletions(-) create mode 100644 azure-devops/azext_devops/dev/repos/policy.py diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index 430698a0..816ffcfa 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -24,3 +24,8 @@ DEVOPS_TEAM_PROJECT_DEFAULT = 'project' PAT_ENV_VARIABLE_NAME = CLI_ENV_VARIABLE_PREFIX + 'PAT' AUTH_TOKEN_ENV_VARIABLE_NAME = CLI_ENV_VARIABLE_PREFIX + 'AUTH_TOKEN' + +# policy constants + +APPROVER_COUNT_POLICY = 'ApproverCountPolicy' +APPROVER_COUNT_POLICY_ID = 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd' diff --git a/azure-devops/azext_devops/dev/repos/_format.py b/azure-devops/azext_devops/dev/repos/_format.py index 7c363d17..b4576c0d 100644 --- a/azure-devops/azext_devops/dev/repos/_format.py +++ b/azure-devops/azext_devops/dev/repos/_format.py @@ -12,6 +12,29 @@ _WORK_ITEM_TITLE_TRUNCATION_LENGTH = 70 +def transform_repo_policies_table_output(result): + table_output = [] + for item in result: + table_output.append(_transform_repo_policy_request_row(item)) + return table_output + + +def transform_repo_policy_table_output(result): + table_output = [_transform_repo_policy_request_row(result)] + return table_output + + +def _transform_repo_policy_request_row(row): + table_row = OrderedDict() + table_row['ID'] = row['id'] + table_row['Is Blocking'] = row['isBlocking'] + table_row['Is Enabled'] = row['isEnabled'] + #this will break if policy is applied across repo but that is not possible via UI at least now + table_row['Repository Id'] = row['settings']['scope'][0]['repositoryId'] + table_row['Branch'] = row['settings']['scope'][0]['refName'] + table_row['Policy Type'] = row['type']['displayName'] + return table_row + def transform_pull_requests_table_output(result): table_output = [] for item in result: diff --git a/azure-devops/azext_devops/dev/repos/commands.py b/azure-devops/azext_devops/dev/repos/commands.py index e344a3f9..c691aca7 100644 --- a/azure-devops/azext_devops/dev/repos/commands.py +++ b/azure-devops/azext_devops/dev/repos/commands.py @@ -4,16 +4,7 @@ # -------------------------------------------------------------------------------------------- from azure.cli.core.commands import CliCommandType -from ._format import (transform_pull_request_table_output, - transform_pull_requests_table_output, - transform_repo_table_output, - transform_repos_table_output, - transform_reviewers_table_output, - transform_reviewer_table_output, - transform_policies_table_output, - transform_policy_table_output, - transform_work_items_table_output, - transform_repo_import_table_output) +from ._format import * reposPullRequestOps = CliCommandType( @@ -28,6 +19,10 @@ operations_tmpl='azext_devops.dev.repos.import_request#{}' ) +policyOps = CliCommandType( + operations_tmpl='azext_devops.dev.repos.policy#{}' +) + def load_code_commands(self, _): with self.command_group('repos', command_type=reposRepositoryOps) as g: @@ -37,6 +32,13 @@ def load_code_commands(self, _): g.command('list', 'list_repos', table_transformer=transform_repos_table_output) g.command('show', 'show_repo', table_transformer=transform_repo_table_output) + with self.command_group('repos policies', command_type=policyOps) as g: + # repository/ branch policies + g.command('create', 'create_policy', table_transformer=transform_repo_policy_table_output) + g.command('list', 'list_policy', table_transformer=transform_repo_policies_table_output) + g.command('show', 'get_policy', table_transformer=transform_repo_policy_table_output) + g.command('delete', 'delete_policy', confirmation='Are you sure you want to delete this policy?') + with self.command_group('repos pr', command_type=reposPullRequestOps) as g: # basic pr commands g.command('create', 'create_pull_request', table_transformer=transform_pull_request_table_output) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py new file mode 100644 index 00000000..05a1b968 --- /dev/null +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -0,0 +1,118 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from knack.util import CLIError +from vsts.exceptions import VstsClientRequestError, VstsServiceError +from vsts.policy.v4_0.models.policy_configuration import PolicyConfiguration + +from azext_devops.dev.common.services import (get_policy_client, resolve_instance_and_project) +from azext_devops.dev.common.const import * + + +def list_policy(organization=None, project=None, detect=None): + """ + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ + :type organization: str + :param project: Name or ID of the project. + :type project: str + :param detect: Automatically detect organization. Default is "on". + :type detect: str + :rtype: [PolicyConfiguration] + """ + try: + organization, project = resolve_instance_and_project( + detect=detect, organization=organization, project=project) + policy_client = get_policy_client(organization) + return policy_client.get_policy_configurations(project=project) + except VstsServiceError as ex: + raise CLIError(ex) + +def get_policy(id, organization=None, project=None, detect=None): + """ + :param id: ID of the policy. + :type id: int + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ + :type organization: str + :param project: Name or ID of the project. + :type project: str + :param detect: Automatically detect organization. Default is "on". + :type detect: str + :rtype: :class:` ` + """ + try: + organization, project = resolve_instance_and_project( + detect=detect, organization=organization, project=project) + policy_client = get_policy_client(organization) + return policy_client.get_policy_configuration(project=project, configuration_id=id) + except VstsServiceError as ex: + raise CLIError(ex) + +def delete_policy(id, organization=None, project=None, detect=None): + """ + :param id: ID of the policy. + :type id: int + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ + :type organization: str + :param project: Name or ID of the project. + :type project: str + :param detect: Automatically detect organization. Default is "on". + :type detect: str + """ + try: + organization, project = resolve_instance_and_project( + detect=detect, organization=organization, project=project) + policy_client = get_policy_client(organization) + return policy_client.delete_policy_configuration(project=project, configuration_id=id) + except VstsServiceError as ex: + raise CLIError(ex) + +def create_policy(repository_id, branch, + isBlocking=True, isEnabled=False, + policy_type=None, + minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, + organization=None, project=None, detect=None): + """ + :param policy_type: Type of policy you want to create + :type policy_type: string + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ + :type organization: str + :param project: Name or ID of the project. + :type project: str + :param detect: Automatically detect organization. Default is "on". + :type detect: str + :rtype: :class:` ` + """ + try: + organization, project = resolve_instance_and_project( + detect=detect, organization=organization, project=project) + policy_client = get_policy_client(organization) + + if(policy_type == APPROVER_COUNT_POLICY): + if any(v is None for v in [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]): + raise CLIError('--minimumApproverCount, --creatorVoteCounts, --allowDownvotes, --resetOnSourcePush are required for ApproverCountPolicy') + + policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) + + if(policy_type == APPROVER_COUNT_POLICY): + policyConfigurationToCreate.type = { + 'id' : APPROVER_COUNT_POLICY_ID + } + + policyConfigurationToCreate.settings = { + 'minimumApproverCount' : minimumApproverCount, + 'creatorVoteCounts' : creatorVoteCounts, + 'allowDownvotes' : allowDownvotes, + 'resetOnSourcePush' : resetOnSourcePush, + 'scope': [ + { + 'repositoryId': repository_id, + 'refName': branch, + 'matchKind': 'exact' + }]} + + return policy_client.create_policy_configuration(configuration=policyConfigurationToCreate, project=project) + except VstsServiceError as ex: + raise CLIError(ex) + From f20d9650b8fc7d54765e2e7cd72e2a18052f33ad Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 15:21:57 +0530 Subject: [PATCH 02/48] check #2 --- azure-devops/azext_devops/dev/common/const.py | 2 ++ .../azext_devops/dev/repos/arguments.py | 4 +++ azure-devops/azext_devops/dev/repos/policy.py | 33 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index 816ffcfa..3eef93ae 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -29,3 +29,5 @@ APPROVER_COUNT_POLICY = 'ApproverCountPolicy' APPROVER_COUNT_POLICY_ID = 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd' + +REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY] diff --git a/azure-devops/azext_devops/dev/repos/arguments.py b/azure-devops/azext_devops/dev/repos/arguments.py index 5adfebc5..0a4bb4d2 100644 --- a/azure-devops/azext_devops/dev/repos/arguments.py +++ b/azure-devops/azext_devops/dev/repos/arguments.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- from knack.arguments import enum_choice_list +from azext_devops.dev.common.const import REPO_POLICY_TYPE # CUSTOM CHOICE LISTS _ON_OFF_SWITCH_VALUES = ['on', 'off'] @@ -18,6 +19,9 @@ def load_code_arguments(self, _): context.argument('reviewers', nargs='*') context.argument('detect', **enum_choice_list(_ON_OFF_SWITCH_VALUES)) + with self.argument_context('repos policies create') as context: + context.argument('policy_type', **enum_choice_list(REPO_POLICY_TYPE)) + with self.argument_context('repos pr') as context: context.argument('description', type=str, options_list=('--description', '-d'), nargs='*') context.argument('repository', options_list=('--repository', '-r')) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 05a1b968..4f3571cd 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -74,8 +74,26 @@ def create_policy(repository_id, branch, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, organization=None, project=None, detect=None): """ + :param repository_id: Id (UUID) of the repository on which to apply the policy + :type repository_id: string + :param branch: Branch on which this policy should be applied + :type branch: string + :param isBlocking: Tells if the policy should be blocking or not + :type isBlocking: bool + :param isEnabled: Tells if the policy enabled or not + :type isEnabled: bool :param policy_type: Type of policy you want to create :type policy_type: string + + :param minimumApproverCount: Minimum approver count. Required if policy type is ApproverCountPolicy. + :type minimumApproverCount: int + :param creatorVoteCounts: Creator vote counts or not. Required if policy type is ApproverCountPolicy + :type creatorVoteCounts: bool + :param allowDownvotes: Allow Downvotes. Required if policy type is ApproverCountPolicy. + :type allowDownvotes: bool + :param resetOnSourcePush: Reset on Source Push. Required if policy type is ApproverCountPolicy. + :type resetOnSourcePush: bool + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ :type organization: str :param project: Name or ID of the project. @@ -94,6 +112,13 @@ def create_policy(repository_id, branch, raise CLIError('--minimumApproverCount, --creatorVoteCounts, --allowDownvotes, --resetOnSourcePush are required for ApproverCountPolicy') policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) + scope = [ + { + 'repositoryId': repository_id, + 'refName': branch, + 'matchKind': 'exact' + } + ] if(policy_type == APPROVER_COUNT_POLICY): policyConfigurationToCreate.type = { @@ -105,12 +130,8 @@ def create_policy(repository_id, branch, 'creatorVoteCounts' : creatorVoteCounts, 'allowDownvotes' : allowDownvotes, 'resetOnSourcePush' : resetOnSourcePush, - 'scope': [ - { - 'repositoryId': repository_id, - 'refName': branch, - 'matchKind': 'exact' - }]} + 'scope': scope + } return policy_client.create_policy_configuration(configuration=policyConfigurationToCreate, project=project) except VstsServiceError as ex: From 8a78a5e0d1d0577978ed6980c6bf2645213a0180 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 16:00:11 +0530 Subject: [PATCH 03/48] build policy --- azure-devops/azext_devops/dev/common/const.py | 5 ++- .../azext_devops/dev/repos/_format.py | 8 ++++- azure-devops/azext_devops/dev/repos/policy.py | 33 +++++++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index 3eef93ae..be6f5559 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -30,4 +30,7 @@ APPROVER_COUNT_POLICY = 'ApproverCountPolicy' APPROVER_COUNT_POLICY_ID = 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd' -REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY] +BUILD_POLICY = 'Buildpolicy' +BUILD_POLICY_ID = '0609b952-1397-4640-95ec-e00a01b2c241' + +REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY, BUILD_POLICY] diff --git a/azure-devops/azext_devops/dev/repos/_format.py b/azure-devops/azext_devops/dev/repos/_format.py index b4576c0d..35770070 100644 --- a/azure-devops/azext_devops/dev/repos/_format.py +++ b/azure-devops/azext_devops/dev/repos/_format.py @@ -27,14 +27,20 @@ def transform_repo_policy_table_output(result): def _transform_repo_policy_request_row(row): table_row = OrderedDict() table_row['ID'] = row['id'] + table_row['Name'] = _get_policy_display_name(row) table_row['Is Blocking'] = row['isBlocking'] table_row['Is Enabled'] = row['isEnabled'] #this will break if policy is applied across repo but that is not possible via UI at least now table_row['Repository Id'] = row['settings']['scope'][0]['repositoryId'] table_row['Branch'] = row['settings']['scope'][0]['refName'] - table_row['Policy Type'] = row['type']['displayName'] return table_row +def _get_policy_display_name(row): + if 'displayName' in row['settings']: + return row['settings']['displayName'] + + return row['type']['displayName'] + def transform_pull_requests_table_output(result): table_output = [] for item in result: diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 4f3571cd..d9b0dfe1 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -69,9 +69,10 @@ def delete_policy(id, organization=None, project=None, detect=None): raise CLIError(ex) def create_policy(repository_id, branch, - isBlocking=True, isEnabled=False, + isBlocking=False, isEnabled=False, policy_type=None, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, + buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, organization=None, project=None, detect=None): """ :param repository_id: Id (UUID) of the repository on which to apply the policy @@ -80,7 +81,7 @@ def create_policy(repository_id, branch, :type branch: string :param isBlocking: Tells if the policy should be blocking or not :type isBlocking: bool - :param isEnabled: Tells if the policy enabled or not + :param isEnabled: Tells if the policy is enabled or not :type isEnabled: bool :param policy_type: Type of policy you want to create :type policy_type: string @@ -94,6 +95,17 @@ def create_policy(repository_id, branch, :param resetOnSourcePush: Reset on Source Push. Required if policy type is ApproverCountPolicy. :type resetOnSourcePush: bool + :param buildDefinitionId: Build Definition Id. Required if policy type is Buildpolicy. + :type buildDefinitionId: int + :param queueOnSourceUpdateOnly: Queue Only on source update. Required if policy type is Buildpolicy. + :type queueOnSourceUpdateOnly: bool + :param manualQueueOnly : Manual Queue Only. Required if policy type is Buildpolicy. + :type manualQueueOnly : bool + :param displayName : Display Name. Required if policy type is Buildpolicy. + :type displayName : string + :param validDuration : Valid duration. Required if policy type is Buildpolicy. + :type validDuration : double + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ :type organization: str :param project: Name or ID of the project. @@ -110,6 +122,9 @@ def create_policy(repository_id, branch, if(policy_type == APPROVER_COUNT_POLICY): if any(v is None for v in [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]): raise CLIError('--minimumApproverCount, --creatorVoteCounts, --allowDownvotes, --resetOnSourcePush are required for ApproverCountPolicy') + elif(policy_type == BUILD_POLICY): + if any(v is None for v in [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]): + raise CLIError('--buildDefinitionId, --queueOnSourceUpdateOnly, --manualQueueOnly, --displayName, --validDuration are required for Buildpolicy') policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) scope = [ @@ -133,6 +148,20 @@ def create_policy(repository_id, branch, 'scope': scope } + elif(policy_type == BUILD_POLICY): + policyConfigurationToCreate.type = { + 'id' : BUILD_POLICY_ID + } + + policyConfigurationToCreate.settings = { + 'buildDefinitionId' : buildDefinitionId, + 'queueOnSourceUpdateOnly' : queueOnSourceUpdateOnly, + 'manualQueueOnly' : manualQueueOnly, + 'displayName' : displayName, + 'validDuration' : validDuration, + 'scope' : scope + } + return policy_client.create_policy_configuration(configuration=policyConfigurationToCreate, project=project) except VstsServiceError as ex: raise CLIError(ex) From dec82574f56e2ff21614e18f9cfef99dd97ebb13 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 16:34:46 +0530 Subject: [PATCH 04/48] simply wow --- azure-devops/azext_devops/dev/repos/policy.py | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index d9b0dfe1..1c3ec97c 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +import sys + from knack.util import CLIError from vsts.exceptions import VstsClientRequestError, VstsServiceError from vsts.policy.v4_0.models.policy_configuration import PolicyConfiguration @@ -121,7 +123,9 @@ def create_policy(repository_id, branch, if(policy_type == APPROVER_COUNT_POLICY): if any(v is None for v in [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]): - raise CLIError('--minimumApproverCount, --creatorVoteCounts, --allowDownvotes, --resetOnSourcePush are required for ApproverCountPolicy') + paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) + raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) + elif(policy_type == BUILD_POLICY): if any(v is None for v in [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]): raise CLIError('--buildDefinitionId, --queueOnSourceUpdateOnly, --manualQueueOnly, --displayName, --validDuration are required for Buildpolicy') @@ -134,19 +138,32 @@ def create_policy(repository_id, branch, 'matchKind': 'exact' } ] + policyConfigurationToCreate.settings = { + 'scope': scope + } + if(policy_type == APPROVER_COUNT_POLICY): policyConfigurationToCreate.type = { 'id' : APPROVER_COUNT_POLICY_ID } - policyConfigurationToCreate.settings = { - 'minimumApproverCount' : minimumApproverCount, - 'creatorVoteCounts' : creatorVoteCounts, - 'allowDownvotes' : allowDownvotes, - 'resetOnSourcePush' : resetOnSourcePush, - 'scope': scope - } + paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) + paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] + index = 0 + + for param in paramNameArray: + policyConfigurationToCreate.settings[param] = paramArray[index] + index = index + 1 + + + # policyConfigurationToCreate.settings = { + # 'minimumApproverCount' : minimumApproverCount, + # 'creatorVoteCounts' : creatorVoteCounts, + # 'allowDownvotes' : allowDownvotes, + # 'resetOnSourcePush' : resetOnSourcePush, + # 'scope': scope + # } elif(policy_type == BUILD_POLICY): policyConfigurationToCreate.type = { @@ -166,3 +183,14 @@ def create_policy(repository_id, branch, except VstsServiceError as ex: raise CLIError(ex) + +def nameOfArray(exp): + frame = sys._getframe(1) + fname = frame.f_code.co_filename + line = frame.f_lineno + with open(fname) as f: + line = f.read().split('\n')[line - 1] + start = line.find('nameOfArray([') + 13 + end = line.find('])', start) + linePassedToFunction = line[start:end] + return [x.strip() for x in linePassedToFunction.split(',')] From 3e39bf37dd8aa11289b13c8b039633755518e991 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 16:39:28 +0530 Subject: [PATCH 05/48] simply wow change --- azure-devops/azext_devops/dev/repos/policy.py | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 1c3ec97c..55ef0d3e 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -141,43 +141,31 @@ def create_policy(repository_id, branch, policyConfigurationToCreate.settings = { 'scope': scope } + + # these 2 will be filled by respective types + paramNameArray = [] + paramArray = [] + typeId = '' if(policy_type == APPROVER_COUNT_POLICY): - policyConfigurationToCreate.type = { - 'id' : APPROVER_COUNT_POLICY_ID - } - + typeId = APPROVER_COUNT_POLICY_ID paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] - index = 0 - - for param in paramNameArray: - policyConfigurationToCreate.settings[param] = paramArray[index] - index = index + 1 - - - # policyConfigurationToCreate.settings = { - # 'minimumApproverCount' : minimumApproverCount, - # 'creatorVoteCounts' : creatorVoteCounts, - # 'allowDownvotes' : allowDownvotes, - # 'resetOnSourcePush' : resetOnSourcePush, - # 'scope': scope - # } elif(policy_type == BUILD_POLICY): - policyConfigurationToCreate.type = { - 'id' : BUILD_POLICY_ID - } - - policyConfigurationToCreate.settings = { - 'buildDefinitionId' : buildDefinitionId, - 'queueOnSourceUpdateOnly' : queueOnSourceUpdateOnly, - 'manualQueueOnly' : manualQueueOnly, - 'displayName' : displayName, - 'validDuration' : validDuration, - 'scope' : scope - } + typeId = BUILD_POLICY_ID + paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] + + policyConfigurationToCreate.type = { + 'id' : typeId + } + + index = 0 + for param in paramNameArray: + policyConfigurationToCreate.settings[param] = paramArray[index] + index = index + 1 return policy_client.create_policy_configuration(configuration=policyConfigurationToCreate, project=project) except VstsServiceError as ex: From b7106ab56d509a7c86f8e0cc81d655bb4ae49fa2 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 16:40:33 +0530 Subject: [PATCH 06/48] some minor cleanup --- azure-devops/azext_devops/dev/repos/policy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 55ef0d3e..f9a9124d 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -128,7 +128,8 @@ def create_policy(repository_id, branch, elif(policy_type == BUILD_POLICY): if any(v is None for v in [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]): - raise CLIError('--buildDefinitionId, --queueOnSourceUpdateOnly, --manualQueueOnly, --displayName, --validDuration are required for Buildpolicy') + paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) scope = [ From 0d3552ea6acdeb137bc2cb3ae6a92f25dbefc5d4 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 16:49:04 +0530 Subject: [PATCH 07/48] minor refactoring --- azure-devops/azext_devops/dev/repos/policy.py | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index f9a9124d..f3cefd4c 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -121,16 +121,29 @@ def create_policy(repository_id, branch, detect=detect, organization=organization, project=project) policy_client = get_policy_client(organization) + # these 2 will be filled by respective types + paramNameArray = [] + paramArray = [] + typeId = '' + if(policy_type == APPROVER_COUNT_POLICY): if any(v is None for v in [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]): paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) + typeId = APPROVER_COUNT_POLICY_ID + paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) + paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] + elif(policy_type == BUILD_POLICY): if any(v is None for v in [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]): paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) + typeId = BUILD_POLICY_ID + paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] + policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) scope = [ { @@ -143,22 +156,6 @@ def create_policy(repository_id, branch, 'scope': scope } - # these 2 will be filled by respective types - paramNameArray = [] - paramArray = [] - typeId = '' - - - if(policy_type == APPROVER_COUNT_POLICY): - typeId = APPROVER_COUNT_POLICY_ID - paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) - paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] - - elif(policy_type == BUILD_POLICY): - typeId = BUILD_POLICY_ID - paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) - paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] - policyConfigurationToCreate.type = { 'id' : typeId } From 597438ba5dfb1b4f6d5ac71696576068d23b23dc Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Thu, 24 Jan 2019 16:59:25 +0530 Subject: [PATCH 08/48] added a UT for nameOfArray --- .../azext_devops/test/repos/test_policy.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 azure-devops/azext_devops/test/repos/test_policy.py diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py new file mode 100644 index 00000000..7294a3bd --- /dev/null +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -0,0 +1,21 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import unittest +from azext_devops.dev.repos.policy import nameOfArray + + +class TestUuidMethods(unittest.TestCase): + + def test_name_of_array(self): + first_name = 'a' + middle_name = 'b' + last_name = 'c' + nameOfArrayResult = nameOfArray([first_name,middle_name,last_name]) + self.assertEqual(nameOfArrayResult, ['first_name','middle_name','last_name']) + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 016c0e6861bc2d6e2db9c0fa5497de50b4cf2519 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 10:37:44 +0530 Subject: [PATCH 09/48] added comment resolution policy --- azure-devops/azext_devops/dev/common/const.py | 18 ++++++++++++++++- azure-devops/azext_devops/dev/repos/policy.py | 20 +++++++++---------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index be6f5559..73bf4da2 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -33,4 +33,20 @@ BUILD_POLICY = 'Buildpolicy' BUILD_POLICY_ID = '0609b952-1397-4640-95ec-e00a01b2c241' -REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY, BUILD_POLICY] +COMMENT_REQUIREMENTS_POLICY = 'CommentRequirementsPolicy' +COMMENT_REQUIREMENTS_POLICY_ID = 'c6a1889d-b943-4856-b76f-9e46bb6b0df2' + +MERGE_STRATEGY_POLICY = 'MergeStrategyPolicy' +MERGE_STRATEGY_POLICY_ID = 'fa4e907d-c16b-4a4c-9dfa-4916e5d171ab' + +FILE_SIZE_POLICY = 'FileSizePolicy' +FILE_SIZE_POLICY_ID = '2e26e725-8201-4edd-8bf5-978563c34a80' + +WORKITEM_LINKING_POLICY = 'WorkItemLinkingPolicy' +WORKITEM_LINKING_POLICY_ID = '40e92b44-2fe1-4dd6-b3d8-74a9c21d0c6e' + +STATUS_POLICY = 'StatusPolicy' +STATUS_POLICY_ID = 'cbdc66da-9728-4af8-aada-9a5a32e4a226' + +REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY, BUILD_POLICY, COMMENT_REQUIREMENTS_POLICY, MERGE_STRATEGY_POLICY, + FILE_SIZE_POLICY, WORKITEM_LINKING_POLICY, STATUS_POLICY] diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index f3cefd4c..08cb2013 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -127,22 +127,20 @@ def create_policy(repository_id, branch, typeId = '' if(policy_type == APPROVER_COUNT_POLICY): - if any(v is None for v in [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]): - paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) - raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) - typeId = APPROVER_COUNT_POLICY_ID - paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] - - elif(policy_type == BUILD_POLICY): - if any(v is None for v in [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]): - paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) + if any(v is None for v in paramArray): raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) - + elif(policy_type == BUILD_POLICY): typeId = BUILD_POLICY_ID - paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] + paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + if any(v is None for v in paramArray): + raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) + elif(policy_type == COMMENT_REQUIREMENTS_POLICY): + typeId = COMMENT_REQUIREMENTS_POLICY_ID + # this particular policy does not need any other parameter policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) scope = [ From e0d72365f219f15143a1811516fa3e7bdf5000f6 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 10:48:08 +0530 Subject: [PATCH 10/48] MergeStrategyPolicy done --- azure-devops/azext_devops/dev/repos/policy.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 08cb2013..63d28e57 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -74,6 +74,7 @@ def create_policy(repository_id, branch, isBlocking=False, isEnabled=False, policy_type=None, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, + useSquashMerge=None, buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, organization=None, project=None, detect=None): """ @@ -108,6 +109,9 @@ def create_policy(repository_id, branch, :param validDuration : Valid duration. Required if policy type is Buildpolicy. :type validDuration : double + :param useSquashMerge: Use Squash Merge. Required if policy type is MergeStrategyPolicy + :type useSquashMerge: bool + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ :type organization: str :param project: Name or ID of the project. @@ -131,16 +135,23 @@ def create_policy(repository_id, branch, paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) if any(v is None for v in paramArray): - raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) + raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), APPROVER_COUNT_POLICY)) elif(policy_type == BUILD_POLICY): typeId = BUILD_POLICY_ID paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) if any(v is None for v in paramArray): - raise CLIError('{} are required for ApproverCountPolicy'.format('--' + ' --'.join(paramNameArray))) + raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), BUILD_POLICY)) elif(policy_type == COMMENT_REQUIREMENTS_POLICY): typeId = COMMENT_REQUIREMENTS_POLICY_ID # this particular policy does not need any other parameter + elif(policy_type == MERGE_STRATEGY_POLICY): + typeId = MERGE_STRATEGY_POLICY_ID + paramArray = [useSquashMerge] + paramNameArray = nameOfArray([useSquashMerge]) + if any(v is None for v in paramArray): + raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), BUILD_POLICY)) + policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) scope = [ From 35bf4cd1e43a4f0369f9628b4484c5e1e5be45a0 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 11:01:56 +0530 Subject: [PATCH 11/48] some refactoring here and there --- azure-devops/azext_devops/dev/repos/policy.py | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 63d28e57..f6b0c4b5 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -76,6 +76,7 @@ def create_policy(repository_id, branch, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, useSquashMerge=None, buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, + maximumGitBlobSizeInBytes=None, useUncompressedSize=None, organization=None, project=None, detect=None): """ :param repository_id: Id (UUID) of the repository on which to apply the policy @@ -112,6 +113,11 @@ def create_policy(repository_id, branch, :param useSquashMerge: Use Squash Merge. Required if policy type is MergeStrategyPolicy :type useSquashMerge: bool + :param maximumGitBlobSizeInBytes: Maximum Git Blob Size In Bytes. Required if policy type is FileSizePolicy + :type maximumGitBlobSizeInBytes: long + :param useUncompressedSize: Use uncompressed size. Required if policy type is FileSizePolicy + :type useUncompressedSize: bool + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ :type organization: str :param project: Name or ID of the project. @@ -128,30 +134,36 @@ def create_policy(repository_id, branch, # these 2 will be filled by respective types paramNameArray = [] paramArray = [] - typeId = '' + policytypeId = '' if(policy_type == APPROVER_COUNT_POLICY): - typeId = APPROVER_COUNT_POLICY_ID + policytypeId = APPROVER_COUNT_POLICY_ID paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) - if any(v is None for v in paramArray): - raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), APPROVER_COUNT_POLICY)) + elif(policy_type == BUILD_POLICY): - typeId = BUILD_POLICY_ID + policytypeId = BUILD_POLICY_ID paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) - if any(v is None for v in paramArray): - raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), BUILD_POLICY)) + elif(policy_type == COMMENT_REQUIREMENTS_POLICY): - typeId = COMMENT_REQUIREMENTS_POLICY_ID + policytypeId = COMMENT_REQUIREMENTS_POLICY_ID # this particular policy does not need any other parameter + elif(policy_type == MERGE_STRATEGY_POLICY): - typeId = MERGE_STRATEGY_POLICY_ID + policytypeId = MERGE_STRATEGY_POLICY_ID paramArray = [useSquashMerge] paramNameArray = nameOfArray([useSquashMerge]) - if any(v is None for v in paramArray): - raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), BUILD_POLICY)) + elif(policy_type == FILE_SIZE_POLICY): + policytypeId = FILE_SIZE_POLICY_ID + paramArray = [maximumGitBlobSizeInBytes, useUncompressedSize] + paramNameArray = nameOfArray([maximumGitBlobSizeInBytes, useUncompressedSize]) + + + + # check if we have value in all the required params or not + raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policy_type) policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) scope = [ @@ -166,7 +178,7 @@ def create_policy(repository_id, branch, } policyConfigurationToCreate.type = { - 'id' : typeId + 'id' : policytypeId } index = 0 @@ -179,6 +191,13 @@ def create_policy(repository_id, branch, raise CLIError(ex) +def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): + if not paramNameArray: + return + if any(v is None for v in paramArray): + raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), policyName)) + + def nameOfArray(exp): frame = sys._getframe(1) fname = frame.f_code.co_filename From 9fb65dad2d4e9ab714419debd1254c33ec7700d6 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 11:03:39 +0530 Subject: [PATCH 12/48] work item linking policy --- azure-devops/azext_devops/dev/repos/policy.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index f6b0c4b5..3e3090e3 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -160,6 +160,10 @@ def create_policy(repository_id, branch, paramArray = [maximumGitBlobSizeInBytes, useUncompressedSize] paramNameArray = nameOfArray([maximumGitBlobSizeInBytes, useUncompressedSize]) + elif(policy_type == WORKITEM_LINKING_POLICY): + policytypeId = WORKITEM_LINKING_POLICY_ID + # this particular policy does not need any other parameter + # check if we have value in all the required params or not From 7dab3ce4d3c3b14cb748f552f1e58034206615e9 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 11:05:34 +0530 Subject: [PATCH 13/48] some cleanup --- azure-devops/azext_devops/dev/common/const.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index 73bf4da2..53db02bf 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -45,8 +45,5 @@ WORKITEM_LINKING_POLICY = 'WorkItemLinkingPolicy' WORKITEM_LINKING_POLICY_ID = '40e92b44-2fe1-4dd6-b3d8-74a9c21d0c6e' -STATUS_POLICY = 'StatusPolicy' -STATUS_POLICY_ID = 'cbdc66da-9728-4af8-aada-9a5a32e4a226' - REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY, BUILD_POLICY, COMMENT_REQUIREMENTS_POLICY, MERGE_STRATEGY_POLICY, FILE_SIZE_POLICY, WORKITEM_LINKING_POLICY, STATUS_POLICY] From 07bee91f06454c917fcb7aff2e121fe385c4ccad Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 11:50:28 +0530 Subject: [PATCH 14/48] required reviewers --- azure-devops/azext_devops/dev/common/const.py | 5 ++- azure-devops/azext_devops/dev/repos/policy.py | 44 ++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index 53db02bf..ff8cd6cc 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -45,5 +45,8 @@ WORKITEM_LINKING_POLICY = 'WorkItemLinkingPolicy' WORKITEM_LINKING_POLICY_ID = '40e92b44-2fe1-4dd6-b3d8-74a9c21d0c6e' +REQUIRED_REVIEWER_POLICY = 'RequiredReviewersPolicy' +REQUIRED_REVIEWER_POLICY_ID = 'fd2167ab-b0be-447a-8ec8-39368250530e' + REPO_POLICY_TYPE = [APPROVER_COUNT_POLICY, BUILD_POLICY, COMMENT_REQUIREMENTS_POLICY, MERGE_STRATEGY_POLICY, - FILE_SIZE_POLICY, WORKITEM_LINKING_POLICY, STATUS_POLICY] + FILE_SIZE_POLICY, WORKITEM_LINKING_POLICY, REQUIRED_REVIEWER_POLICY] diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 3e3090e3..8ab9ab71 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -6,11 +6,15 @@ import sys from knack.util import CLIError +from knack.log import get_logger from vsts.exceptions import VstsClientRequestError, VstsServiceError from vsts.policy.v4_0.models.policy_configuration import PolicyConfiguration from azext_devops.dev.common.services import (get_policy_client, resolve_instance_and_project) from azext_devops.dev.common.const import * +from azext_devops.dev.common.identities import resolve_identity_as_id + +logger = get_logger(__name__) def list_policy(organization=None, project=None, detect=None): @@ -31,6 +35,7 @@ def list_policy(organization=None, project=None, detect=None): except VstsServiceError as ex: raise CLIError(ex) + def get_policy(id, organization=None, project=None, detect=None): """ :param id: ID of the policy. @@ -51,6 +56,7 @@ def get_policy(id, organization=None, project=None, detect=None): except VstsServiceError as ex: raise CLIError(ex) + def delete_policy(id, organization=None, project=None, detect=None): """ :param id: ID of the policy. @@ -70,6 +76,7 @@ def delete_policy(id, organization=None, project=None, detect=None): except VstsServiceError as ex: raise CLIError(ex) + def create_policy(repository_id, branch, isBlocking=False, isEnabled=False, policy_type=None, @@ -77,6 +84,7 @@ def create_policy(repository_id, branch, useSquashMerge=None, buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, maximumGitBlobSizeInBytes=None, useUncompressedSize=None, + optionalReviewerIds=None, requiredReviewerIds=None, message=None, organization=None, project=None, detect=None): """ :param repository_id: Id (UUID) of the repository on which to apply the policy @@ -90,6 +98,13 @@ def create_policy(repository_id, branch, :param policy_type: Type of policy you want to create :type policy_type: string + :param optionalReviewerIds: Optional Reviewers (List of email ids seperated with ';'). Required if policy type is RequiredReviewersPolicy. + :type optionalReviewerIds: string + :param requiredReviewerIds: Required Reviewers (List of email ids seperated with ';'). Required if policy type is RequiredReviewersPolicy. + :type requiredReviewerIds: string + :param message: Message. Required if policy type is RequiredReviewersPolicy. + :type message: string + :param minimumApproverCount: Minimum approver count. Required if policy type is ApproverCountPolicy. :type minimumApproverCount: int :param creatorVoteCounts: Creator vote counts or not. Required if policy type is ApproverCountPolicy @@ -164,7 +179,20 @@ def create_policy(repository_id, branch, policytypeId = WORKITEM_LINKING_POLICY_ID # this particular policy does not need any other parameter - + elif(policy_type == REQUIRED_REVIEWER_POLICY): + policytypeId = REQUIRED_REVIEWER_POLICY_ID + + optionalReviewerIds = resolveIdentityMailsToIds(optionalReviewerIds, organization) + requiredReviewerIds = resolveIdentityMailsToIds(requiredReviewerIds, organization) + + if optionalReviewerIds and (not requiredReviewerIds): + requiredReviewerIds = [] + + if requiredReviewerIds and (not optionalReviewerIds): + optionalReviewerIds = [] + + paramArray = [optionalReviewerIds, requiredReviewerIds, message] + paramNameArray = nameOfArray([optionalReviewerIds, requiredReviewerIds, message]) # check if we have value in all the required params or not raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policy_type) @@ -195,6 +223,20 @@ def create_policy(repository_id, branch, raise CLIError(ex) +def resolveIdentityMailsToIds(mailList, organization): + logger.debug('mail list is {}'.format((mailList))) + if not mailList or (not mailList.strip()): + return None + + idList = [] + for mail in mailList.split(';'): + mailStripped = mail.strip() + logger.debug('trying to resolve {}'.format(mailStripped)) + id = resolve_identity_as_id(mailStripped ,organization) + logger.debug('got id as {}'.format(id)) + idList.append(id) + return idList + def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): if not paramNameArray: return From 535e3c91c37b5b490e9eafe34201f89e2205e682 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 11:53:07 +0530 Subject: [PATCH 15/48] minor --- azure-devops/azext_devops/dev/repos/policy.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 8ab9ab71..2e8263e4 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -181,16 +181,13 @@ def create_policy(repository_id, branch, elif(policy_type == REQUIRED_REVIEWER_POLICY): policytypeId = REQUIRED_REVIEWER_POLICY_ID - optionalReviewerIds = resolveIdentityMailsToIds(optionalReviewerIds, organization) requiredReviewerIds = resolveIdentityMailsToIds(requiredReviewerIds, organization) - + # special handling for this policy if optionalReviewerIds and (not requiredReviewerIds): requiredReviewerIds = [] - if requiredReviewerIds and (not optionalReviewerIds): optionalReviewerIds = [] - paramArray = [optionalReviewerIds, requiredReviewerIds, message] paramNameArray = nameOfArray([optionalReviewerIds, requiredReviewerIds, message]) From df2d36dcc63d5692b16314b5b5a95a552e4e053e Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 13:37:52 +0530 Subject: [PATCH 16/48] minor refactoring for preparaion of update stuff --- azure-devops/azext_devops/dev/repos/policy.py | 160 ++++++++++-------- 1 file changed, 91 insertions(+), 69 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 2e8263e4..0373dc11 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -146,79 +146,101 @@ def create_policy(repository_id, branch, detect=detect, organization=organization, project=project) policy_client = get_policy_client(organization) - # these 2 will be filled by respective types - paramNameArray = [] - paramArray = [] - policytypeId = '' - - if(policy_type == APPROVER_COUNT_POLICY): - policytypeId = APPROVER_COUNT_POLICY_ID - paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] - paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) - - elif(policy_type == BUILD_POLICY): - policytypeId = BUILD_POLICY_ID - paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] - paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) - - elif(policy_type == COMMENT_REQUIREMENTS_POLICY): - policytypeId = COMMENT_REQUIREMENTS_POLICY_ID - # this particular policy does not need any other parameter - - elif(policy_type == MERGE_STRATEGY_POLICY): - policytypeId = MERGE_STRATEGY_POLICY_ID - paramArray = [useSquashMerge] - paramNameArray = nameOfArray([useSquashMerge]) - - elif(policy_type == FILE_SIZE_POLICY): - policytypeId = FILE_SIZE_POLICY_ID - paramArray = [maximumGitBlobSizeInBytes, useUncompressedSize] - paramNameArray = nameOfArray([maximumGitBlobSizeInBytes, useUncompressedSize]) - - elif(policy_type == WORKITEM_LINKING_POLICY): - policytypeId = WORKITEM_LINKING_POLICY_ID - # this particular policy does not need any other parameter - - elif(policy_type == REQUIRED_REVIEWER_POLICY): - policytypeId = REQUIRED_REVIEWER_POLICY_ID - optionalReviewerIds = resolveIdentityMailsToIds(optionalReviewerIds, organization) - requiredReviewerIds = resolveIdentityMailsToIds(requiredReviewerIds, organization) - # special handling for this policy - if optionalReviewerIds and (not requiredReviewerIds): - requiredReviewerIds = [] - if requiredReviewerIds and (not optionalReviewerIds): - optionalReviewerIds = [] - paramArray = [optionalReviewerIds, requiredReviewerIds, message] - paramNameArray = nameOfArray([optionalReviewerIds, requiredReviewerIds, message]) - - # check if we have value in all the required params or not - raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policy_type) - - policyConfigurationToCreate = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) - scope = [ - { - 'repositoryId': repository_id, - 'refName': branch, - 'matchKind': 'exact' - } - ] - policyConfigurationToCreate.settings = { - 'scope': scope - } - - policyConfigurationToCreate.type = { - 'id' : policytypeId - } - - index = 0 - for param in paramNameArray: - policyConfigurationToCreate.settings[param] = paramArray[index] - index = index + 1 + policyConfigurationToCreate = generateConfigurationObject(repository_id, branch, + policy_type, + isBlocking, isEnabled, + minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush, + useSquashMerge, + buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration, + maximumGitBlobSizeInBytes, useUncompressedSize, + optionalReviewerIds, requiredReviewerIds, message, + organization) return policy_client.create_policy_configuration(configuration=policyConfigurationToCreate, project=project) except VstsServiceError as ex: raise CLIError(ex) - + +def generateConfigurationObject( + repository_id,branch, + policy_type=None, + isBlocking=False, isEnabled=False, + minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, + useSquashMerge=None, + buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, + maximumGitBlobSizeInBytes=None, useUncompressedSize=None, + optionalReviewerIds=None, requiredReviewerIds=None, message=None, + organization=None): + # these 2 will be filled by respective types + paramNameArray = [] + paramArray = [] + policytypeId = '' + + if(policy_type == APPROVER_COUNT_POLICY): + policytypeId = APPROVER_COUNT_POLICY_ID + paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] + paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) + + elif(policy_type == BUILD_POLICY): + policytypeId = BUILD_POLICY_ID + paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] + paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + + elif(policy_type == COMMENT_REQUIREMENTS_POLICY): + policytypeId = COMMENT_REQUIREMENTS_POLICY_ID + # this particular policy does not need any other parameter + + elif(policy_type == MERGE_STRATEGY_POLICY): + policytypeId = MERGE_STRATEGY_POLICY_ID + paramArray = [useSquashMerge] + paramNameArray = nameOfArray([useSquashMerge]) + + elif(policy_type == FILE_SIZE_POLICY): + policytypeId = FILE_SIZE_POLICY_ID + paramArray = [maximumGitBlobSizeInBytes, useUncompressedSize] + paramNameArray = nameOfArray([maximumGitBlobSizeInBytes, useUncompressedSize]) + + elif(policy_type == WORKITEM_LINKING_POLICY): + policytypeId = WORKITEM_LINKING_POLICY_ID + # this particular policy does not need any other parameter + + elif(policy_type == REQUIRED_REVIEWER_POLICY): + policytypeId = REQUIRED_REVIEWER_POLICY_ID + optionalReviewerIds = resolveIdentityMailsToIds(optionalReviewerIds, organization) + requiredReviewerIds = resolveIdentityMailsToIds(requiredReviewerIds, organization) + # special handling for this policy + if optionalReviewerIds and (not requiredReviewerIds): + requiredReviewerIds = [] + if requiredReviewerIds and (not optionalReviewerIds): + optionalReviewerIds = [] + paramArray = [optionalReviewerIds, requiredReviewerIds, message] + paramNameArray = nameOfArray([optionalReviewerIds, requiredReviewerIds, message]) + + # check if we have value in all the required params or not + raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policy_type) + + policyConfiguration = PolicyConfiguration(is_blocking=isBlocking, is_enabled=isEnabled) + scope = [ + { + 'repositoryId': repository_id, + 'refName': branch, + 'matchKind': 'exact' + } + ] + policyConfiguration.settings = { + 'scope': scope + } + + policyConfiguration.type = { + 'id' : policytypeId + } + + index = 0 + for param in paramNameArray: + policyConfiguration.settings[param] = paramArray[index] + index = index + 1 + + return policyConfiguration + def resolveIdentityMailsToIds(mailList, organization): logger.debug('mail list is {}'.format((mailList))) From 292a650f4e7e4f2372d56dbe556cecc82026d106 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 13:52:31 +0530 Subject: [PATCH 17/48] added update policy command --- .../azext_devops/dev/repos/commands.py | 1 + azure-devops/azext_devops/dev/repos/policy.py | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/azure-devops/azext_devops/dev/repos/commands.py b/azure-devops/azext_devops/dev/repos/commands.py index c691aca7..795c817d 100644 --- a/azure-devops/azext_devops/dev/repos/commands.py +++ b/azure-devops/azext_devops/dev/repos/commands.py @@ -37,6 +37,7 @@ def load_code_commands(self, _): g.command('create', 'create_policy', table_transformer=transform_repo_policy_table_output) g.command('list', 'list_policy', table_transformer=transform_repo_policies_table_output) g.command('show', 'get_policy', table_transformer=transform_repo_policy_table_output) + g.command('update', 'update_policy', table_transformer=transform_repo_policy_table_output) g.command('delete', 'delete_policy', confirmation='Are you sure you want to delete this policy?') with self.command_group('repos pr', command_type=reposPullRequestOps) as g: diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 0373dc11..0c390878 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -160,6 +160,93 @@ def create_policy(repository_id, branch, except VstsServiceError as ex: raise CLIError(ex) +def update_policy(repository_id, branch, + policy_id, + isBlocking=False, isEnabled=False, + policy_type=None, + minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, + useSquashMerge=None, + buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, + maximumGitBlobSizeInBytes=None, useUncompressedSize=None, + optionalReviewerIds=None, requiredReviewerIds=None, message=None, + organization=None, project=None, detect=None): + """ + :param repository_id: Id (UUID) of the repository on which to apply the policy + :type repository_id: string + :param branch: Branch on which this policy should be applied + :type branch: string + :param policy_id: Policy Id of policy which needs to be updated + :param policy_id: int + :param isBlocking: Tells if the policy should be blocking or not + :type isBlocking: bool + :param isEnabled: Tells if the policy is enabled or not + :type isEnabled: bool + :param policy_type: Type of policy you want to create + :type policy_type: string + + :param optionalReviewerIds: Optional Reviewers (List of email ids seperated with ';'). Required if policy type is RequiredReviewersPolicy. + :type optionalReviewerIds: string + :param requiredReviewerIds: Required Reviewers (List of email ids seperated with ';'). Required if policy type is RequiredReviewersPolicy. + :type requiredReviewerIds: string + :param message: Message. Required if policy type is RequiredReviewersPolicy. + :type message: string + + :param minimumApproverCount: Minimum approver count. Required if policy type is ApproverCountPolicy. + :type minimumApproverCount: int + :param creatorVoteCounts: Creator vote counts or not. Required if policy type is ApproverCountPolicy + :type creatorVoteCounts: bool + :param allowDownvotes: Allow Downvotes. Required if policy type is ApproverCountPolicy. + :type allowDownvotes: bool + :param resetOnSourcePush: Reset on Source Push. Required if policy type is ApproverCountPolicy. + :type resetOnSourcePush: bool + + :param buildDefinitionId: Build Definition Id. Required if policy type is Buildpolicy. + :type buildDefinitionId: int + :param queueOnSourceUpdateOnly: Queue Only on source update. Required if policy type is Buildpolicy. + :type queueOnSourceUpdateOnly: bool + :param manualQueueOnly : Manual Queue Only. Required if policy type is Buildpolicy. + :type manualQueueOnly : bool + :param displayName : Display Name. Required if policy type is Buildpolicy. + :type displayName : string + :param validDuration : Valid duration. Required if policy type is Buildpolicy. + :type validDuration : double + + :param useSquashMerge: Use Squash Merge. Required if policy type is MergeStrategyPolicy + :type useSquashMerge: bool + + :param maximumGitBlobSizeInBytes: Maximum Git Blob Size In Bytes. Required if policy type is FileSizePolicy + :type maximumGitBlobSizeInBytes: long + :param useUncompressedSize: Use uncompressed size. Required if policy type is FileSizePolicy + :type useUncompressedSize: bool + + :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ + :type organization: str + :param project: Name or ID of the project. + :type project: str + :param detect: Automatically detect organization. Default is "on". + :type detect: str + :rtype: :class:` ` + """ + try: + organization, project = resolve_instance_and_project( + detect=detect, organization=organization, project=project) + policy_client = get_policy_client(organization) + + policyConfigurationToCreate = generateConfigurationObject(repository_id, branch, + policy_type, + isBlocking, isEnabled, + minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush, + useSquashMerge, + buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration, + maximumGitBlobSizeInBytes, useUncompressedSize, + optionalReviewerIds, requiredReviewerIds, message, + organization) + + return policy_client.update_policy_configuration(configuration=policyConfigurationToCreate, project=project, + configuration_id=policy_id) + except VstsServiceError as ex: + raise CLIError(ex) + def generateConfigurationObject( repository_id,branch, policy_type=None, From a8121b368405e1197213a939bb2aa7bc8a384dc4 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 14:03:35 +0530 Subject: [PATCH 18/48] list policy UT --- .../azext_devops/test/repos/test_policy.py | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 7294a3bd..f952156f 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -4,18 +4,51 @@ # -------------------------------------------------------------------------------------------- import unittest -from azext_devops.dev.repos.policy import nameOfArray +try: + # Attempt to load mock (works on Python 3.3 and above) + from unittest.mock import patch +except ImportError: + # Attempt to load mock (works on Python version below 3.3) + from mock import patch + +from azext_devops.dev.repos.policy import * +from azext_devops.dev.common.services import clear_connection_cache class TestUuidMethods(unittest.TestCase): + _TEST_DEVOPS_ORGANIZATION = 'https://AzureDevOpsCliTest.visualstudio.com' + _TEST_DEVOPS_PROJECT = 'sample project' + _TEST_PAT_TOKEN = 'lwghjbj67fghokrgxsytghg75nk2ssguljk7a78qpcg2ttygviyt' + + def setUp(self): + self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') + + self.mock_get_policy = self.get_policy_patcher.start() + + clear_connection_cache() + + def tearDown(self): + self.mock_get_policy.stop() + def test_name_of_array(self): first_name = 'a' middle_name = 'b' last_name = 'c' + desired_result = ['first_name','middle_name','last_name'] nameOfArrayResult = nameOfArray([first_name,middle_name,last_name]) - self.assertEqual(nameOfArrayResult, ['first_name','middle_name','last_name']) + self.assertEqual(nameOfArrayResult, desired_result) + #the misplace here is intentional + nameOfArrayResult = nameOfArray([ first_name , middle_name , last_name]) + self.assertEqual(nameOfArrayResult, desired_result) + + def test_list_policy(self): + response = list_policy(organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + #assert + self.mock_get_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT) if __name__ == '__main__': unittest.main() \ No newline at end of file From 7009e092476f327321fb65debbfe1fc962dc336d Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 14:04:17 +0530 Subject: [PATCH 19/48] minor --- azure-devops/azext_devops/test/repos/test_policy.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index f952156f..501d09cb 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -22,14 +22,14 @@ class TestUuidMethods(unittest.TestCase): _TEST_PAT_TOKEN = 'lwghjbj67fghokrgxsytghg75nk2ssguljk7a78qpcg2ttygviyt' def setUp(self): - self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') + self.get_policies_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') - self.mock_get_policy = self.get_policy_patcher.start() + self.mock_get_policies = self.get_policies_patcher.start() clear_connection_cache() def tearDown(self): - self.mock_get_policy.stop() + self.mock_get_policies.stop() def test_name_of_array(self): first_name = 'a' @@ -48,7 +48,7 @@ def test_list_policy(self): detect='off') #assert - self.mock_get_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT) + self.mock_get_policies.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT) if __name__ == '__main__': unittest.main() \ No newline at end of file From 3b9d601b204781310a1d93bfd886d6bc68a0af1c Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 14:08:34 +0530 Subject: [PATCH 20/48] one more UT for get policy --- .../azext_devops/test/repos/test_policy.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 501d09cb..806d80a7 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -23,13 +23,16 @@ class TestUuidMethods(unittest.TestCase): def setUp(self): self.get_policies_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') + self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configuration') self.mock_get_policies = self.get_policies_patcher.start() + self.mock_get_policy = self.get_policy_patcher.start() clear_connection_cache() def tearDown(self): self.mock_get_policies.stop() + self.mock_get_policy.stop() def test_name_of_array(self): first_name = 'a' @@ -43,12 +46,21 @@ def test_name_of_array(self): self.assertEqual(nameOfArrayResult, desired_result) def test_list_policy(self): - response = list_policy(organization = self._TEST_DEVOPS_ORGANIZATION, + list_policy(organization = self._TEST_DEVOPS_ORGANIZATION, project = self._TEST_DEVOPS_PROJECT, detect='off') #assert self.mock_get_policies.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT) + def test_get_policy(self): + get_policy(id = 121, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + + #assert + self.mock_get_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT, configuration_id=121) + if __name__ == '__main__': unittest.main() \ No newline at end of file From e4bdbbc0f69079b15584ffbb5abe07dfe3638455 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 14:10:17 +0530 Subject: [PATCH 21/48] added UT for delete policy --- .../azext_devops/test/repos/test_policy.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 806d80a7..d67b4f9d 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -24,15 +24,18 @@ class TestUuidMethods(unittest.TestCase): def setUp(self): self.get_policies_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configuration') + self.delete_policy = patch('vsts.policy.v4_0.policy_client.PolicyClient.delete_policy_configuration') self.mock_get_policies = self.get_policies_patcher.start() self.mock_get_policy = self.get_policy_patcher.start() + self.mock_delete_policy = self.delete_policy.start() clear_connection_cache() def tearDown(self): self.mock_get_policies.stop() self.mock_get_policy.stop() + self.mock_delete_policy.stop() def test_name_of_array(self): first_name = 'a' @@ -62,5 +65,16 @@ def test_get_policy(self): #assert self.mock_get_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT, configuration_id=121) + def test_delete_policy(self): + delete_policy(id = 121, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + + #assert + self.mock_delete_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT, configuration_id=121) + + + if __name__ == '__main__': unittest.main() \ No newline at end of file From a4a1d194f3eff9652c49058cbf4706cd60c72318 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 14:16:58 +0530 Subject: [PATCH 22/48] some minor things --- azure-devops/azext_devops/test/repos/test_policy.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index d67b4f9d..f1ba48a5 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -74,7 +74,5 @@ def test_delete_policy(self): #assert self.mock_delete_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT, configuration_id=121) - - if __name__ == '__main__': unittest.main() \ No newline at end of file From 8b8abdb6f7a6aa9becf60e95345680290a762453 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 14:25:40 +0530 Subject: [PATCH 23/48] test error message generation --- azure-devops/azext_devops/test/repos/test_policy.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index f1ba48a5..44a18516 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- import unittest +from knack.util import CLIError try: # Attempt to load mock (works on Python 3.3 and above) from unittest.mock import patch @@ -20,6 +21,7 @@ class TestUuidMethods(unittest.TestCase): _TEST_DEVOPS_ORGANIZATION = 'https://AzureDevOpsCliTest.visualstudio.com' _TEST_DEVOPS_PROJECT = 'sample project' _TEST_PAT_TOKEN = 'lwghjbj67fghokrgxsytghg75nk2ssguljk7a78qpcg2ttygviyt' + _TEST_REPOSITORY_ID = 'b4da517c-0398-42dc-b2a8-0d3f240757f9' def setUp(self): self.get_policies_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') @@ -74,5 +76,14 @@ def test_delete_policy(self): #assert self.mock_delete_policy.assert_called_once_with(project=self._TEST_DEVOPS_PROJECT, configuration_id=121) + def test_create_policy_argument_missing_message(self): + try: + create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', + policy_type='ApproverCountPolicy', + minimumApproverCount=2) + self.fail('create should have thrown CLIError') + except CLIError as ex: + self.assertEqual(str(ex), '--minimumApproverCount --creatorVoteCounts --allowDownvotes --resetOnSourcePush are required for ApproverCountPolicy') + if __name__ == '__main__': unittest.main() \ No newline at end of file From b383a08e56de23c3fc925b15db6a5dacf065f609 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:03:54 +0530 Subject: [PATCH 24/48] very small --- azure-devops/azext_devops/test/repos/test_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 44a18516..14533882 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -83,7 +83,9 @@ def test_create_policy_argument_missing_message(self): minimumApproverCount=2) self.fail('create should have thrown CLIError') except CLIError as ex: - self.assertEqual(str(ex), '--minimumApproverCount --creatorVoteCounts --allowDownvotes --resetOnSourcePush are required for ApproverCountPolicy') + #assert + self.assertEqual(str(ex), + '--minimumApproverCount --creatorVoteCounts --allowDownvotes --resetOnSourcePush are required for ApproverCountPolicy') if __name__ == '__main__': unittest.main() \ No newline at end of file From bd277a14a905d40ecc154b5ac5dc9374062929f0 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:11:33 +0530 Subject: [PATCH 25/48] create base test --- .../azext_devops/test/repos/test_policy.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 14533882..e6b859be 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -26,11 +26,13 @@ class TestUuidMethods(unittest.TestCase): def setUp(self): self.get_policies_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configuration') - self.delete_policy = patch('vsts.policy.v4_0.policy_client.PolicyClient.delete_policy_configuration') + self.delete_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.delete_policy_configuration') + self.create_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.create_policy_configuration') self.mock_get_policies = self.get_policies_patcher.start() self.mock_get_policy = self.get_policy_patcher.start() - self.mock_delete_policy = self.delete_policy.start() + self.mock_delete_policy = self.delete_policy_patcher.start() + self.mock_create_policy = self.create_policy_patcher.start() clear_connection_cache() @@ -38,6 +40,7 @@ def tearDown(self): self.mock_get_policies.stop() self.mock_get_policy.stop() self.mock_delete_policy.stop() + self.mock_create_policy.stop() def test_name_of_array(self): first_name = 'a' @@ -87,5 +90,18 @@ def test_create_policy_argument_missing_message(self): self.assertEqual(str(ex), '--minimumApproverCount --creatorVoteCounts --allowDownvotes --resetOnSourcePush are required for ApproverCountPolicy') + def test_create_policy_scope(self): + create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + + self.mock_create_policy.assert_called_once() + create_policy_object = self.mock_create_policy.call_args_list[0][1] + self.assertEqual(self._TEST_DEVOPS_PROJECT, create_policy_object['project'], str(create_policy_object)) + + if __name__ == '__main__': unittest.main() \ No newline at end of file From ed7c0d3032bc7d8af41504ab406efae71bebc190 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:20:09 +0530 Subject: [PATCH 26/48] UT for setting up scope --- azure-devops/azext_devops/test/repos/test_policy.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index e6b859be..6268e706 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -101,6 +101,10 @@ def test_create_policy_scope(self): self.mock_create_policy.assert_called_once() create_policy_object = self.mock_create_policy.call_args_list[0][1] self.assertEqual(self._TEST_DEVOPS_PROJECT, create_policy_object['project'], str(create_policy_object)) + scope = create_policy_object['configuration'].settings['scope'][0] # 0 because we set only only scope from CLI + self.assertEqual(scope['repositoryId'], self._TEST_REPOSITORY_ID) + self.assertEqual(scope['refName'], 'master') + self.assertEqual(scope['matchKind'], 'exact') if __name__ == '__main__': From 20ca49ba3b21519832adb79828e105425390d0c6 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:22:35 +0530 Subject: [PATCH 27/48] setting creation test --- .../azext_devops/test/repos/test_policy.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 6268e706..01858c40 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -105,6 +105,23 @@ def test_create_policy_scope(self): self.assertEqual(scope['repositoryId'], self._TEST_REPOSITORY_ID) self.assertEqual(scope['refName'], 'master') self.assertEqual(scope['matchKind'], 'exact') + + def test_create_policy_setting_creation(self): + create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + + self.mock_create_policy.assert_called_once() + create_policy_object = self.mock_create_policy.call_args_list[0][1] + self.assertEqual(self._TEST_DEVOPS_PROJECT, create_policy_object['project'], str(create_policy_object)) + scope = create_policy_object['configuration'].settings # 0 because we set only only scope from CLI + self.assertEqual(scope['minimumApproverCount'], 2) + self.assertEqual(scope['creatorVoteCounts'], True) + self.assertEqual(scope['allowDownvotes'], False) + self.assertEqual(scope['resetOnSourcePush'], True) if __name__ == '__main__': From b618a4cad084c2dc9aa7da0bfffe60290b3b8879 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:24:33 +0530 Subject: [PATCH 28/48] Id assignment in create policy --- .../azext_devops/test/repos/test_policy.py | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 01858c40..caf4f972 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -117,11 +117,26 @@ def test_create_policy_setting_creation(self): self.mock_create_policy.assert_called_once() create_policy_object = self.mock_create_policy.call_args_list[0][1] self.assertEqual(self._TEST_DEVOPS_PROJECT, create_policy_object['project'], str(create_policy_object)) - scope = create_policy_object['configuration'].settings # 0 because we set only only scope from CLI - self.assertEqual(scope['minimumApproverCount'], 2) - self.assertEqual(scope['creatorVoteCounts'], True) - self.assertEqual(scope['allowDownvotes'], False) - self.assertEqual(scope['resetOnSourcePush'], True) + settings = create_policy_object['configuration'].settings # 0 because we set only only scope from CLI + self.assertEqual(settings['minimumApproverCount'], 2) + self.assertEqual(settings['creatorVoteCounts'], True) + self.assertEqual(settings['allowDownvotes'], False) + self.assertEqual(settings['resetOnSourcePush'], True) + + def test_create_policy_id_assignment(self): + create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + + self.mock_create_policy.assert_called_once() + create_policy_object = self.mock_create_policy.call_args_list[0][1] + self.assertEqual(self._TEST_DEVOPS_PROJECT, create_policy_object['project'], str(create_policy_object)) + policy_type_id = create_policy_object['configuration'].type['id'] # 0 because we set only only scope from CLI + self.assertEqual(policy_type_id, 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd') + if __name__ == '__main__': From a726ae54ee26f2c76b079391a4a21a885b5d5a83 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:34:38 +0530 Subject: [PATCH 29/48] UTs done --- .../azext_devops/test/repos/test_policy.py | 46 ++++++++++++------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index caf4f972..002c6dcd 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -28,11 +28,13 @@ def setUp(self): self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configuration') self.delete_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.delete_policy_configuration') self.create_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.create_policy_configuration') + self.update_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.update_policy_configuration') self.mock_get_policies = self.get_policies_patcher.start() self.mock_get_policy = self.get_policy_patcher.start() self.mock_delete_policy = self.delete_policy_patcher.start() self.mock_create_policy = self.create_policy_patcher.start() + self.mock_update_policy = self.update_policy_patcher.start() clear_connection_cache() @@ -41,6 +43,7 @@ def tearDown(self): self.mock_get_policy.stop() self.mock_delete_policy.stop() self.mock_create_policy.stop() + self.mock_update_policy.stop() def test_name_of_array(self): first_name = 'a' @@ -92,11 +95,11 @@ def test_create_policy_argument_missing_message(self): def test_create_policy_scope(self): create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', - policy_type='ApproverCountPolicy', - minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, - organization = self._TEST_DEVOPS_ORGANIZATION, - project = self._TEST_DEVOPS_PROJECT, - detect='off') + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') self.mock_create_policy.assert_called_once() create_policy_object = self.mock_create_policy.call_args_list[0][1] @@ -108,11 +111,11 @@ def test_create_policy_scope(self): def test_create_policy_setting_creation(self): create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', - policy_type='ApproverCountPolicy', - minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, - organization = self._TEST_DEVOPS_ORGANIZATION, - project = self._TEST_DEVOPS_PROJECT, - detect='off') + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') self.mock_create_policy.assert_called_once() create_policy_object = self.mock_create_policy.call_args_list[0][1] @@ -125,11 +128,11 @@ def test_create_policy_setting_creation(self): def test_create_policy_id_assignment(self): create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', - policy_type='ApproverCountPolicy', - minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, - organization = self._TEST_DEVOPS_ORGANIZATION, - project = self._TEST_DEVOPS_PROJECT, - detect='off') + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') self.mock_create_policy.assert_called_once() create_policy_object = self.mock_create_policy.call_args_list[0][1] @@ -137,7 +140,18 @@ def test_create_policy_id_assignment(self): policy_type_id = create_policy_object['configuration'].type['id'] # 0 because we set only only scope from CLI self.assertEqual(policy_type_id, 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd') - + def test_update_policy(self): + update_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', + policy_id = 121, + policy_type='ApproverCountPolicy', + minimumApproverCount=2, creatorVoteCounts= True, allowDownvotes= False, resetOnSourcePush= True, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + + self.mock_update_policy.assert_called_once() + update_policy_object = self.mock_update_policy.call_args_list[0][1] + self.assertEqual(update_policy_object['configuration_id'], 121) if __name__ == '__main__': unittest.main() \ No newline at end of file From c0fafa9275fcfae389a71546aff77148c287a2e1 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:42:07 +0530 Subject: [PATCH 30/48] fix broken UT --- azure-devops/azext_devops/test/repos/test_policy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 002c6dcd..d21e7493 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -86,7 +86,10 @@ def test_create_policy_argument_missing_message(self): try: create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', policy_type='ApproverCountPolicy', - minimumApproverCount=2) + minimumApproverCount=2, + organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') self.fail('create should have thrown CLIError') except CLIError as ex: #assert From 7942fab37f9c11767cdbebbf1d0a9161469eb5b8 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 15:56:09 +0530 Subject: [PATCH 31/48] style fix in commands --- azure-devops/azext_devops/dev/repos/commands.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/dev/repos/commands.py b/azure-devops/azext_devops/dev/repos/commands.py index 795c817d..37418ea2 100644 --- a/azure-devops/azext_devops/dev/repos/commands.py +++ b/azure-devops/azext_devops/dev/repos/commands.py @@ -4,7 +4,18 @@ # -------------------------------------------------------------------------------------------- from azure.cli.core.commands import CliCommandType -from ._format import * +from ._format import (transform_pull_request_table_output, + transform_pull_requests_table_output, + transform_repo_table_output, + transform_repos_table_output, + transform_reviewers_table_output, + transform_reviewer_table_output, + transform_policies_table_output, + transform_policy_table_output, + transform_work_items_table_output, + transform_repo_import_table_output, + transform_repo_policy_table_output, + transform_repo_policies_table_output) reposPullRequestOps = CliCommandType( From c609c4e39c4c21b4d52b856391727bf41fb23bde Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 16:00:23 +0530 Subject: [PATCH 32/48] try if line-too-long works --- azure-devops/azext_devops/dev/repos/policy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 0c390878..bee49212 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -3,6 +3,8 @@ # Licensed under the MIT License. See License.txt in the project root for license information. # -------------------------------------------------------------------------------------------- +# pylint: disable=line-too-long + import sys from knack.util import CLIError From b7b6a5ceee75da0e48c7be4b796aff8ed0b8f110 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 16:09:56 +0530 Subject: [PATCH 33/48] some style fixes --- azure-devops/azext_devops/dev/repos/policy.py | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index bee49212..4afe055b 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=line-too-long +# This file a lot of these are intentional because that makes grouping variables easy import sys @@ -78,7 +79,7 @@ def delete_policy(id, organization=None, project=None, detect=None): except VstsServiceError as ex: raise CLIError(ex) - +# pylint: disable=too-many-locals def create_policy(repository_id, branch, isBlocking=False, isEnabled=False, policy_type=None, @@ -148,20 +149,22 @@ def create_policy(repository_id, branch, detect=detect, organization=organization, project=project) policy_client = get_policy_client(organization) - policyConfigurationToCreate = generateConfigurationObject(repository_id, branch, - policy_type, - isBlocking, isEnabled, - minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush, - useSquashMerge, - buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration, - maximumGitBlobSizeInBytes, useUncompressedSize, - optionalReviewerIds, requiredReviewerIds, message, - organization) + policyConfigurationToCreate = generateConfigurationObject( + repository_id, branch, + policy_type, + isBlocking, isEnabled, + minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush, + useSquashMerge, + buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration, + maximumGitBlobSizeInBytes, useUncompressedSize, + optionalReviewerIds, requiredReviewerIds, message, + organization) return policy_client.create_policy_configuration(configuration=policyConfigurationToCreate, project=project) except VstsServiceError as ex: raise CLIError(ex) +# pylint: disable=too-many-locals def update_policy(repository_id, branch, policy_id, isBlocking=False, isEnabled=False, @@ -234,23 +237,25 @@ def update_policy(repository_id, branch, detect=detect, organization=organization, project=project) policy_client = get_policy_client(organization) - policyConfigurationToCreate = generateConfigurationObject(repository_id, branch, - policy_type, - isBlocking, isEnabled, - minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush, - useSquashMerge, - buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration, - maximumGitBlobSizeInBytes, useUncompressedSize, - optionalReviewerIds, requiredReviewerIds, message, - organization) + policyConfigurationToCreate = generateConfigurationObject( + repository_id, branch, + policy_type, + isBlocking, isEnabled, + minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush, + useSquashMerge, + buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration, + maximumGitBlobSizeInBytes, useUncompressedSize, + optionalReviewerIds, requiredReviewerIds, message, + organization) return policy_client.update_policy_configuration(configuration=policyConfigurationToCreate, project=project, configuration_id=policy_id) except VstsServiceError as ex: raise CLIError(ex) +# pylint: disable=too-many-locals def generateConfigurationObject( - repository_id,branch, + repository_id, branch, policy_type=None, isBlocking=False, isEnabled=False, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, @@ -264,35 +269,35 @@ def generateConfigurationObject( paramArray = [] policytypeId = '' - if(policy_type == APPROVER_COUNT_POLICY): + if policy_type == APPROVER_COUNT_POLICY: policytypeId = APPROVER_COUNT_POLICY_ID paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) - elif(policy_type == BUILD_POLICY): + elif policy_type == BUILD_POLICY: policytypeId = BUILD_POLICY_ID paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) - elif(policy_type == COMMENT_REQUIREMENTS_POLICY): + elif policy_type == COMMENT_REQUIREMENTS_POLICY: policytypeId = COMMENT_REQUIREMENTS_POLICY_ID # this particular policy does not need any other parameter - elif(policy_type == MERGE_STRATEGY_POLICY): + elif policy_type == MERGE_STRATEGY_POLICY: policytypeId = MERGE_STRATEGY_POLICY_ID paramArray = [useSquashMerge] paramNameArray = nameOfArray([useSquashMerge]) - elif(policy_type == FILE_SIZE_POLICY): + elif policy_type == FILE_SIZE_POLICY: policytypeId = FILE_SIZE_POLICY_ID paramArray = [maximumGitBlobSizeInBytes, useUncompressedSize] paramNameArray = nameOfArray([maximumGitBlobSizeInBytes, useUncompressedSize]) - elif(policy_type == WORKITEM_LINKING_POLICY): + elif policy_type == WORKITEM_LINKING_POLICY: policytypeId = WORKITEM_LINKING_POLICY_ID # this particular policy does not need any other parameter - elif(policy_type == REQUIRED_REVIEWER_POLICY): + elif policy_type == REQUIRED_REVIEWER_POLICY: policytypeId = REQUIRED_REVIEWER_POLICY_ID optionalReviewerIds = resolveIdentityMailsToIds(optionalReviewerIds, organization) requiredReviewerIds = resolveIdentityMailsToIds(requiredReviewerIds, organization) @@ -338,11 +343,11 @@ def resolveIdentityMailsToIds(mailList, organization): idList = [] for mail in mailList.split(';'): - mailStripped = mail.strip() - logger.debug('trying to resolve {}'.format(mailStripped)) - id = resolve_identity_as_id(mailStripped ,organization) - logger.debug('got id as {}'.format(id)) - idList.append(id) + mailStripped = mail.strip() + logger.debug('trying to resolve {}'.format(mailStripped)) + id = resolve_identity_as_id(mailStripped, organization) + logger.debug('got id as {}'.format(id)) + idList.append(id) return idList def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): From d7c53cd2c932383a8b36ffd4106fd67499796a5c Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 17:15:50 +0530 Subject: [PATCH 34/48] some style fixes --- azure-devops/azext_devops/dev/repos/policy.py | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 4afe055b..7b5aead8 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -39,7 +39,7 @@ def list_policy(organization=None, project=None, detect=None): raise CLIError(ex) -def get_policy(id, organization=None, project=None, detect=None): +def get_policy(id, organization=None, project=None, detect=None): # pylint: disable=redefined-builtin """ :param id: ID of the policy. :type id: int @@ -60,7 +60,7 @@ def get_policy(id, organization=None, project=None, detect=None): raise CLIError(ex) -def delete_policy(id, organization=None, project=None, detect=None): +def delete_policy(id, organization=None, project=None, detect=None): # pylint: disable=redefined-builtin """ :param id: ID of the policy. :type id: int @@ -176,7 +176,7 @@ def update_policy(repository_id, branch, optionalReviewerIds=None, requiredReviewerIds=None, message=None, organization=None, project=None, detect=None): """ - :param repository_id: Id (UUID) of the repository on which to apply the policy + :param repository_id: Id (UUID) of the repository on which to apply the policy to. :type repository_id: string :param branch: Branch on which this policy should be applied :type branch: string @@ -248,22 +248,24 @@ def update_policy(repository_id, branch, optionalReviewerIds, requiredReviewerIds, message, organization) - return policy_client.update_policy_configuration(configuration=policyConfigurationToCreate, project=project, - configuration_id=policy_id) + return policy_client.update_policy_configuration( + configuration=policyConfigurationToCreate, + project=project, + configuration_id=policy_id) except VstsServiceError as ex: raise CLIError(ex) # pylint: disable=too-many-locals -def generateConfigurationObject( - repository_id, branch, - policy_type=None, - isBlocking=False, isEnabled=False, - minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, - useSquashMerge=None, - buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, - maximumGitBlobSizeInBytes=None, useUncompressedSize=None, - optionalReviewerIds=None, requiredReviewerIds=None, message=None, - organization=None): + +def generateConfigurationObject(repository_id, branch, + policy_type=None, + isBlocking=False, isEnabled=False, + minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, + useSquashMerge=None, + buildDefinitionId=None, queueOnSourceUpdateOnly=None, manualQueueOnly=None, displayName=None, validDuration=None, + maximumGitBlobSizeInBytes=None, useUncompressedSize=None, + optionalReviewerIds=None, requiredReviewerIds=None, message=None, + organization=None): # these 2 will be filled by respective types paramNameArray = [] paramArray = [] @@ -337,7 +339,7 @@ def generateConfigurationObject( def resolveIdentityMailsToIds(mailList, organization): - logger.debug('mail list is {}'.format((mailList))) + logger.debug('mail list {}'.format((mailList))) if not mailList or (not mailList.strip()): return None @@ -345,9 +347,10 @@ def resolveIdentityMailsToIds(mailList, organization): for mail in mailList.split(';'): mailStripped = mail.strip() logger.debug('trying to resolve {}'.format(mailStripped)) - id = resolve_identity_as_id(mailStripped, organization) - logger.debug('got id as {}'.format(id)) - idList.append(id) + identityId = resolve_identity_as_id(mailStripped, organization) + logger.debug('got id as {}'.format(identityId)) + idList.append(identityId) + return idList def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): @@ -358,6 +361,7 @@ def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): def nameOfArray(exp): + logger.debug(str(exp)) frame = sys._getframe(1) fname = frame.f_code.co_filename line = frame.f_lineno From 06e0c69604c6392097a7c1d3f4cce09e6fc854f0 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 17:26:50 +0530 Subject: [PATCH 35/48] more style fixes --- azure-devops/azext_devops/dev/repos/policy.py | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 7b5aead8..96fb0590 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -10,11 +10,24 @@ from knack.util import CLIError from knack.log import get_logger -from vsts.exceptions import VstsClientRequestError, VstsServiceError +from vsts.exceptions import VstsServiceError from vsts.policy.v4_0.models.policy_configuration import PolicyConfiguration from azext_devops.dev.common.services import (get_policy_client, resolve_instance_and_project) -from azext_devops.dev.common.const import * +from azext_devops.dev.common.const import (APPROVER_COUNT_POLICY, + APPROVER_COUNT_POLICY_ID, + BUILD_POLICY, + BUILD_POLICY_ID, + COMMENT_REQUIREMENTS_POLICY, + COMMENT_REQUIREMENTS_POLICY_ID, + MERGE_STRATEGY_POLICY, + MERGE_STRATEGY_POLICY_ID, + FILE_SIZE_POLICY, + FILE_SIZE_POLICY_ID, + WORKITEM_LINKING_POLICY, + WORKITEM_LINKING_POLICY_ID, + REQUIRED_REVIEWER_POLICY, + REQUIRED_REVIEWER_POLICY_ID) from azext_devops.dev.common.identities import resolve_identity_as_id logger = get_logger(__name__) @@ -339,16 +352,16 @@ def generateConfigurationObject(repository_id, branch, def resolveIdentityMailsToIds(mailList, organization): - logger.debug('mail list {}'.format((mailList))) + 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 {}'.format(mailStripped)) + logger.debug('trying to resolve %s' %mailStripped) identityId = resolve_identity_as_id(mailStripped, organization) - logger.debug('got id as {}'.format(identityId)) + logger.debug('got id as %s' %identityId) idList.append(identityId) return idList @@ -362,7 +375,9 @@ def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): def nameOfArray(exp): logger.debug(str(exp)) - frame = sys._getframe(1) + # without the below protected access it will be very hard to implement policy create + # also we have a UT for this so we will catch any break ASAP + frame = sys._getframe(1) # pylint: disable=protected-access fname = frame.f_code.co_filename line = frame.f_lineno with open(fname) as f: From d5758f0aa3ddee6bf12cb7101c83f78a0b4245b8 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 17:30:35 +0530 Subject: [PATCH 36/48] remove the controversial code --- azure-devops/azext_devops/dev/repos/policy.py | 25 ++++--------------- .../azext_devops/test/repos/test_policy.py | 11 -------- 2 files changed, 5 insertions(+), 31 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 96fb0590..6e662c3a 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -287,12 +287,12 @@ def generateConfigurationObject(repository_id, branch, if policy_type == APPROVER_COUNT_POLICY: policytypeId = APPROVER_COUNT_POLICY_ID paramArray = [minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush] - paramNameArray = nameOfArray([minimumApproverCount, creatorVoteCounts, allowDownvotes, resetOnSourcePush]) + paramNameArray = ['minimumApproverCount', 'creatorVoteCounts', 'allowDownvotes', 'resetOnSourcePush'] elif policy_type == BUILD_POLICY: policytypeId = BUILD_POLICY_ID paramArray = [buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration] - paramNameArray = nameOfArray([buildDefinitionId, queueOnSourceUpdateOnly, manualQueueOnly, displayName, validDuration]) + paramNameArray = ['buildDefinitionId', 'queueOnSourceUpdateOnly', 'manualQueueOnly', 'displayName', 'validDuration'] elif policy_type == COMMENT_REQUIREMENTS_POLICY: policytypeId = COMMENT_REQUIREMENTS_POLICY_ID @@ -301,12 +301,12 @@ def generateConfigurationObject(repository_id, branch, elif policy_type == MERGE_STRATEGY_POLICY: policytypeId = MERGE_STRATEGY_POLICY_ID paramArray = [useSquashMerge] - paramNameArray = nameOfArray([useSquashMerge]) + paramNameArray = ['useSquashMerge'] elif policy_type == FILE_SIZE_POLICY: policytypeId = FILE_SIZE_POLICY_ID paramArray = [maximumGitBlobSizeInBytes, useUncompressedSize] - paramNameArray = nameOfArray([maximumGitBlobSizeInBytes, useUncompressedSize]) + paramNameArray = ['maximumGitBlobSizeInBytes', 'useUncompressedSize'] elif policy_type == WORKITEM_LINKING_POLICY: policytypeId = WORKITEM_LINKING_POLICY_ID @@ -322,7 +322,7 @@ def generateConfigurationObject(repository_id, branch, if requiredReviewerIds and (not optionalReviewerIds): optionalReviewerIds = [] paramArray = [optionalReviewerIds, requiredReviewerIds, message] - paramNameArray = nameOfArray([optionalReviewerIds, requiredReviewerIds, message]) + paramNameArray = ['optionalReviewerIds', 'requiredReviewerIds', 'message'] # check if we have value in all the required params or not raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policy_type) @@ -371,18 +371,3 @@ def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): return if any(v is None for v in paramArray): raise CLIError('{} are required for {}'.format('--' + ' --'.join(paramNameArray), policyName)) - - -def nameOfArray(exp): - logger.debug(str(exp)) - # without the below protected access it will be very hard to implement policy create - # also we have a UT for this so we will catch any break ASAP - frame = sys._getframe(1) # pylint: disable=protected-access - fname = frame.f_code.co_filename - line = frame.f_lineno - with open(fname) as f: - line = f.read().split('\n')[line - 1] - start = line.find('nameOfArray([') + 13 - end = line.find('])', start) - linePassedToFunction = line[start:end] - return [x.strip() for x in linePassedToFunction.split(',')] diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index d21e7493..92326f47 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -45,17 +45,6 @@ def tearDown(self): self.mock_create_policy.stop() self.mock_update_policy.stop() - def test_name_of_array(self): - first_name = 'a' - middle_name = 'b' - last_name = 'c' - desired_result = ['first_name','middle_name','last_name'] - nameOfArrayResult = nameOfArray([first_name,middle_name,last_name]) - self.assertEqual(nameOfArrayResult, desired_result) - #the misplace here is intentional - nameOfArrayResult = nameOfArray([ first_name , middle_name , last_name]) - self.assertEqual(nameOfArrayResult, desired_result) - def test_list_policy(self): list_policy(organization = self._TEST_DEVOPS_ORGANIZATION, project = self._TEST_DEVOPS_PROJECT, From 18c7abf570cfe2e4b22b1cf69374488f387733fb Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 17:36:20 +0530 Subject: [PATCH 37/48] fix some more styles --- azure-devops/azext_devops/dev/repos/policy.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 6e662c3a..d81f8c80 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -6,8 +6,6 @@ # pylint: disable=line-too-long # This file a lot of these are intentional because that makes grouping variables easy -import sys - from knack.util import CLIError from knack.log import get_logger from vsts.exceptions import VstsServiceError @@ -352,16 +350,16 @@ def generateConfigurationObject(repository_id, branch, def resolveIdentityMailsToIds(mailList, organization): - logger.debug('mail list %s ' % mailList) + 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) + logger.debug('trying to resolve %s', mailStripped) identityId = resolve_identity_as_id(mailStripped, organization) - logger.debug('got id as %s' %identityId) + logger.debug('got id as %s', identityId) idList.append(identityId) return idList From 2f90ae87c877044beda54b8eb10c7298552113a5 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 17:44:16 +0530 Subject: [PATCH 38/48] flake 8 fixes --- azure-devops/azext_devops/dev/repos/_format.py | 5 ++++- azure-devops/azext_devops/dev/repos/policy.py | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/_format.py b/azure-devops/azext_devops/dev/repos/_format.py index 35770070..3e7faaea 100644 --- a/azure-devops/azext_devops/dev/repos/_format.py +++ b/azure-devops/azext_devops/dev/repos/_format.py @@ -24,23 +24,26 @@ def transform_repo_policy_table_output(result): return table_output + def _transform_repo_policy_request_row(row): table_row = OrderedDict() table_row['ID'] = row['id'] table_row['Name'] = _get_policy_display_name(row) table_row['Is Blocking'] = row['isBlocking'] table_row['Is Enabled'] = row['isEnabled'] - #this will break if policy is applied across repo but that is not possible via UI at least now + # this will break if policy is applied across repo but that is not possible via UI at least now table_row['Repository Id'] = row['settings']['scope'][0]['repositoryId'] table_row['Branch'] = row['settings']['scope'][0]['refName'] return table_row + def _get_policy_display_name(row): if 'displayName' in row['settings']: return row['settings']['displayName'] return row['type']['displayName'] + def transform_pull_requests_table_output(result): table_output = [] for item in result: diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index d81f8c80..969d2012 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -90,6 +90,7 @@ def delete_policy(id, organization=None, project=None, detect=None): # pylint: except VstsServiceError as ex: raise CLIError(ex) + # pylint: disable=too-many-locals def create_policy(repository_id, branch, isBlocking=False, isEnabled=False, @@ -175,6 +176,7 @@ def create_policy(repository_id, branch, except VstsServiceError as ex: raise CLIError(ex) + # pylint: disable=too-many-locals def update_policy(repository_id, branch, policy_id, @@ -266,8 +268,8 @@ def update_policy(repository_id, branch, except VstsServiceError as ex: raise CLIError(ex) -# pylint: disable=too-many-locals +# pylint: disable=too-many-locals def generateConfigurationObject(repository_id, branch, policy_type=None, isBlocking=False, isEnabled=False, @@ -333,12 +335,13 @@ def generateConfigurationObject(repository_id, branch, 'matchKind': 'exact' } ] + policyConfiguration.settings = { 'scope': scope - } + } policyConfiguration.type = { - 'id' : policytypeId + 'id': policytypeId } index = 0 @@ -364,6 +367,7 @@ def resolveIdentityMailsToIds(mailList, organization): return idList + def raiseErrorIfRequiredParamMissing(paramArray, paramNameArray, policyName): if not paramNameArray: return From b358a67c9f3956a1cd53d5c37a4427b3e7fe6ada Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Fri, 25 Jan 2019 17:49:51 +0530 Subject: [PATCH 39/48] final flake 8 fix --- azure-devops/azext_devops/dev/repos/_format.py | 1 - 1 file changed, 1 deletion(-) diff --git a/azure-devops/azext_devops/dev/repos/_format.py b/azure-devops/azext_devops/dev/repos/_format.py index 3e7faaea..67d5b045 100644 --- a/azure-devops/azext_devops/dev/repos/_format.py +++ b/azure-devops/azext_devops/dev/repos/_format.py @@ -24,7 +24,6 @@ def transform_repo_policy_table_output(result): return table_output - def _transform_repo_policy_request_row(row): table_row = OrderedDict() table_row['ID'] = row['id'] From 1be208b873023ab943b0df5445d0f5a97bf0142a Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 11:35:09 +0530 Subject: [PATCH 40/48] added policy_configuration --- azure-devops/azext_devops/dev/repos/policy.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 969d2012..3d98f681 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -92,7 +92,8 @@ def delete_policy(id, organization=None, project=None, detect=None): # pylint: # pylint: disable=too-many-locals -def create_policy(repository_id, branch, +def create_policy(policy_configuration=None, + repository_id=None, branch=None, isBlocking=False, isEnabled=False, policy_type=None, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, @@ -102,6 +103,11 @@ def create_policy(repository_id, branch, optionalReviewerIds=None, requiredReviewerIds=None, message=None, organization=None, project=None, detect=None): """ + :param policy_configuration: File path of file containing policy configuration to create in a serialized form. + please use / backslash when typing in directory path. + Only --project and --organization param are needed when passing this. + :type policy_configuration: string + :param repository_id: Id (UUID) of the repository on which to apply the policy :type repository_id: string :param branch: Branch on which this policy should be applied @@ -162,6 +168,7 @@ def create_policy(repository_id, branch, policy_client = get_policy_client(organization) policyConfigurationToCreate = generateConfigurationObject( + policy_configuration, repository_id, branch, policy_type, isBlocking, isEnabled, @@ -178,8 +185,9 @@ def create_policy(repository_id, branch, # pylint: disable=too-many-locals -def update_policy(repository_id, branch, - policy_id, +def update_policy(policy_configuration=None, + repository_id=None, branch=None, + policy_id=None, isBlocking=False, isEnabled=False, policy_type=None, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, @@ -251,6 +259,7 @@ def update_policy(repository_id, branch, policy_client = get_policy_client(organization) policyConfigurationToCreate = generateConfigurationObject( + policy_configuration, repository_id, branch, policy_type, isBlocking, isEnabled, @@ -270,7 +279,8 @@ def update_policy(repository_id, branch, # pylint: disable=too-many-locals -def generateConfigurationObject(repository_id, branch, +def generateConfigurationObject(policy_configuration=None, + repository_id=None, branch=None, policy_type=None, isBlocking=False, isEnabled=False, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, @@ -279,6 +289,11 @@ def generateConfigurationObject(repository_id, branch, maximumGitBlobSizeInBytes=None, useUncompressedSize=None, optionalReviewerIds=None, requiredReviewerIds=None, message=None, organization=None): + if policy_configuration != None: + with open(policy_configuration) as f: + import json + return json.load(f) + # these 2 will be filled by respective types paramNameArray = [] paramArray = [] From 47e2a95a285882078b6d7685fa6656b47f987a28 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 11:40:53 +0530 Subject: [PATCH 41/48] fixing help texts --- azure-devops/azext_devops/dev/repos/policy.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 3d98f681..3b75c309 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -112,9 +112,9 @@ def create_policy(policy_configuration=None, :type repository_id: string :param branch: Branch on which this policy should be applied :type branch: string - :param isBlocking: Tells if the policy should be blocking or not + :param isBlocking: Whether the policy should be blocking or not. Default value is false. Accepted values are true and false. :type isBlocking: bool - :param isEnabled: Tells if the policy is enabled or not + :param isEnabled: Whether the policy is enabled or not. By default the value is true. Accepted values and true and false. :type isEnabled: bool :param policy_type: Type of policy you want to create :type policy_type: string @@ -128,30 +128,30 @@ def create_policy(policy_configuration=None, :param minimumApproverCount: Minimum approver count. Required if policy type is ApproverCountPolicy. :type minimumApproverCount: int - :param creatorVoteCounts: Creator vote counts or not. Required if policy type is ApproverCountPolicy + :param creatorVoteCounts: Whether the creator's vote count counts or not. Required if policy type is ApproverCountPolicy :type creatorVoteCounts: bool - :param allowDownvotes: Allow Downvotes. Required if policy type is ApproverCountPolicy. + :param allowDownvotes: Whether to allow downvotes or not. Required if policy type is ApproverCountPolicy. :type allowDownvotes: bool - :param resetOnSourcePush: Reset on Source Push. Required if policy type is ApproverCountPolicy. + :param resetOnSourcePush: Whether to reset source on push. Required if policy type is ApproverCountPolicy. :type resetOnSourcePush: bool :param buildDefinitionId: Build Definition Id. Required if policy type is Buildpolicy. :type buildDefinitionId: int :param queueOnSourceUpdateOnly: Queue Only on source update. Required if policy type is Buildpolicy. :type queueOnSourceUpdateOnly: bool - :param manualQueueOnly : Manual Queue Only. Required if policy type is Buildpolicy. + :param manualQueueOnly : Whether to allow only manual queue of builds. Required if policy type is Buildpolicy. :type manualQueueOnly : bool :param displayName : Display Name. Required if policy type is Buildpolicy. :type displayName : string - :param validDuration : Valid duration. Required if policy type is Buildpolicy. + :param validDuration : Policy validity duration (in hours). Required if policy type is Buildpolicy. :type validDuration : double - :param useSquashMerge: Use Squash Merge. Required if policy type is MergeStrategyPolicy + :param useSquashMerge: Whether to squash merge always. Required if policy type is MergeStrategyPolicy :type useSquashMerge: bool :param maximumGitBlobSizeInBytes: Maximum Git Blob Size In Bytes. Required if policy type is FileSizePolicy :type maximumGitBlobSizeInBytes: long - :param useUncompressedSize: Use uncompressed size. Required if policy type is FileSizePolicy + :param useUncompressedSize: Whether to use uncompressed size. Required if policy type is FileSizePolicy :type useUncompressedSize: bool :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ @@ -201,11 +201,11 @@ def update_policy(policy_configuration=None, :type repository_id: string :param branch: Branch on which this policy should be applied :type branch: string - :param policy_id: Policy Id of policy which needs to be updated + :param policy_id: ID of the policy which needs to be updated :param policy_id: int - :param isBlocking: Tells if the policy should be blocking or not + :param isBlocking: Whether the policy should be blocking or not. Default value is false. Accepted values are true and false. :type isBlocking: bool - :param isEnabled: Tells if the policy is enabled or not + :param isEnabled: Whether the policy is enabled or not. By default the value is true. Accepted values and true and false. :type isEnabled: bool :param policy_type: Type of policy you want to create :type policy_type: string @@ -219,30 +219,30 @@ def update_policy(policy_configuration=None, :param minimumApproverCount: Minimum approver count. Required if policy type is ApproverCountPolicy. :type minimumApproverCount: int - :param creatorVoteCounts: Creator vote counts or not. Required if policy type is ApproverCountPolicy + :param creatorVoteCounts: Whether the creator's vote count counts or not. Required if policy type is ApproverCountPolicy :type creatorVoteCounts: bool - :param allowDownvotes: Allow Downvotes. Required if policy type is ApproverCountPolicy. + :param allowDownvotes: Whether to allow downvotes or not. Required if policy type is ApproverCountPolicy. :type allowDownvotes: bool - :param resetOnSourcePush: Reset on Source Push. Required if policy type is ApproverCountPolicy. + :param resetOnSourcePush: Whether to reset source on push. Required if policy type is ApproverCountPolicy. :type resetOnSourcePush: bool :param buildDefinitionId: Build Definition Id. Required if policy type is Buildpolicy. :type buildDefinitionId: int :param queueOnSourceUpdateOnly: Queue Only on source update. Required if policy type is Buildpolicy. :type queueOnSourceUpdateOnly: bool - :param manualQueueOnly : Manual Queue Only. Required if policy type is Buildpolicy. + :param manualQueueOnly : Whether to allow only manual queue of builds. Required if policy type is Buildpolicy. :type manualQueueOnly : bool :param displayName : Display Name. Required if policy type is Buildpolicy. :type displayName : string - :param validDuration : Valid duration. Required if policy type is Buildpolicy. + :param validDuration : Policy validity duration (in hours). Required if policy type is Buildpolicy. :type validDuration : double - :param useSquashMerge: Use Squash Merge. Required if policy type is MergeStrategyPolicy + :param useSquashMerge: Whether to squash merge always. Required if policy type is MergeStrategyPolicy :type useSquashMerge: bool :param maximumGitBlobSizeInBytes: Maximum Git Blob Size In Bytes. Required if policy type is FileSizePolicy :type maximumGitBlobSizeInBytes: long - :param useUncompressedSize: Use uncompressed size. Required if policy type is FileSizePolicy + :param useUncompressedSize: Whether to use uncompressed size. Required if policy type is FileSizePolicy :type useUncompressedSize: bool :param organization: Azure Devops organization URL. Example: https://dev.azure.com/MyOrganizationName/ From b6250b6c5f420c4ac81d4016650e4b2fa0df4386 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 11:55:14 +0530 Subject: [PATCH 42/48] Pr comments --- azure-devops/azext_devops/dev/common/const.py | 2 +- azure-devops/azext_devops/dev/repos/_help.py | 6 ++++++ azure-devops/azext_devops/test/repos/test_policy.py | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/azure-devops/azext_devops/dev/common/const.py b/azure-devops/azext_devops/dev/common/const.py index ff8cd6cc..64e953f8 100644 --- a/azure-devops/azext_devops/dev/common/const.py +++ b/azure-devops/azext_devops/dev/common/const.py @@ -30,7 +30,7 @@ APPROVER_COUNT_POLICY = 'ApproverCountPolicy' APPROVER_COUNT_POLICY_ID = 'fa4e907d-c16b-4a4c-9dfa-4906e5d171dd' -BUILD_POLICY = 'Buildpolicy' +BUILD_POLICY = 'BuildPolicy' BUILD_POLICY_ID = '0609b952-1397-4640-95ec-e00a01b2c241' COMMENT_REQUIREMENTS_POLICY = 'CommentRequirementsPolicy' diff --git a/azure-devops/azext_devops/dev/repos/_help.py b/azure-devops/azext_devops/dev/repos/_help.py index 47ed0048..ec014d94 100644 --- a/azure-devops/azext_devops/dev/repos/_help.py +++ b/azure-devops/azext_devops/dev/repos/_help.py @@ -13,6 +13,12 @@ def load_repos_help(): long-summary: """ + helps['repos policies'] = """ + type: group + short-summary: Manage Azure Repos branch policies. + long-summary: + """ + helps['repos pr'] = """ type: group short-summary: Manage pull requests. diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 92326f47..272d6749 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -18,7 +18,7 @@ class TestUuidMethods(unittest.TestCase): - _TEST_DEVOPS_ORGANIZATION = 'https://AzureDevOpsCliTest.visualstudio.com' + _TEST_DEVOPS_ORGANIZATION = 'https://someorgaaaa.visualstudio.com' _TEST_DEVOPS_PROJECT = 'sample project' _TEST_PAT_TOKEN = 'lwghjbj67fghokrgxsytghg75nk2ssguljk7a78qpcg2ttygviyt' _TEST_REPOSITORY_ID = 'b4da517c-0398-42dc-b2a8-0d3f240757f9' From aa96f25c425f40e1e319dda56ec077e56607db80 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 11:56:06 +0530 Subject: [PATCH 43/48] minor style fix --- azure-devops/azext_devops/dev/repos/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 3b75c309..eb4d5869 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -289,7 +289,7 @@ def generateConfigurationObject(policy_configuration=None, maximumGitBlobSizeInBytes=None, useUncompressedSize=None, optionalReviewerIds=None, requiredReviewerIds=None, message=None, organization=None): - if policy_configuration != None: + if policy_configuration is not None: with open(policy_configuration) as f: import json return json.load(f) From dc09dc810b1232c0764905939700df45df0e4b09 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 12:16:36 +0530 Subject: [PATCH 44/48] fix UT to run without network --- azure-devops/azext_devops/test/repos/test_policy.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index 272d6749..c86fa0a3 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -14,31 +14,37 @@ from azext_devops.dev.repos.policy import * from azext_devops.dev.common.services import clear_connection_cache +from vsts.policy.v4_0.policy_client import PolicyClient class TestUuidMethods(unittest.TestCase): - _TEST_DEVOPS_ORGANIZATION = 'https://someorgaaaa.visualstudio.com' + _TEST_DEVOPS_ORGANIZATION = 'https://someorg.visualstudio.com' _TEST_DEVOPS_PROJECT = 'sample project' _TEST_PAT_TOKEN = 'lwghjbj67fghokrgxsytghg75nk2ssguljk7a78qpcg2ttygviyt' _TEST_REPOSITORY_ID = 'b4da517c-0398-42dc-b2a8-0d3f240757f9' def setUp(self): + self.get_client = patch('vsts.vss_connection.VssConnection.get_client') self.get_policies_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configurations') self.get_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.get_policy_configuration') self.delete_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.delete_policy_configuration') self.create_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.create_policy_configuration') self.update_policy_patcher = patch('vsts.policy.v4_0.policy_client.PolicyClient.update_policy_configuration') + self.mock_get_client = self.get_client.start() self.mock_get_policies = self.get_policies_patcher.start() self.mock_get_policy = self.get_policy_patcher.start() self.mock_delete_policy = self.delete_policy_patcher.start() self.mock_create_policy = self.create_policy_patcher.start() self.mock_update_policy = self.update_policy_patcher.start() + self.mock_get_client.return_value = PolicyClient(base_url=self._TEST_DEVOPS_ORGANIZATION) + clear_connection_cache() def tearDown(self): + self.mock_get_client.stop() self.mock_get_policies.stop() self.mock_get_policy.stop() self.mock_delete_policy.stop() From 4c479117c143d44748cda0155f0d6d1126713c53 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 12:27:29 +0530 Subject: [PATCH 45/48] try to stop pather --- azure-devops/azext_devops/test/repos/test_policy.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index c86fa0a3..d84affdf 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -44,12 +44,12 @@ def setUp(self): clear_connection_cache() def tearDown(self): - self.mock_get_client.stop() - self.mock_get_policies.stop() - self.mock_get_policy.stop() - self.mock_delete_policy.stop() - self.mock_create_policy.stop() - self.mock_update_policy.stop() + self.get_client.stop() + self.get_policies_patcher.stop() + self.get_policy_patcher.stop() + self.delete_policy_patcher.stop() + self.create_policy_patcher.stop() + self.update_policy_patcher.stop() def test_list_policy(self): list_policy(organization = self._TEST_DEVOPS_ORGANIZATION, From 0e3b8509e169e6f742e7a12eb3c09cbe89fcbe29 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 13:45:55 +0530 Subject: [PATCH 46/48] make policy id mandatory in update command --- azure-devops/azext_devops/dev/repos/policy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index eb4d5869..fa4ef2d5 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -185,9 +185,9 @@ def create_policy(policy_configuration=None, # pylint: disable=too-many-locals -def update_policy(policy_configuration=None, +def update_policy(policy_id, + policy_configuration=None, repository_id=None, branch=None, - policy_id=None, isBlocking=False, isEnabled=False, policy_type=None, minimumApproverCount=None, creatorVoteCounts=None, allowDownvotes=None, resetOnSourcePush=None, @@ -202,7 +202,7 @@ def update_policy(policy_configuration=None, :param branch: Branch on which this policy should be applied :type branch: string :param policy_id: ID of the policy which needs to be updated - :param policy_id: int + :type policy_id: int :param isBlocking: Whether the policy should be blocking or not. Default value is false. Accepted values are true and false. :type isBlocking: bool :param isEnabled: Whether the policy is enabled or not. By default the value is true. Accepted values and true and false. From f8fd2659e9363ebf5dbd7c1746da9adac32ffe57 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 14:48:20 +0530 Subject: [PATCH 47/48] handling negative scenario --- azure-devops/azext_devops/dev/repos/policy.py | 3 +++ azure-devops/azext_devops/test/repos/test_policy.py | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index fa4ef2d5..545d4225 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -289,6 +289,9 @@ def generateConfigurationObject(policy_configuration=None, maximumGitBlobSizeInBytes=None, useUncompressedSize=None, optionalReviewerIds=None, requiredReviewerIds=None, message=None, organization=None): + if policy_configuration is None and policy_type is None: + raise CLIError('Either --policy-configuration or --policy-type must be passed') + if policy_configuration is not None: with open(policy_configuration) as f: import json diff --git a/azure-devops/azext_devops/test/repos/test_policy.py b/azure-devops/azext_devops/test/repos/test_policy.py index d84affdf..bc924e5c 100644 --- a/azure-devops/azext_devops/test/repos/test_policy.py +++ b/azure-devops/azext_devops/test/repos/test_policy.py @@ -91,6 +91,17 @@ def test_create_policy_argument_missing_message(self): self.assertEqual(str(ex), '--minimumApproverCount --creatorVoteCounts --allowDownvotes --resetOnSourcePush are required for ApproverCountPolicy') + def test_create_policy_policy_type_and_configuration_missing(self): + try: + create_policy(organization = self._TEST_DEVOPS_ORGANIZATION, + project = self._TEST_DEVOPS_PROJECT, + detect='off') + self.fail('create should have thrown CLIError') + except CLIError as ex: + #assert + self.assertEqual(str(ex), + 'Either --policy-configuration or --policy-type must be passed') + def test_create_policy_scope(self): create_policy(repository_id=self._TEST_REPOSITORY_ID, branch='master', policy_type='ApproverCountPolicy', From a960e3ffde3719e5e987c378288884f1adc33aa2 Mon Sep 17 00:00:00 2001 From: Gaurav Saral Date: Tue, 29 Jan 2019 14:59:44 +0530 Subject: [PATCH 48/48] style fix --- azure-devops/azext_devops/dev/repos/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-devops/azext_devops/dev/repos/policy.py b/azure-devops/azext_devops/dev/repos/policy.py index 545d4225..59987c4b 100644 --- a/azure-devops/azext_devops/dev/repos/policy.py +++ b/azure-devops/azext_devops/dev/repos/policy.py @@ -291,7 +291,7 @@ def generateConfigurationObject(policy_configuration=None, organization=None): if policy_configuration is None and policy_type is None: raise CLIError('Either --policy-configuration or --policy-type must be passed') - + if policy_configuration is not None: with open(policy_configuration) as f: import json