diff --git a/doc/version_management.md b/doc/version_management.md new file mode 100644 index 00000000000..f300e995c25 --- /dev/null +++ b/doc/version_management.md @@ -0,0 +1,16 @@ +Azure CLI Version Management +============================ + +## History +Azure CLI 2 was used for branding as compared to Azure classic CLI. We intended to keep the MAJOR version fixed at 2 for a while. A regular release only updated the PATCH version, which was a little confusing. + +## Current Norm +Starting from version 2.1.0, Azure CLI updates: +* MAJOR version for core changes that break commands' behavior globally; +* MINOR version for general backward compatible feature changes and service command breaking changes; +* PATCH verison for bug fixes. + +## Backward Compatibility +Considering Azure CLI is a command line tool for Azure Services, we tend to just bump the MINOR version for breaking changes in a service command module. All breaking changes for commands will be marked as **BREAKING CHANGE** in [release notes](https://docs.microsoft.com/cli/azure/release-notes-azure-cli?view=azure-cli-latest). + +At command level, packages only upgrading the PATCH version guarantee backward compatibility. diff --git a/src/azure-cli-core/azure/cli/core/extension/operations.py b/src/azure-cli-core/azure/cli/core/extension/operations.py index f6411d235bf..ca26b00322f 100644 --- a/src/azure-cli-core/azure/cli/core/extension/operations.py +++ b/src/azure-cli-core/azure/cli/core/extension/operations.py @@ -193,6 +193,7 @@ def check_version_compatibility(azext_metadata): min_max_msg_fmt += 'a min of {}.'.format(min_required) elif max_required: min_max_msg_fmt += 'a max of {}.'.format(max_required) + min_max_msg_fmt += '\nPlease install a compatible extension version or remove it.' raise CLIError(min_max_msg_fmt) diff --git a/src/azure-cli/azure/cli/command_modules/botservice/_params.py b/src/azure-cli/azure/cli/command_modules/botservice/_params.py index ec7d3ac3e5f..91c2a7f5273 100644 --- a/src/azure-cli/azure/cli/command_modules/botservice/_params.py +++ b/src/azure-cli/azure/cli/command_modules/botservice/_params.py @@ -69,7 +69,7 @@ def load_arguments(self, _): c.argument('code_dir', options_list=['--code-dir'], help='The directory to upload bot code from.') c.argument('proj_file_path', options_list=['--proj-file-path', c.deprecate(target='--proj-name', redirect='--proj-file-path', - hide=True, expiration='2.1.0')], + hide=True, expiration='3.0.0')], help='Path to the start up project file name. (E.g. "./EchoBotWithCounter.csproj")') c.argument('version', options_list=['-v', '--version'], help='The Microsoft Bot Builder SDK version of the bot.') @@ -110,7 +110,7 @@ def load_arguments(self, _): with self.argument_context('bot prepare-publish') as c: c.argument('proj_file_path', options_list=['--proj-file-path', c.deprecate(target='--proj-name', redirect='--proj-file-path', - hide=True, expiration='2.1.0')], + hide=True, expiration='3.0.0')], help='Path to the start up project file name. (E.g. "./EchoBotWithCounter.csproj") ' 'Required only for C#.') c.argument('sln_name', help='Name of the start up solution file name. Required only for C#.') diff --git a/src/azure-cli/azure/cli/command_modules/eventgrid/_params.py b/src/azure-cli/azure/cli/command_modules/eventgrid/_params.py index 961684ee803..242832e2830 100644 --- a/src/azure-cli/azure/cli/command_modules/eventgrid/_params.py +++ b/src/azure-cli/azure/cli/command_modules/eventgrid/_params.py @@ -63,7 +63,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements c.argument('labels', arg_type=labels_type) c.argument('endpoint_type', arg_type=get_enum_type(['webhook', 'eventhub', 'storagequeue', 'hybridconnection', 'servicebusqueue'], default='webhook')) c.argument('source_resource_id', help="Fully qualified identifier of the source Azure resource.") - c.argument('resource_id', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='2.1.0', hide=True), help="Fully qualified identifier of the Azure resource.") + c.argument('resource_id', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='3.0.0', hide=True), help="Fully qualified identifier of the Azure resource.") c.argument('endpoint', help="Endpoint where EventGrid should deliver events matching this event subscription. For webhook endpoint type, this should be the corresponding webhook URL. For other endpoint types, this should be the Azure resource identifier of the endpoint. It is expected that the destination endpoint to be already created and available for use before executing any Event Grid command.") c.argument('event_subscription_name', help="Name of the event subscription.") c.argument('subject_begins_with', help="An optional string to filter events for an event subscription based on a prefix. Wildcard characters are not supported.") @@ -103,7 +103,7 @@ def load_arguments(self, _): # pylint: disable=too-many-statements c.argument('odata_query', arg_type=odata_query_type, id_part=None) with self.argument_context('eventgrid event-subscription') as c: - c.argument('topic_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='2.1.0', hide=True), help='Name of Event Grid topic.', options_list=['--topic-name'], completer=get_resource_name_completion_list('Microsoft.EventGrid/topics')) + c.argument('topic_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='3.0.0', hide=True), help='Name of Event Grid topic.', options_list=['--topic-name'], completer=get_resource_name_completion_list('Microsoft.EventGrid/topics')) c.argument('event_subscription_name', arg_type=name_type, help='Name of the event subscription.') c.argument('max_delivery_attempts', help="Maximum number of delivery attempts. Must be a number between 1 and 30.") c.argument('event_ttl', help="Event time to live (in minutes). Must be a number between 1 and 1440.") @@ -112,19 +112,19 @@ def load_arguments(self, _): # pylint: disable=too-many-statements c.argument('expiration_date', help="Date or datetime (in UTC, e.g. '2018-11-30T11:59:59+00:00' or '2018-11-30') after which the event subscription would expire. By default, there is no expiration for the event subscription.") with self.argument_context('eventgrid event-subscription create') as c: - c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='2.1.0', hide=True), arg_type=resource_group_name_type) + c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='3.0.0', hide=True), arg_type=resource_group_name_type) with self.argument_context('eventgrid event-subscription delete') as c: - c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='2.1.0', hide=True), arg_type=resource_group_name_type) + c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='3.0.0', hide=True), arg_type=resource_group_name_type) with self.argument_context('eventgrid event-subscription update') as c: - c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='2.1.0', hide=True), arg_type=resource_group_name_type) + c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='3.0.0', hide=True), arg_type=resource_group_name_type) with self.argument_context('eventgrid event-subscription list') as c: c.argument('odata_query', arg_type=odata_query_type, id_part=None) with self.argument_context('eventgrid event-subscription show') as c: - c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='2.1.0', hide=True), arg_type=resource_group_name_type) + c.argument('resource_group_name', deprecate_info=c.deprecate(redirect="--source-resource-id", expiration='3.0.0', hide=True), arg_type=resource_group_name_type) c.argument('include_full_endpoint_url', arg_type=get_three_state_flag(), options_list=['--include-full-endpoint-url'], help="Specify to indicate whether the full endpoint URL should be returned. True if flag present.", ) with self.argument_context('eventgrid topic-type') as c: diff --git a/src/azure-cli/azure/cli/command_modules/monitor/_params.py b/src/azure-cli/azure/cli/command_modules/monitor/_params.py index 09250638211..c48480d3b3b 100644 --- a/src/azure-cli/azure/cli/command_modules/monitor/_params.py +++ b/src/azure-cli/azure/cli/command_modules/monitor/_params.py @@ -263,11 +263,11 @@ def load_arguments(self, _): c.argument('offset', type=get_period_type(as_timedelta=True)) with self.argument_context('monitor activity-log list', arg_group='Filter') as c: - c.argument('filters', deprecate_info=c.deprecate(target='--filters', hide=True, expiration='2.1.0'), help='OData filters. Will ignore other filter arguments.') + c.argument('filters', deprecate_info=c.deprecate(target='--filters', hide=True, expiration='3.0.0'), help='OData filters. Will ignore other filter arguments.') c.argument('correlation_id') c.argument('resource_group', resource_group_name_type) c.argument('resource_id') - c.argument('resource_provider', options_list=['--namespace', c.deprecate(target='--resource-provider', redirect='--namespace', hide=True, expiration='2.1.0')]) + c.argument('resource_provider', options_list=['--namespace', c.deprecate(target='--resource-provider', redirect='--namespace', hide=True, expiration='3.0.0')]) c.argument('caller') c.argument('status') # endregion diff --git a/src/azure-cli/azure/cli/command_modules/network/_params.py b/src/azure-cli/azure/cli/command_modules/network/_params.py index dd577332978..e8e8f25d839 100644 --- a/src/azure-cli/azure/cli/command_modules/network/_params.py +++ b/src/azure-cli/azure/cli/command_modules/network/_params.py @@ -1281,7 +1281,7 @@ def load_arguments(self, _): with self.argument_context('network vnet peering') as c: c.argument('virtual_network_name', virtual_network_name_type) c.argument('virtual_network_peering_name', options_list=['--name', '-n'], help='The name of the VNet peering.', id_part='child_name_1') - c.argument('remote_virtual_network', options_list=['--remote-vnet', c.deprecate(target='--remote-vnet-id', hide=True, expiration='2.1.0')], help='Resource ID or name of the remote VNet.') + c.argument('remote_virtual_network', options_list=['--remote-vnet', c.deprecate(target='--remote-vnet-id', hide=True, expiration='3.0.0')], help='Resource ID or name of the remote VNet.') with self.argument_context('network vnet peering create') as c: c.argument('allow_virtual_network_access', options_list='--allow-vnet-access', action='store_true', help='Allows access from the local VNet to the remote VNet.') @@ -1309,7 +1309,7 @@ def load_arguments(self, _): for scope in ['network vnet subnet list', 'network vnet peering list']: with self.argument_context(scope) as c: - c.argument('ids', deprecate_info=c.deprecate(hide=True, expiration='2.1.0')) + c.argument('ids', deprecate_info=c.deprecate(hide=True, expiration='3.0.0')) c.argument('virtual_network_name', id_part=None) # endregion diff --git a/src/azure-cli/azure/cli/command_modules/vm/_params.py b/src/azure-cli/azure/cli/command_modules/vm/_params.py index 74289204e86..3e60e02dbfe 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/_params.py +++ b/src/azure-cli/azure/cli/command_modules/vm/_params.py @@ -364,7 +364,7 @@ def load_arguments(self, _): with self.argument_context('vm extension') as c: c.argument('vm_extension_name', name_arg_type, completer=get_resource_name_completion_list('Microsoft.Compute/virtualMachines/extensions'), help='Name of the extension.', id_part='child_name_1') c.argument('vm_name', arg_type=existing_vm_name, options_list=['--vm-name'], id_part='name') - c.argument('expand', deprecate_info=c.deprecate(expiration='2.1.0', hide=True)) + c.argument('expand', deprecate_info=c.deprecate(expiration='3.0.0', hide=True)) with self.argument_context('vm extension list') as c: c.argument('vm_name', arg_type=existing_vm_name, options_list=['--vm-name'], id_part=None) @@ -806,7 +806,7 @@ def load_arguments(self, _): c.ignore('gallery_image') with self.argument_context('sig image-version') as c: - deprecated_option = c.deprecate(target='--gallery-image-version-name', redirect='--gallery-image-version', hide=True, expiration="2.1.0") + deprecated_option = c.deprecate(target='--gallery-image-version-name', redirect='--gallery-image-version', hide=True, expiration="3.0.0") c.argument('gallery_image_version_name', options_list=['--gallery-image-version', '-e', deprecated_option], ) with self.argument_context('sig image-version create') as c: