Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package azure-cli-core for openSUSE:Factory checked in at 2024-09-06 17:18:55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old) and /work/SRC/openSUSE:Factory/.azure-cli-core.new.10096 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "azure-cli-core" Fri Sep 6 17:18:55 2024 rev:72 rq:1199120 version:2.64.0 Changes: -------- --- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes 2024-08-08 10:59:12.121602364 +0200 +++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.10096/azure-cli-core.changes 2024-09-06 17:19:19.905852801 +0200 @@ -1,0 +2,8 @@ +Wed Sep 4 08:01:18 UTC 2024 - John Paul Adrian Glaubitz <adrian.glaub...@suse.com> + +- New upstream release + + Version 2.64.0 + + For detailed information about changes see the + HISTORY.rst file provided with this package + +------------------------------------------------------------------- Old: ---- azure_cli_core-2.63.0.tar.gz New: ---- azure_cli_core-2.64.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ azure-cli-core.spec ++++++ --- /var/tmp/diff_new_pack.EHh0BB/_old 2024-09-06 17:19:20.365871917 +0200 +++ /var/tmp/diff_new_pack.EHh0BB/_new 2024-09-06 17:19:20.365871917 +0200 @@ -24,7 +24,7 @@ %global _sitelibdir %{%{pythons}_sitelib} Name: azure-cli-core -Version: 2.63.0 +Version: 2.64.0 Release: 0 Summary: Microsoft Azure CLI Core Module License: MIT ++++++ azure_cli_core-2.63.0.tar.gz -> azure_cli_core-2.64.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/HISTORY.rst new/azure_cli_core-2.64.0/HISTORY.rst --- old/azure_cli_core-2.63.0/HISTORY.rst 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/HISTORY.rst 2024-08-28 07:44:58.000000000 +0200 @@ -3,6 +3,10 @@ Release History =============== +2.64.0 +++++++ +* Minor fixes + 2.63.0 ++++++ * Resolve CVE-2024-39689 (#29320) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/PKG-INFO new/azure_cli_core-2.64.0/PKG-INFO --- old/azure_cli_core-2.63.0/PKG-INFO 2024-07-31 05:39:33.541276200 +0200 +++ new/azure_cli_core-2.64.0/PKG-INFO 2024-08-28 07:45:13.234815400 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: azure-cli-core -Version: 2.63.0 +Version: 2.64.0 Summary: Microsoft Azure Command-Line Tools Core Module Home-page: https://github.com/Azure/azure-cli Author: Microsoft Corporation @@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: License :: OSI Approved :: MIT License Requires-Python: >=3.8.0 License-File: LICENSE.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/__init__.py new/azure_cli_core-2.64.0/azure/cli/core/__init__.py --- old/azure_cli_core-2.63.0/azure/cli/core/__init__.py 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure/cli/core/__init__.py 2024-08-28 07:44:58.000000000 +0200 @@ -4,7 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=line-too-long -__version__ = "2.63.0" +__version__ = "2.64.0" import os import sys @@ -58,6 +58,7 @@ def __init__(self, **kwargs): super(AzCli, self).__init__(**kwargs) + from azure.cli.core.breaking_change import register_upcoming_breaking_change_info from azure.cli.core.commands import register_cache_arguments from azure.cli.core.commands.arm import ( register_ids_argument, register_global_subscription_argument) @@ -90,6 +91,7 @@ register_global_subscription_argument(self) register_ids_argument(self) # global subscription must be registered first! register_cache_arguments(self) + register_upcoming_breaking_change_info(self) self.progress_controller = None @@ -218,6 +220,7 @@ _load_module_command_loader, _load_extension_command_loader, BLOCKED_MODS, ExtensionCommandSource) from azure.cli.core.extension import ( get_extensions, get_extension_path, get_extension_modname) + from azure.cli.core.breaking_change import (import_module_breaking_changes, import_extension_breaking_changes) def _update_command_table_from_modules(args, command_modules=None): """Loads command tables from modules and merge into the main command table. @@ -254,6 +257,7 @@ try: start_time = timeit.default_timer() module_command_table, module_group_table = _load_module_command_loader(self, args, mod) + import_module_breaking_changes(mod) for cmd in module_command_table.values(): cmd.command_source = mod self.command_table.update(module_command_table) @@ -349,6 +353,7 @@ start_time = timeit.default_timer() extension_command_table, extension_group_table = \ _load_extension_command_loader(self, args, ext_mod) + import_extension_breaking_changes(ext_mod) for cmd_name, cmd in extension_command_table.items(): cmd.command_source = ExtensionCommandSource( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/_help.py new/azure_cli_core-2.64.0/azure/cli/core/_help.py --- old/azure_cli_core-2.63.0/azure/cli/core/_help.py 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure/cli/core/_help.py 2024-08-28 07:44:58.000000000 +0200 @@ -250,6 +250,76 @@ super(CliHelpFile, self).__init__(help_ctx, delimiters) self.links = [] + from knack.deprecation import resolve_deprecate_info, ImplicitDeprecated, Deprecated + from azure.cli.core.breaking_change import UpcomingBreakingChangeTag, MergedStatusTag + direct_deprecate_info = None + breaking_changes = [] + deprecate_info = resolve_deprecate_info(help_ctx.cli_ctx, delimiters) + if isinstance(deprecate_info, Deprecated): + direct_deprecate_info = deprecate_info + elif isinstance(deprecate_info, UpcomingBreakingChangeTag): + breaking_changes.append(deprecate_info) + # If there are more than two `deprecate_info` and/or upcoming breaking changes, + # extract them and store separately from the merged status tag. + elif isinstance(deprecate_info, MergedStatusTag): + depr, bcs = CliHelpFile.classify_merged_status_tag(deprecate_info) + direct_deprecate_info = depr[0] if depr else None + breaking_changes.extend(bcs) + + # search for implicit deprecation + path_comps = delimiters.split()[:-1] + implicit_deprecate_info = None + while path_comps: + deprecate_info = resolve_deprecate_info(help_ctx.cli_ctx, ' '.join(path_comps)) + if isinstance(deprecate_info, Deprecated) and implicit_deprecate_info is None: + implicit_deprecate_info = deprecate_info + elif isinstance(deprecate_info, UpcomingBreakingChangeTag): + breaking_changes.append(deprecate_info) + # If there are more than two `deprecate_info` and/or upcoming breaking changes, + # extract them and store separately from the merged status tag. + elif isinstance(deprecate_info, MergedStatusTag): + depr, bcs = CliHelpFile.classify_merged_status_tag(deprecate_info) + if depr and implicit_deprecate_info is None: + implicit_deprecate_info = depr[0] + breaking_changes.extend(bcs) + del path_comps[-1] + + if implicit_deprecate_info: + deprecate_kwargs = implicit_deprecate_info.__dict__.copy() + deprecate_kwargs['object_type'] = 'command' if delimiters in \ + help_ctx.cli_ctx.invocation.commands_loader.command_table else 'command group' + del deprecate_kwargs['_get_tag'] + del deprecate_kwargs['_get_message'] + self.deprecate_info = ImplicitDeprecated(cli_ctx=help_ctx.cli_ctx, **deprecate_kwargs) + else: + self.deprecate_info = direct_deprecate_info + + all_deprecate_info = [self.deprecate_info] if self.deprecate_info else [] + all_deprecate_info.extend(breaking_changes) + if len(all_deprecate_info) > 1: + # Merge multiple `deprecate_info` and/or breaking changes so their messages can be displayed together. + self.deprecate_info = MergedStatusTag(help_ctx.cli_ctx, *all_deprecate_info) + elif all_deprecate_info: + self.deprecate_info = all_deprecate_info[0] + + @staticmethod + def classify_merged_status_tag(merged_status_tag): + from knack.deprecation import Deprecated + from azure.cli.core.breaking_change import UpcomingBreakingChangeTag, MergedStatusTag + + deprecate_info = [] + breaking_changes = [] + for tag in merged_status_tag.tags: + if isinstance(tag, Deprecated): + deprecate_info.append(tag) + elif isinstance(tag, UpcomingBreakingChangeTag): + breaking_changes.append(tag) + elif isinstance(tag, MergedStatusTag): + depr, bcs = CliHelpFile.classify_merged_status_tag(tag) + deprecate_info.extend(depr) + breaking_changes.extend(bcs) + return deprecate_info, breaking_changes + def _should_include_example(self, ex): supported_profiles = ex.get('supported-profiles') unsupported_profiles = ex.get('unsupported-profiles') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/breaking_change.py new/azure_cli_core-2.64.0/azure/cli/core/breaking_change.py --- old/azure_cli_core-2.63.0/azure/cli/core/breaking_change.py 1970-01-01 01:00:00.000000000 +0100 +++ new/azure_cli_core-2.64.0/azure/cli/core/breaking_change.py 2024-08-28 07:44:58.000000000 +0200 @@ -0,0 +1,611 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- +import abc +import argparse +from collections import defaultdict + +from knack.log import get_logger +from knack.deprecation import Deprecated +from knack.util import StatusTag, color_map + + +logger = get_logger() + +NEXT_BREAKING_CHANGE_RELEASE = '2.67.0' +DEFAULT_BREAKING_CHANGE_TAG = '[Breaking Change]' + + +def _get_action_class(cli_ctx, action): + action_class = argparse.Action + + # action is either a user-defined Action class or a string referring a library-defined Action + if isinstance(action, type) and issubclass(action, argparse.Action): + action_class = action + elif isinstance(action, str): + action_class = cli_ctx.invocation.command_loader.cli_ctx.invocation.parser._registries['action'][action] # pylint: disable=protected-access + return action_class + + +def _argument_breaking_change_action(cli_ctx, status_tag, action): + + action_class = _get_action_class(cli_ctx, action) + + class ArgumentBreakingChangeAction(action_class): + + def __call__(self, parser, namespace, values, option_string=None): + if not hasattr(namespace, '_argument_deprecations'): + setattr(namespace, '_argument_deprecations', []) + if isinstance(status_tag, MergedStatusTag): + for tag in status_tag.tags: + namespace._argument_deprecations.append(tag) + else: + if status_tag not in namespace._argument_deprecations: + namespace._argument_deprecations.append(status_tag) + try: + super().__call__(parser, namespace, values, option_string) + except NotImplementedError: + setattr(namespace, self.dest, values) + + return ArgumentBreakingChangeAction + + +def _find_arg(arg_name, arguments): + if arg_name in arguments: + return arg_name, arguments[arg_name] + for key, argument in arguments.items(): + for option in argument.options_list or []: + if arg_name == option or (isinstance(option, Deprecated) and arg_name == option.target): + return key, argument + trimmed_arg_name = arg_name.strip('-').replace('-', '_') + if trimmed_arg_name in arguments: + return trimmed_arg_name, arguments[trimmed_arg_name] + return None, None + + +class UpcomingBreakingChangeTag(StatusTag): + def __init__(self, cli_ctx, object_type='', target=None, target_version=None, tag_func=None, message_func=None, + always_display=False): + def _default_get_message(bc): + msg = f"A breaking change may occur to this {bc.object_type} " + if isinstance(target_version, TargetVersion): + msg += str(target_version) + '.' + elif isinstance(target_version, str): + msg += 'in ' + target_version + '.' + else: + msg += 'in future release.' + return msg + + self.always_display = always_display + self.target_version = target_version + super().__init__( + cli_ctx=cli_ctx, + object_type=object_type, + target=target, + color=color_map['deprecation'], + tag_func=tag_func or (lambda _: DEFAULT_BREAKING_CHANGE_TAG), + message_func=message_func or _default_get_message + ) + + def expired(self): + return False + + +class MergedStatusTag(StatusTag): + # This class is used to merge multiple status tags into one. + # It is particularly useful when multiple breaking changes and deprecation information need to be recorded + # in a single deprecate_info field. + + def __init__(self, cli_ctx, *tags): + assert len(tags) > 0 + tag = tags[0] + self.tags = list(tags) + + def _get_merged_tag(self): + return ''.join({tag._get_tag(self) for tag in self.tags}) # pylint: disable=protected-access + + def _get_merged_msg(self): + return '\n'.join({tag._get_message(self) for tag in self.tags}) # pylint: disable=protected-access + + super().__init__(cli_ctx, tag.object_type, tag.target, tag_func=_get_merged_tag, + message_func=_get_merged_msg, color=tag._color) + + def merge(self, other): + self.tags.append(other) + + def hidden(self): + return any(tag.hidden() for tag in self.tags) + + def show_in_help(self): + return any(tag.show_in_help() for tag in self.tags) + + def expired(self): + return any(tag.expired() for tag in self.tags) + + @property + def tag(self): + return ''.join({str(tag.tag) for tag in self.tags}) + + @property + def message(self): + return '\n'.join({str(tag.message) for tag in self.tags}) + + +def _next_breaking_change_version(): + return NEXT_BREAKING_CHANGE_RELEASE + + +# pylint: disable=too-few-public-methods +class TargetVersion(abc.ABC): + @abc.abstractmethod + def __str__(self): + raise NotImplementedError() + + @abc.abstractmethod + def version(self): + raise NotImplementedError() + + +# pylint: disable=too-few-public-methods +class NextBreakingChangeWindow(TargetVersion): + def __str__(self): + next_breaking_change_version = _next_breaking_change_version() + if next_breaking_change_version: + return f'in next breaking change release({next_breaking_change_version})' + return 'in next breaking change release' + + def version(self): + return _next_breaking_change_version() + + +# pylint: disable=too-few-public-methods +class ExactVersion(TargetVersion): + def __init__(self, version): + self._version = version + + def __str__(self): + return f'in {self._version}' + + def version(self): + return self._version + + +# pylint: disable=too-few-public-methods +class UnspecificVersion(TargetVersion): + def __str__(self): + return 'in a future release' + + def version(self): + return None + + +class BreakingChange(abc.ABC): + def __init__(self, cmd, arg=None, target=None, target_version=None): + self.cmd = cmd + if isinstance(arg, str): + self.args = [arg] + elif isinstance(arg, list): + self.args = arg + else: + self.args = [] + self.target = target if target else '/'.join(self.args) if self.args else self.cmd + if isinstance(target_version, TargetVersion): + self._target_version = target_version + elif isinstance(target_version, str): + self._target_version = ExactVersion(target_version) + else: + self._target_version = UnspecificVersion() + + @property + def message(self): + return '' + + @property + def target_version(self): + return self._target_version + + @staticmethod + def format_doc_link(doc_link): + return f' To know more about the Breaking Change, please visit {doc_link}.' if doc_link else '' + + @property + def command_name(self): + if self.cmd.startswith('az '): + return self.cmd[3:].strip() + return self.cmd + + def is_command_group(self, cli_ctx): + return self.command_name in cli_ctx.invocation.commands_loader.command_group_table + + def to_tag(self, cli_ctx, **kwargs): + if self.args: + object_type = 'argument' + elif self.is_command_group(cli_ctx): + object_type = 'command group' + else: + object_type = 'command' + tag_kwargs = { + 'object_type': object_type, + 'target': self.target, + 'target_version': self.target_version, + 'message_func': lambda _: self.message, + } + tag_kwargs.update(kwargs) + return UpcomingBreakingChangeTag(cli_ctx, **tag_kwargs) + + def register(self, cli_ctx): + if self.args: + command = cli_ctx.invocation.commands_loader.command_table.get(self.command_name) + if not command: + return + for arg_name in self.args: + if self._register_option_deprecate(cli_ctx, command.arguments, arg_name): + continue + arg_name, arg = _find_arg(arg_name, command.arguments) + if not arg: + continue + arg.deprecate_info = self.appended_status_tag(cli_ctx, arg.deprecate_info, self.to_tag(cli_ctx)) + arg.action = _argument_breaking_change_action(cli_ctx, arg.deprecate_info, arg.options['action']) + elif self.is_command_group(cli_ctx): + command_group = cli_ctx.invocation.commands_loader.command_group_table[self.command_name] + if not command_group: + self._register_to_direct_sub_cg_or_command(cli_ctx, self.command_name, self.to_tag(cli_ctx)) + else: + command_group.group_kwargs['deprecate_info'] = \ + self.appended_status_tag(cli_ctx, command_group.group_kwargs.get('deprecate_info'), + self.to_tag(cli_ctx)) + else: + command = cli_ctx.invocation.commands_loader.command_table.get(self.cmd) + if not command: + return + command.deprecate_info = self.appended_status_tag(cli_ctx, command.deprecate_info, self.to_tag(cli_ctx)) + + @staticmethod + def appended_status_tag(cli_ctx, old_status_tag, new_status_tag): + if isinstance(old_status_tag, (Deprecated, UpcomingBreakingChangeTag)): + return MergedStatusTag(cli_ctx, old_status_tag, new_status_tag) + if isinstance(old_status_tag, MergedStatusTag): + old_status_tag.merge(new_status_tag) + return old_status_tag + return new_status_tag + + def _register_to_direct_sub_cg_or_command(self, cli_ctx, cg_name, status_tag): + for key, command_group in cli_ctx.invocation.commands_loader.command_group_table.items(): + split_key = key.rsplit(maxsplit=1) + # If inpass command group name is empty, all first level command group should be registered to. + # Otherwise, we need to find all direct sub command groups. + if (not cg_name and len(split_key) == 1) or (len(split_key) == 2 and split_key[0] == cg_name): + from azure.cli.core.commands import AzCommandGroup + if isinstance(command_group, AzCommandGroup): + command_group.group_kwargs['deprecate_info'] = \ + self.appended_status_tag(cli_ctx, command_group.group_kwargs.get('deprecate_info'), status_tag) + else: + self._register_to_direct_sub_cg_or_command(cli_ctx, key, status_tag) + for key, command in cli_ctx.invocation.commands_loader.command_table.items(): + # If inpass command group name is empty, all first level command should be registered to. + if (not cg_name and ' ' not in key) or key.rsplit(maxsplit=1)[0] == cg_name: + command.deprecate_info = self.appended_status_tag(cli_ctx, command.deprecate_info, self.to_tag(cli_ctx)) + + def _register_option_deprecate(self, cli_ctx, arguments, option_name): + for _, argument in arguments.items(): + if argument.options_list and len(argument.options_list) > 1: + for idx, option in enumerate(argument.options_list): + if isinstance(option, str) and option_name == option and isinstance(self, AzCLIDeprecate): + if isinstance(argument.options_list, tuple): + # Some of the command would declare options_list as tuple + argument.options_list = list(argument.options_list) + argument.options_list[idx] = self.to_tag(cli_ctx, object_type='option') + argument.options_list[idx].target = option + argument.action = _argument_breaking_change_action(cli_ctx, argument.options_list[idx], + argument.options['action']) + return True + return False + + +class AzCLIDeprecate(BreakingChange): + def __init__(self, cmd, arg=None, target_version=NextBreakingChangeWindow(), **kwargs): + super().__init__(cmd, arg, None, target_version) + self.kwargs = kwargs + + @staticmethod + def _build_message(object_type, target, target_version, redirect): + if object_type in ['argument', 'option']: + msg = "{} '{}' has been deprecated and will be removed ".format(object_type, target).capitalize() + elif object_type: + msg = "This {} has been deprecated and will be removed ".format(object_type) + else: + msg = "'{}' has been deprecated and will be removed ".format(target) + msg += str(target_version) + '.' + if redirect: + msg += " Use '{}' instead.".format(redirect) + return msg + + @property + def message(self): + return self._build_message(self.kwargs.get('object_type'), self.target, self.target_version, + self.kwargs.get('redirect')) + + def to_tag(self, cli_ctx, **kwargs): + if self.args: + object_type = 'argument' + elif self.is_command_group(cli_ctx): + object_type = 'command group' + else: + object_type = 'command' + tag_kwargs = { + 'object_type': object_type, + 'message_func': lambda depr: self._build_message( + depr.object_type, depr.target, self.target_version, depr.redirect), + 'target': self.target + } + tag_kwargs.update(self.kwargs) + tag_kwargs.update(kwargs) + return Deprecated(cli_ctx, **tag_kwargs) + + +class AzCLIRemoveChange(BreakingChange): + """ + Remove the command groups, commands or arguments in a future release. + + **It is recommended to utilize `deprecate_info` instead of this class to pre-announce Breaking Change of Removal.** + :param target: name of the removed command group, command or argument + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + :param redirect: alternative way to replace the old behavior + :param doc_link: link of the related document + """ + + def __init__(self, cmd, arg=None, target_version=NextBreakingChangeWindow(), target=None, redirect=None, + doc_link=None): + super().__init__(cmd, arg, target, target_version) + self.alter = redirect + self.doc_link = doc_link + + @property + def message(self): + alter = f" Please use '{self.alter}' instead." if self.alter else '' + doc = self.format_doc_link(self.doc_link) + return f"'{self.target}' will be removed {str(self._target_version)}.{alter}{doc}" + + +class AzCLIRenameChange(BreakingChange): + """ + Rename the command groups, commands or arguments to a new name in a future release. + + **It is recommended to utilize `deprecate_info` instead of this class to pre-announce Breaking Change of Renaming.** + It is recommended that the old name and the new name should be reserved in few releases. + :param target: name of the renamed command group, command or argument + :param new_name: new name + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + :param doc_link: link of the related document + """ + + def __init__(self, cmd, new_name, arg=None, target=None, target_version=NextBreakingChangeWindow(), doc_link=None): + super().__init__(cmd, arg, target, target_version) + self.new_name = new_name + self.doc_link = doc_link + + @property + def message(self): + doc = self.format_doc_link(self.doc_link) + return f"'{self.target}' will be renamed to '{self.new_name}' {str(self._target_version)}.{doc}" + + +class AzCLIOutputChange(BreakingChange): + """ + The output of the command will be changed in a future release. + :param description: describe the changes in output + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + :param guide: how to adapt to the change + :param doc_link: link of the related document + """ + + def __init__(self, cmd, description: str, target_version=NextBreakingChangeWindow(), guide=None, doc_link=None): + super().__init__(cmd, None, None, target_version) + self.desc = description + self.guide = guide + self.doc_link = doc_link + + @property + def message(self): + desc = self.desc.rstrip() + if desc and desc[-1] not in ',.;?!': + desc = desc + '.' + if self.guide: + guide = self.guide.rstrip() + if guide and guide[-1] not in ',.;?!': + guide = ' ' + guide + '.' + else: + guide = '' + doc = self.format_doc_link(self.doc_link) + return f'The output will be changed {str(self.target_version)}. {desc}{guide}{doc}' + + +class AzCLILogicChange(BreakingChange): + """ + There would be a breaking change in the logic of the command in future release. + :param summary: a short summary about the breaking change + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + :param detail: detailed information + :param doc_link: link of the related document + """ + + def __init__(self, cmd, summary, target_version=NextBreakingChangeWindow(), detail=None, doc_link=None): + super().__init__(cmd, None, None, target_version) + self.summary = summary + self.detail = detail + self.doc_link = doc_link + + @property + def message(self): + detail = f' {self.detail}' if self.detail else '' + return f'{self.summary} {str(self.target_version)}.{detail}{self.format_doc_link(self.doc_link)}' + + +class AzCLIDefaultChange(BreakingChange): + """ + The default value of an argument would be changed in a future release. + :param target: name of the related argument + :param current_default: current default value of the argument + :param new_default: new default value of the argument + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + :param doc_link: link of the related document + """ + + def __init__(self, cmd, arg, current_default, new_default, target_version=NextBreakingChangeWindow(), + target=None, doc_link=None): + super().__init__(cmd, arg, target, target_version) + self.current_default = current_default + self.new_default = new_default + self.doc_link = doc_link + + @property + def message(self): + doc = self.format_doc_link(self.doc_link) + return (f"The default value of '{self.target}' will be changed to '{self.new_default}' from " + f"'{self.current_default}' {str(self._target_version)}.{doc}") + + def to_tag(self, cli_ctx, **kwargs): + if 'always_display' not in kwargs: + kwargs['always_display'] = True + return super().to_tag(cli_ctx, **kwargs) + + +class AzCLIBeRequired(BreakingChange): + """ + The argument would become required in a future release. + :param target: name of the related argument + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + :param doc_link: link of the related document + """ + + def __init__(self, cmd, arg, target_version=NextBreakingChangeWindow(), target=None, doc_link=None): + super().__init__(cmd, arg, target, target_version) + self.doc_link = doc_link + + @property + def message(self): + doc = self.format_doc_link(self.doc_link) + return f"The argument '{self.target}' will become required {str(self._target_version)}.{doc}" + + def to_tag(self, cli_ctx, **kwargs): + if 'always_display' not in kwargs: + kwargs['always_display'] = True + return super().to_tag(cli_ctx, **kwargs) + + +class AzCLIOtherChange(BreakingChange): + """ + Other custom breaking changes. + :param message: A description of the breaking change, including the version number where it is expected to occur. + :param target_version: version where the breaking change is expected to happen. + :type target_version: TargetVersion + """ + + def __init__(self, cmd, message, arg=None, target_version=NextBreakingChangeWindow()): + super().__init__(cmd, arg, None, target_version) + self._message = message + + @property + def message(self): + return self._message + + +upcoming_breaking_changes = defaultdict(lambda: []) + + +def import_module_breaking_changes(mod): + try: + from importlib import import_module + import_module('azure.cli.command_modules.' + mod + '._breaking_change') + except ImportError: + pass + + +def import_extension_breaking_changes(ext_mod): + try: + from importlib import import_module + import_module(ext_mod + '._breaking_change') + except ImportError: + pass + + +def register_upcoming_breaking_change_info(cli_ctx): + from knack import events + + def update_breaking_change_info(cli_ctx, **kwargs): # pylint: disable=unused-argument + for key, breaking_changes in upcoming_breaking_changes.items(): + # Conditional Breaking Changes are announced with key `CommandName.Tag`. They should not be registered. + if '.' in key: + continue + for breaking_change in breaking_changes: + breaking_change.register(cli_ctx) + + cli_ctx.register_event(events.EVENT_INVOKER_POST_CMD_TBL_CREATE, update_breaking_change_info) + + +def register_deprecate_info(command_name, arg=None, target_version=NextBreakingChangeWindow(), **kwargs): + upcoming_breaking_changes[command_name].append(AzCLIDeprecate(command_name, arg, target_version, **kwargs)) + + +def register_output_breaking_change(command_name, description, target_version=NextBreakingChangeWindow(), guide=None, + doc_link=None): + upcoming_breaking_changes[command_name].append( + AzCLIOutputChange(command_name, description, target_version, guide, doc_link)) + + +def register_logic_breaking_change(command_name, summary, target_version=NextBreakingChangeWindow(), detail=None, + doc_link=None): + upcoming_breaking_changes[command_name].append( + AzCLILogicChange(command_name, summary, target_version, detail, doc_link)) + + +def register_default_value_breaking_change(command_name, arg, current_default, new_default, + target_version=NextBreakingChangeWindow(), target=None, doc_link=None): + upcoming_breaking_changes[command_name].append( + AzCLIDefaultChange(command_name, arg, current_default, new_default, target_version, target, doc_link)) + + +def register_required_flag_breaking_change(command_name, arg, target_version=NextBreakingChangeWindow(), target=None, + doc_link=None): + upcoming_breaking_changes[command_name].append(AzCLIBeRequired(command_name, arg, target_version, target, doc_link)) + + +def register_other_breaking_change(command_name, message, arg=None, target_version=NextBreakingChangeWindow()): + upcoming_breaking_changes[command_name].append(AzCLIOtherChange(command_name, message, arg, target_version)) + + +def register_command_group_deprecate(command_group, redirect=None, hide=None, + target_version=NextBreakingChangeWindow(), **kwargs): + register_deprecate_info(command_group, redirect=redirect, hide=hide, target_version=target_version, **kwargs) + + +def register_command_deprecate(command, redirect=None, hide=None, + target_version=NextBreakingChangeWindow(), **kwargs): + register_deprecate_info(command, redirect=redirect, hide=hide, target_version=target_version, **kwargs) + + +def register_argument_deprecate(command, argument, redirect=None, hide=None, + target_version=NextBreakingChangeWindow(), **kwargs): + register_deprecate_info(command, argument, redirect=redirect, hide=hide, target_version=target_version, **kwargs) + + +def register_conditional_breaking_change(tag, breaking_change): + upcoming_breaking_changes[breaking_change.command_name + '.' + tag].append(breaking_change) + + +def print_conditional_breaking_change(cli_ctx, tag, custom_logger=None): + command = cli_ctx.invocation.commands_loader.command_name + custom_logger = custom_logger or logger + + command_comps = command.split() + while command_comps: + for breaking_change in upcoming_breaking_changes.get(' '.join(command_comps) + '.' + tag, []): + custom_logger.warning(breaking_change.message) + del command_comps[-1] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/commands/__init__.py new/azure_cli_core-2.64.0/azure/cli/core/commands/__init__.py --- old/azure_cli_core-2.63.0/azure/cli/core/commands/__init__.py 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure/cli/core/commands/__init__.py 2024-08-28 07:44:58.000000000 +0200 @@ -14,8 +14,10 @@ import sys import time import copy +from collections import OrderedDict from importlib import import_module +from azure.cli.core.breaking_change import UpcomingBreakingChangeTag, MergedStatusTag # pylint: disable=unused-import from azure.cli.core.commands.constants import ( BLOCKED_MODS, DEFAULT_QUERY_TIME_RANGE, CLI_COMMON_KWARGS, CLI_COMMAND_KWARGS, CLI_PARAM_KWARGS, @@ -31,7 +33,7 @@ from knack.arguments import CLICommandArgument from knack.commands import CLICommand, CommandGroup, PREVIEW_EXPERIMENTAL_CONFLICT_ERROR -from knack.deprecation import ImplicitDeprecated, resolve_deprecate_info +from knack.deprecation import ImplicitDeprecated, resolve_deprecate_info, Deprecated from knack.invocation import CommandInvoker from knack.preview import ImplicitPreviewItem, PreviewItem, resolve_preview_info from knack.experimental import ImplicitExperimentalItem, ExperimentalItem, resolve_experimental_info @@ -751,15 +753,36 @@ self._resolve_extension_override_warning(cmd) def _resolve_preview_and_deprecation_warnings(self, cmd, parsed_args): - deprecations = [] + getattr(parsed_args, '_argument_deprecations', []) + deprecations = getattr(parsed_args, '_argument_deprecations', []) + # Handle `always_display` argument breaking changes + for _, argument in parsed_args.func.arguments.items(): + # Some arguments have breaking changes that must always be displayed. + # Iterate through them and show the warnings. + if isinstance(argument.deprecate_info, UpcomingBreakingChangeTag): + if argument.deprecate_info.always_display: + deprecations.append(argument.deprecate_info) + elif isinstance(argument.deprecate_info, MergedStatusTag): + for deprecation in argument.deprecate_info.tags: + if isinstance(deprecation, UpcomingBreakingChangeTag) and deprecation.always_display: + deprecations.append(deprecation) + # Dedup the deprecations + # If an argument has multiple breaking changes or deprecations, + # duplicated deprecations would be produced due to the inherent logic of action + deprecations = list(OrderedDict.fromkeys(deprecations)) if cmd.deprecate_info: deprecations.append(cmd.deprecate_info) # search for implicit deprecation path_comps = cmd.name.split()[:-1] implicit_deprecate_info = None - while path_comps and not implicit_deprecate_info: - implicit_deprecate_info = resolve_deprecate_info(self.cli_ctx, ' '.join(path_comps)) + while path_comps: + deprecate_info = resolve_deprecate_info(self.cli_ctx, ' '.join(path_comps)) + if isinstance(deprecate_info, Deprecated) and implicit_deprecate_info is None: + implicit_deprecate_info = deprecate_info + elif isinstance(deprecate_info, UpcomingBreakingChangeTag): + deprecations.append(deprecate_info) + elif isinstance(deprecate_info, MergedStatusTag): + deprecations.extend(deprecate_info.tags) del path_comps[-1] if implicit_deprecate_info: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/profiles/_shared.py new/azure_cli_core-2.64.0/azure/cli/core/profiles/_shared.py --- old/azure_cli_core-2.63.0/azure/cli/core/profiles/_shared.py 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure/cli/core/profiles/_shared.py 2024-08-28 07:44:58.000000000 +0200 @@ -156,7 +156,7 @@ 'latest': { ResourceType.MGMT_STORAGE: '2023-05-01', ResourceType.MGMT_NETWORK: '2022-01-01', - ResourceType.MGMT_COMPUTE: SDKProfile('2024-03-01', { + ResourceType.MGMT_COMPUTE: SDKProfile('2024-07-01', { 'resource_skus': '2019-04-01', 'disks': '2023-04-02', 'disk_encryption_sets': '2022-03-02', @@ -168,7 +168,7 @@ 'gallery_applications': '2021-07-01', 'gallery_application_versions': '2022-01-03', 'shared_galleries': '2022-01-03', - 'virtual_machine_scale_sets': '2024-03-01', + 'virtual_machine_scale_sets': '2024-07-01', }), ResourceType.MGMT_RESOURCE_FEATURES: '2021-07-01', ResourceType.MGMT_RESOURCE_LINKS: '2016-09-01', @@ -215,7 +215,7 @@ ResourceType.DATA_STORAGE: '2018-11-09', ResourceType.DATA_STORAGE_BLOB: '2022-11-02', ResourceType.DATA_STORAGE_FILEDATALAKE: '2021-08-06', - ResourceType.DATA_STORAGE_FILESHARE: '2022-11-02', + ResourceType.DATA_STORAGE_FILESHARE: '2024-08-04', ResourceType.DATA_STORAGE_QUEUE: '2018-03-28', ResourceType.DATA_COSMOS_TABLE: '2017-04-17', ResourceType.MGMT_SERVICEBUS: '2022-10-01-preview', @@ -258,7 +258,7 @@ ResourceType.MGMT_IOTHUB: '2023-06-30-preview', ResourceType.MGMT_IOTDPS: '2021-10-15', ResourceType.MGMT_IOTCENTRAL: '2021-11-01-preview', - ResourceType.MGMT_ARO: '2023-09-04', + ResourceType.MGMT_ARO: '2023-11-22', ResourceType.MGMT_DATABOXEDGE: '2021-02-01-preview', ResourceType.MGMT_CUSTOMLOCATION: '2021-03-15-preview', ResourceType.MGMT_CONTAINERSERVICE: SDKProfile('2024-05-01'), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure/cli/core/util.py new/azure_cli_core-2.64.0/azure/cli/core/util.py --- old/azure_cli_core-2.63.0/azure/cli/core/util.py 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure/cli/core/util.py 2024-08-28 07:44:58.000000000 +0200 @@ -1356,3 +1356,17 @@ encrypt = cli_ctx.config.getboolean('core', 'encrypt_token_cache', fallback=fallback) return encrypt + + +def run_cmd(args, *, capture_output=False, timeout=None, check=False, encoding=None, env=None): + """Run command in a subprocess. It reduces (not eliminates) shell injection by forcing args to be a list + and shell=False. Other arguments are keyword-only. For their documentation, see + https://docs.python.org/3/library/subprocess.html#subprocess.run + """ + if not isinstance(args, list): + from azure.cli.core.azclierror import ArgumentUsageError + raise ArgumentUsageError("Invalid args. run_cmd args must be a list") + + import subprocess + return subprocess.run(args, capture_output=capture_output, timeout=timeout, check=check, + encoding=encoding, env=env) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure_cli_core.egg-info/PKG-INFO new/azure_cli_core-2.64.0/azure_cli_core.egg-info/PKG-INFO --- old/azure_cli_core-2.63.0/azure_cli_core.egg-info/PKG-INFO 2024-07-31 05:39:33.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure_cli_core.egg-info/PKG-INFO 2024-08-28 07:45:13.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: azure-cli-core -Version: 2.63.0 +Version: 2.64.0 Summary: Microsoft Azure Command-Line Tools Core Module Home-page: https://github.com/Azure/azure-cli Author: Microsoft Corporation @@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: License :: OSI Approved :: MIT License Requires-Python: >=3.8.0 License-File: LICENSE.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/azure_cli_core.egg-info/SOURCES.txt new/azure_cli_core-2.64.0/azure_cli_core.egg-info/SOURCES.txt --- old/azure_cli_core-2.63.0/azure_cli_core.egg-info/SOURCES.txt 2024-07-31 05:39:33.000000000 +0200 +++ new/azure_cli_core-2.64.0/azure_cli_core.egg-info/SOURCES.txt 2024-08-28 07:45:13.000000000 +0200 @@ -19,6 +19,7 @@ azure/cli/core/api.py azure/cli/core/azclierror.py azure/cli/core/azlogging.py +azure/cli/core/breaking_change.py azure/cli/core/cloud.py azure/cli/core/command_recommender.py azure/cli/core/credential_helper.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/azure_cli_core-2.63.0/setup.py new/azure_cli_core-2.64.0/setup.py --- old/azure_cli_core-2.63.0/setup.py 2024-07-31 05:39:17.000000000 +0200 +++ new/azure_cli_core-2.64.0/setup.py 2024-08-28 07:44:58.000000000 +0200 @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "2.63.0" +VERSION = "2.64.0" # If we have source, validate that our version numbers match # This should prevent uploading releases with mismatched versions. @@ -39,6 +39,7 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'License :: OSI Approved :: MIT License', ]