зеркало из https://github.com/microsoft/azure-cli.git
Preview extension notation & Simple default view for extension list-a… (#5882)
* Preview extension notation & Simple default view for extension list-available - Show message for extensions marked as preview on -h * Update HISTORY.rst * Add more tests * fix style
This commit is contained in:
Родитель
28ede753f2
Коммит
e4339a7dac
|
@ -50,4 +50,9 @@ Type: `string`
|
|||
|
||||
Example: `"azext.maxCliCoreVersion": "2.0.15"`
|
||||
|
||||
### azext.isPreview
|
||||
Description: Indicate that the extension is in preview.
|
||||
|
||||
Type: `boolean`
|
||||
|
||||
Example: `"azext.isPreview": true`
|
||||
|
|
|
@ -6,7 +6,7 @@ Release History
|
|||
|
||||
2.0.30
|
||||
++++++
|
||||
* Minor fixes
|
||||
* Show message for extensions marked as preview on -h.
|
||||
|
||||
2.0.29
|
||||
++++++
|
||||
|
|
|
@ -163,7 +163,8 @@ class MainCommandsLoader(CLICommandsLoader):
|
|||
if extensions:
|
||||
logger.debug("Found %s extensions: %s", len(extensions), [e.name for e in extensions])
|
||||
allowed_extensions = _handle_extension_suppressions(extensions)
|
||||
for ext_name in [e.name for e in allowed_extensions]:
|
||||
for ext in allowed_extensions:
|
||||
ext_name = ext.name
|
||||
ext_dir = get_extension_path(ext_name)
|
||||
sys.path.append(ext_dir)
|
||||
try:
|
||||
|
@ -177,7 +178,8 @@ class MainCommandsLoader(CLICommandsLoader):
|
|||
for cmd_name, cmd in extension_command_table.items():
|
||||
cmd.command_source = ExtensionCommandSource(
|
||||
extension_name=ext_name,
|
||||
overrides_command=cmd_name in cmd_to_mod_map)
|
||||
overrides_command=cmd_name in cmd_to_mod_map,
|
||||
preview=ext.preview)
|
||||
|
||||
self.command_table.update(extension_command_table)
|
||||
elapsed_time = timeit.default_timer() - start_time
|
||||
|
|
|
@ -56,6 +56,8 @@ class AzCliHelp(CLIHelp):
|
|||
return
|
||||
if help_file.command_source and isinstance(help_file.command_source, ExtensionCommandSource):
|
||||
logger.warning(help_file.command_source.get_command_warn_msg())
|
||||
if help_file.command_source.preview:
|
||||
logger.warning(help_file.command_source.get_preview_warn_msg())
|
||||
|
||||
@classmethod
|
||||
def print_detailed_help(cls, cli_name, help_file):
|
||||
|
|
|
@ -568,11 +568,12 @@ def _load_module_command_loader(loader, args, mod):
|
|||
class ExtensionCommandSource(object):
|
||||
""" Class for commands contributed by an extension """
|
||||
|
||||
def __init__(self, overrides_command=False, extension_name=None):
|
||||
def __init__(self, overrides_command=False, extension_name=None, preview=False):
|
||||
super(ExtensionCommandSource, self).__init__()
|
||||
# True if the command overrides a CLI command
|
||||
self.overrides_command = overrides_command
|
||||
self.extension_name = extension_name
|
||||
self.preview = preview
|
||||
|
||||
def get_command_warn_msg(self):
|
||||
if self.overrides_command:
|
||||
|
@ -585,6 +586,11 @@ class ExtensionCommandSource(object):
|
|||
return "This command is from the following extension: {}".format(self.extension_name)
|
||||
return "This command is from an extension."
|
||||
|
||||
def get_preview_warn_msg(self):
|
||||
if self.preview:
|
||||
return "The extension is in preview"
|
||||
return None
|
||||
|
||||
|
||||
def _load_client_exception_class():
|
||||
# Since loading msrest is expensive, we avoid it until we have to
|
||||
|
|
|
@ -22,6 +22,7 @@ AZEXT_METADATA_FILENAME = 'azext_metadata.json'
|
|||
|
||||
EXT_METADATA_MINCLICOREVERSION = 'azext.minCliCoreVersion'
|
||||
EXT_METADATA_MAXCLICOREVERSION = 'azext.maxCliCoreVersion'
|
||||
EXT_METADATA_ISPREVIEW = 'azext.isPreview'
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
@ -42,6 +43,7 @@ class Extension(object):
|
|||
self.ext_type = ext_type
|
||||
self._version = None
|
||||
self._metadata = None
|
||||
self._preview = None
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
|
@ -67,6 +69,19 @@ class Extension(object):
|
|||
logger.debug("Unable to get extension metadata: %s", traceback.format_exc())
|
||||
return self._metadata
|
||||
|
||||
@property
|
||||
def preview(self):
|
||||
"""
|
||||
Lazy load preview status.
|
||||
Returns the preview status of the extension.
|
||||
"""
|
||||
try:
|
||||
if not isinstance(self._preview, bool):
|
||||
self._preview = bool(self.metadata.get(EXT_METADATA_ISPREVIEW))
|
||||
except Exception: # pylint: disable=broad-except
|
||||
logger.debug("Unable to get extension preview status: %s", traceback.format_exc())
|
||||
return self._preview
|
||||
|
||||
def get_version(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -152,9 +152,9 @@ class TestCommandRegistration(unittest.TestCase):
|
|||
return ext_name
|
||||
|
||||
def _mock_get_extensions():
|
||||
MockExtension = namedtuple('Extension', ['name'])
|
||||
return [MockExtension(name=__name__ + '.ExtCommandsLoader'),
|
||||
MockExtension(name=__name__ + '.Ext2CommandsLoader')]
|
||||
MockExtension = namedtuple('Extension', ['name', 'preview'])
|
||||
return [MockExtension(name=__name__ + '.ExtCommandsLoader', preview=False),
|
||||
MockExtension(name=__name__ + '.Ext2CommandsLoader', preview=False)]
|
||||
|
||||
def _mock_load_command_loader(loader, args, name, prefix):
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ Release History
|
|||
|
||||
0.0.11
|
||||
++++++
|
||||
* Minor fixes
|
||||
* Preview extensions: Show message on `az extension add` if extension is in preview
|
||||
* BC: `az extension list-available` - The full extension data is now available with `--show-details`
|
||||
* `az extension list-available` - A simplified view of the extensions available is now shown by default
|
||||
|
||||
0.0.10
|
||||
+++++++
|
||||
|
|
|
@ -27,7 +27,10 @@ class ExtensionCommandsLoader(AzCommandsLoader):
|
|||
return bool(not command_args.get('source') or prompt_y_n('Are you sure you want to install this extension?'))
|
||||
|
||||
def transform_extension_list_available(results):
|
||||
return [OrderedDict([('Name', r)]) for r in results]
|
||||
if isinstance(results, dict):
|
||||
# For --show-details, transform the table
|
||||
return [OrderedDict([('Name', r)]) for r in results]
|
||||
return results
|
||||
|
||||
def validate_extension_add(namespace):
|
||||
if (namespace.extension_name and namespace.source) or (not namespace.extension_name and not namespace.source):
|
||||
|
@ -67,5 +70,8 @@ class ExtensionCommandsLoader(AzCommandsLoader):
|
|||
c.argument('source', options_list=['--source', '-s'], help='Filepath or URL to an extension', completer=FilesCompleter())
|
||||
c.argument('yes', options_list=['--yes', '-y'], action='store_true', help='Do not prompt for confirmation.')
|
||||
|
||||
with self.argument_context('extension list-available') as c:
|
||||
c.argument('show_details', options_list=['--show-details', '-d'], action='store_true', help='Show the raw data from the extension index.')
|
||||
|
||||
|
||||
COMMAND_LOADER_CLS = ExtensionCommandsLoader
|
||||
|
|
|
@ -11,14 +11,17 @@ import traceback
|
|||
import hashlib
|
||||
from subprocess import check_output, STDOUT, CalledProcessError
|
||||
from six.moves.urllib.parse import urlparse # pylint: disable=import-error
|
||||
from collections import OrderedDict
|
||||
|
||||
import requests
|
||||
from wheel.install import WHEEL_INFO_RE
|
||||
from pkg_resources import parse_version
|
||||
|
||||
from knack.log import get_logger
|
||||
|
||||
from azure.cli.core.util import CLIError
|
||||
from azure.cli.core.extension import (extension_exists, get_extension_path, get_extensions,
|
||||
get_extension, ext_compat_with_cli,
|
||||
get_extension, ext_compat_with_cli, EXT_METADATA_ISPREVIEW,
|
||||
WheelExtension, ExtensionNotInstalledException)
|
||||
from azure.cli.core.telemetry import set_extension_management_detail
|
||||
|
||||
|
@ -200,6 +203,11 @@ def add_extension(source=None, extension_name=None, index_url=None, yes=None, #
|
|||
raise CLIError("No matching extensions for '{}'. Use --debug for more information.".format(extension_name))
|
||||
_add_whl_ext(source, ext_sha256=ext_sha256, pip_extra_index_urls=pip_extra_index_urls, pip_proxy=pip_proxy)
|
||||
_augment_telemetry_with_ext_info(extension_name)
|
||||
try:
|
||||
if extension_name and get_extension(extension_name).preview:
|
||||
logger.warning("The installed extension '%s' is in preview.", extension_name)
|
||||
except ExtensionNotInstalledException:
|
||||
pass
|
||||
|
||||
|
||||
def remove_extension(extension_name):
|
||||
|
@ -263,8 +271,28 @@ def update_extension(extension_name, index_url=None, pip_extra_index_urls=None,
|
|||
raise CLIError(e)
|
||||
|
||||
|
||||
def list_available_extensions(index_url=None):
|
||||
return get_index_extensions(index_url=index_url)
|
||||
def list_available_extensions(index_url=None, show_details=False):
|
||||
index_data = get_index_extensions(index_url=index_url)
|
||||
if show_details:
|
||||
return index_data
|
||||
installed_extensions = get_extensions()
|
||||
installed_extension_names = [e.name for e in installed_extensions]
|
||||
results = []
|
||||
for name, items in OrderedDict(sorted(index_data.items())).items():
|
||||
latest = sorted(items, key=lambda c: parse_version(c['metadata']['version']), reverse=True)[0]
|
||||
installed = False
|
||||
if name in installed_extension_names:
|
||||
installed = True
|
||||
if parse_version(latest['metadata']['version']) > parse_version(get_extension(name).version):
|
||||
installed = str(True) + ' (upgrade available)'
|
||||
results.append({
|
||||
'name': name,
|
||||
'version': latest['metadata']['version'],
|
||||
'summary': latest['metadata']['summary'],
|
||||
'preview': latest['metadata'].get(EXT_METADATA_ISPREVIEW, False),
|
||||
'installed': installed
|
||||
})
|
||||
return results
|
||||
|
||||
|
||||
def get_lsb_release():
|
||||
|
@ -296,14 +324,14 @@ def check_distro_consistency():
|
|||
except Exception as err: # pylint: disable=broad-except
|
||||
current_linux_dist_name = None
|
||||
stored_linux_dist_name = None
|
||||
logger.debug('Linux distro check: An error occurred while checking \
|
||||
linux distribution version source list consistency.')
|
||||
logger.debug('Linux distro check: An error occurred while checking '
|
||||
'linux distribution version source list consistency.')
|
||||
logger.debug(err)
|
||||
|
||||
if current_linux_dist_name != stored_linux_dist_name:
|
||||
logger.warning("Linux distro check: Mismatch distribution \
|
||||
name in %s file", LIST_FILE_PATH)
|
||||
logger.warning("Linux distro check: If command fails, install the appropriate package \
|
||||
for your distribution or change the above file accordingly.")
|
||||
logger.warning("Linux distro check: Mismatch distribution "
|
||||
"name in %s file", LIST_FILE_PATH)
|
||||
logger.warning("Linux distro check: If command fails, install the appropriate package "
|
||||
"for your distribution or change the above file accordingly.")
|
||||
logger.warning("Linux distro check: %s has '%s', current distro is '%s'",
|
||||
LIST_FILE_PATH, stored_linux_dist_name, current_linux_dist_name)
|
||||
|
|
|
@ -251,6 +251,29 @@ class TestExtensionCommands(unittest.TestCase):
|
|||
list_available_extensions(index_url=index_url)
|
||||
c.assert_called_once_with(index_url)
|
||||
|
||||
def test_list_available_extensions_show_details(self):
|
||||
with mock.patch('azure.cli.command_modules.extension.custom.get_index_extensions', autospec=True) as c:
|
||||
list_available_extensions(show_details=True)
|
||||
c.assert_called_once_with(None)
|
||||
|
||||
def test_list_available_extensions_no_show_details(self):
|
||||
sample_index_extensions = {
|
||||
'test_sample_extension1': [{
|
||||
'metadata': {
|
||||
'name': 'test_sample_extension1',
|
||||
'summary': 'my summary',
|
||||
'version': '0.1.0'
|
||||
}}]
|
||||
}
|
||||
with mock.patch('azure.cli.command_modules.extension.custom.get_index_extensions', return_value=sample_index_extensions):
|
||||
res = list_available_extensions()
|
||||
self.assertIsInstance(res, list)
|
||||
self.assertEqual(len(res), len(sample_index_extensions))
|
||||
self.assertEqual(res[0]['name'], 'test_sample_extension1')
|
||||
self.assertEqual(res[0]['summary'], 'my summary')
|
||||
self.assertEqual(res[0]['version'], '0.1.0')
|
||||
self.assertEqual(res[0]['preview'], False)
|
||||
|
||||
def test_add_list_show_remove_extension_extra_index_url(self):
|
||||
"""
|
||||
Tests extension addition while specifying --extra-index-url parameter.
|
||||
|
|
Загрузка…
Ссылка в новой задаче