Skip to content

Commit

Permalink
[Core] BREAKING CHANGE: az extension add/update and az upgrade: I…
Browse files Browse the repository at this point in the history
…nstall stable version for extension by default, use preview if unapplicable (Azure#30163)

* switch default value for allow-preview
  • Loading branch information
AllyW authored Nov 12, 2024
1 parent 2cb3d51 commit b1f35f2
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 64 deletions.
6 changes: 6 additions & 0 deletions src/azure-cli-core/azure/cli/core/extension/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,9 @@ def is_stable_from_metadata(item):
return not (item["metadata"].get(EXT_METADATA_ISPREVIEW, False) or
item["metadata"].get(EXT_METADATA_ISEXPERIMENTAL, False) or
is_preview_from_semantic_version(item["metadata"]['version']))


def is_preview_from_metadata(item):
return bool(item["metadata"].get(EXT_METADATA_ISPREVIEW, False) or
item["metadata"].get(EXT_METADATA_ISEXPERIMENTAL, False) or
is_preview_from_semantic_version(item["metadata"]['version']))
27 changes: 25 additions & 2 deletions src/azure-cli-core/azure/cli/core/extension/_resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from packaging.version import parse
from typing import Callable, List, NamedTuple, Union

from azure.cli.core.extension import ext_compat_with_cli, WHEEL_INFO_RE, is_stable_from_metadata
from azure.cli.core.extension import (ext_compat_with_cli, WHEEL_INFO_RE,
is_stable_from_metadata, is_preview_from_metadata)
from azure.cli.core.extension._index import get_index_extensions

from knack.log import get_logger
Expand Down Expand Up @@ -104,6 +105,28 @@ def resolve_from_index(extension_name, cur_version=None, index_url=None, target_
if not candidates:
raise NoExtensionCandidatesError(f"No extension found with name '{extension_name}'")

if allow_preview is None:
# default value of allow-preview changed from true to false
# and the following part deals with two influenced scenariors if user does not specify allow-preview
# 1. if extension module does not have any stable version, set allow-preview=True and display warning message to
# unblock those extension module user
# 2. if extension module has a later preview version than stable one, dispaly a warning message to user
# indicating how to try the newer preview one, but allow-preview is still set to be False by default
allow_preview = False
stable_candidates = list(filter(is_stable_from_metadata, candidates))
preview_candidates = list(filter(is_preview_from_metadata, candidates))
if len(stable_candidates) == 0:
logger.warning("No stable version of '%s' to install. Preview versions allowed.", extension_name)
allow_preview = True
elif len(preview_candidates) != 0:
max_preview_item = max(preview_candidates, key=lambda x: parse(x['metadata']['version']))
max_stable_item = max(stable_candidates, key=lambda x: parse(x['metadata']['version']))
if parse(max_preview_item['metadata']['version']) > parse(max_stable_item['metadata']['version']):
logger.warning("Extension '%s' has a later preview version to install, add `--allow-preview True` "
"to try preview version.", extension_name)
else:
logger.info("No preview versions need to be tried.")

# Helper to curry predicate functions
def list_filter(f):
return lambda cs: list(filter(f, cs))
Expand All @@ -120,7 +143,7 @@ def list_filter(f):
_ExtensionFilter(
filter=list_filter(is_stable_from_metadata),
on_empty_results_message=f"No suitable stable version of '{extension_name}' to install. "
f"Add `--allow-preview` to try preview versions"
f"Add `--allow-preview True` to try preview versions"
)]

if target_version:
Expand Down
18 changes: 9 additions & 9 deletions src/azure-cli-core/azure/cli/core/extension/dynamic_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,15 @@ def _get_extension_run_after_dynamic_install_config(cli_ctx):


def _get_extension_allow_preview_install_config(cli_ctx):
default_value = True
if cli_ctx and cli_ctx.config.get('extension', 'dynamic_install_allow_preview', None) is None:
logger.warning("Preview version of extension is enabled by default for extension installation now. "
"Will be disabled in future release. ")
logger.warning("Please run 'az config set extension.dynamic_install_allow_preview=true or false' "
"to config it specifically. ")
dynamic_install_allow_preview = cli_ctx.config.getboolean('extension',
'dynamic_install_allow_preview',
default_value) if cli_ctx else default_value
dynamic_install_allow_preview = None
if cli_ctx:
if cli_ctx.config.get('extension', 'dynamic_install_allow_preview', None) is None:
logger.warning("Preview version of extension is disabled by default for extension installation, "
"enabled for modules without stable versions. ")
logger.warning("Please run 'az config set extension.dynamic_install_allow_preview=true or false' "
"to config it specifically. ")
else:
dynamic_install_allow_preview = cli_ctx.config.getboolean('extension', 'dynamic_install_allow_preview')
return dynamic_install_allow_preview


Expand Down
12 changes: 0 additions & 12 deletions src/azure-cli-core/azure/cli/core/extension/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,12 +306,6 @@ def check_version_compatibility(azext_metadata):
def add_extension(cmd=None, source=None, extension_name=None, index_url=None, yes=None, # pylint: disable=unused-argument, too-many-statements
pip_extra_index_urls=None, pip_proxy=None, system=None,
version=None, cli_ctx=None, upgrade=None, allow_preview=None):
if allow_preview is None:
logger.warning("Default enabled including preview versions for extension installation now. "
"Disabled in future release. "
"Use '--allow-preview true' to enable it specifically if needed. "
"Use '--allow-preview false' to install stable version only. ")
allow_preview = True
ext_sha256 = None
update_to_latest = version == 'latest' and not source

Expand Down Expand Up @@ -405,12 +399,6 @@ def show_extension(extension_name):


def update_extension(cmd=None, extension_name=None, index_url=None, pip_extra_index_urls=None, pip_proxy=None, allow_preview=None, cli_ctx=None, version=None, download_url=None, ext_sha256=None):
if allow_preview is None:
logger.warning("Default enabled including preview versions for extension installation now. "
"Disabled in future release. "
"Use '--allow-preview true' to enable it specifically if needed. "
"Use '--allow-preview false' to install stable version only. ")
allow_preview = True
try:
cmd_cli_ctx = cli_ctx or cmd.cli_ctx
ext = get_extension(extension_name, ext_type=WheelExtension)
Expand Down
50 changes: 41 additions & 9 deletions src/azure-cli-core/azure/cli/core/tests/test_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,15 +323,9 @@ def test_add_list_show_preview_extension(self):
self.assertEqual(ext["version"], "2.0.0a1")
remove_extension("ml")

def test_add_preview_extension_by_default(self):
def test_add_stable_extension_by_default(self):
test_ext_source = _get_test_data_file('extension_test_pkg-1.2.3-py3-none-any.whl')
with mock.patch('azure.cli.core.extension.operations.logger') as mock_logger:
add_extension(cmd=self.cmd, source=test_ext_source)
call_args = mock_logger.warning.call_args
self.assertEqual("Default enabled including preview versions for extension installation now. "
"Disabled in future release. "
"Use '--allow-preview true' to enable it specifically if needed. "
"Use '--allow-preview false' to install stable version only. ", call_args[0][0])
add_extension(cmd=self.cmd, source=test_ext_source)
ext = show_extension("extension-test-pkg")
self.assertEqual(ext["name"], "extension-test-pkg")
self.assertEqual(ext["version"], "1.2.3")
Expand Down Expand Up @@ -395,9 +389,47 @@ def test_add_extension_preview_inavailable(self):
from knack.util import CLIError
with mock.patch('azure.cli.core.extension._resolve.get_index_extensions',
return_value=mocked_index_data):
with self.assertRaisesRegex(CLIError, "No suitable stable version of 'extension-test-pkg' to install. Add `--allow-preview` to try preview versions"):
with self.assertRaisesRegex(CLIError, "No suitable stable version of 'extension-test-pkg' to install. Add `--allow-preview True` to try preview versions"):
add_extension(cmd=self.cmd, extension_name=extension_name, allow_preview=False)

with mock.patch('azure.cli.core.extension._resolve.logger') as mock_logger:
add_extension(cmd=self.cmd, extension_name=extension_name)
call_args = mock_logger.warning.call_args
self.assertEqual("No stable version of '%s' to install. Preview versions allowed.", call_args[0][0])
self.assertEqual(extension_name, call_args[0][1])
self.assertEqual(mock_logger.warning.call_count, 1)
ext = show_extension(extension_name)
self.assertEqual(ext["name"], extension_name)
self.assertEqual(ext["version"], "1.4.1a1")
remove_extension(extension_name)

def test_add_extension_with_later_preview(self):
extension_name = "extension-test-pkg"
extension1 = 'extension_test_pkg-1.0.0b1-py3-none-any.whl'
extension2 = 'extension_test_pkg-1.2.3-py3-none-any.whl'
extension3 = 'extension_test_pkg-1.4.1a1-py3-none-any.whl'

mocked_index_data = {
extension_name: [
mock_ext(extension1, version='1.0.0b1', download_url=_get_test_data_file(extension1)),
mock_ext(extension2, version='1.2.3', download_url=_get_test_data_file(extension2)),
mock_ext(extension3, version='1.4.1a1', download_url=_get_test_data_file(extension3))
]
}
from knack.util import CLIError
with mock.patch('azure.cli.core.extension._resolve.get_index_extensions',
return_value=mocked_index_data):
with mock.patch('azure.cli.core.extension._resolve.logger') as mock_logger:
add_extension(cmd=self.cmd, extension_name=extension_name)
call_args = mock_logger.warning.call_args
self.assertEqual("Extension '%s' has a later preview version to install, add `--allow-preview True` to try preview version.", call_args[0][0])
self.assertEqual(extension_name, call_args[0][1])
self.assertEqual(mock_logger.warning.call_count, 1)
ext = show_extension(extension_name)
self.assertEqual(ext["name"], extension_name)
self.assertEqual(ext["version"], "1.2.3")
remove_extension(extension_name)

def test_update_extension_with_preview(self):
extension_name = "extension-test-pkg"
extension1 = 'extension_test_pkg-1.0.0b1-py3-none-any.whl'
Expand Down

This file was deleted.

10 changes: 0 additions & 10 deletions src/azure-cli/azure/cli/command_modules/util/_breaking_change.py

This file was deleted.

12 changes: 2 additions & 10 deletions src/azure-cli/azure/cli/command_modules/util/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,20 +172,12 @@ def upgrade_version(cmd, update_all=None, yes=None, allow_preview=None): # pyli

if exts:
logger.warning("Upgrading extensions")
if allow_preview is None:
logger.warning("Default enabled including preview versions for extension installation now. "
"Disabled in future release. "
"Use '--allow-preview-extensions true' to enable it specifically if needed. "
"Use '--allow-preview-extensions false' to install stable version only. ")
allow_preview = True
for ext_name in exts:
try:
logger.warning("Checking update for %s", ext_name)
cmds = ['az', 'extension', 'update', '-n', ext_name]
if allow_preview:
cmds += ["--allow-preview", "true"]
else:
cmds += ["--allow-preview", "false"]
if allow_preview is not None:
cmds += ["--allow-preview", str(allow_preview)]
subprocess.call(cmds, shell=platform.system() == 'Windows')
except Exception as ex: # pylint: disable=broad-except
msg = "Extension {} update failed during az upgrade. {}".format(ext_name, str(ex))
Expand Down

0 comments on commit b1f35f2

Please sign in to comment.