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(): # exclude extensions/versions incompatible with current CLI version items = [item for item in items if ext_compat_with_cli(item['metadata'])[0]] if not items: continue latest = max(items, key=lambda c: parse_version(c['metadata']['version'])) installed = False if name in installed_extension_names: installed = True ext_version = get_extension(name).version if ext_version and parse_version(latest['metadata']['version']) > parse_version(ext_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 test_ext_compat_with_cli_only_min_v_constraint(self): expected_cli_version = '0.0.5' expected_min_required = '0.0.1' azext_metadata = {EXT_METADATA_MINCLICOREVERSION: expected_min_required} with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, _, _, _ = ext_compat_with_cli(azext_metadata) self.assertTrue(is_compatible)
def test_ext_compat_with_cli_failed_bad_max_but_close_v_constraint(self): expected_cli_version = '0.0.5' expected_max_required = '0.0.5b1' azext_metadata = {EXT_METADATA_MAXCLICOREVERSION: expected_max_required} with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, _, _, _ = ext_compat_with_cli(azext_metadata) self.assertFalse(is_compatible)
def test_ext_compat_with_cli_failed_bad_max_but_close_v_constraint(self): expected_cli_version = '0.0.5' expected_max_required = '0.0.5b1' azext_metadata = {EXT_METADATA_MAXCLICOREVERSION: expected_max_required} with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, _, _, _, _ = ext_compat_with_cli(azext_metadata) self.assertFalse(is_compatible)
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(ext_type=WheelExtension) installed_extension_names = [e.name for e in installed_extensions] results = [] for name, items in OrderedDict(sorted(index_data.items())).items(): # exclude extensions/versions incompatible with current CLI version items = [item for item in items if ext_compat_with_cli(item['metadata'])[0]] if not items: continue latest = max(items, key=lambda c: parse_version(c['metadata']['version'])) installed = False if name in installed_extension_names: installed = True ext_version = get_extension(name).version if ext_version and parse_version(latest['metadata']['version']) > parse_version(ext_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), 'experimental': latest['metadata'].get(EXT_METADATA_ISEXPERIMENTAL, False), 'installed': installed }) return results
def check_version_compatibility(azext_metadata): is_compatible, cli_core_version, min_required, max_required, min_ext_required = ext_compat_with_cli(azext_metadata) # logger.debug("Extension compatibility result: is_compatible=%s cli_core_version=%s min_required=%s " # "max_required=%s", is_compatible, cli_core_version, min_required, max_required) if not is_compatible: ext_name = azext_metadata.get('name') ext_version = azext_metadata.get('version') min_max_msgs = [ f"The '{ext_name}' extension version {ext_version} is not compatible with your current CLI core version {cli_core_version}." ] if min_ext_required: min_max_msgs.append(f"This CLI core requires a min of {min_ext_required} for the '{ext_name}' extension.") min_max_msgs.append(f"Please run 'az extension update -n {ext_name}' to update it.") elif min_required and max_required: min_max_msgs.append(f'This extension requires a min of {min_required} and max of {max_required} CLI core.') min_max_msgs.append("Please run 'az upgrade' to upgrade to a compatible version.") elif min_required: min_max_msgs.append(f'This extension requires a min of {min_required} CLI core.') min_max_msgs.append("Please run 'az upgrade' to upgrade to a compatible version.") elif max_required: min_max_msgs.append(f'This extension requires a max of {max_required} CLI core.') # we do not want users to downgrade CLI core version, so we suggest updating the extension in this case min_max_msgs.append(f"Please run 'az extension update -n {ext_name}' to update the extension.") raise CLIError("\n".join(min_max_msgs))
def test_ext_compat_with_cli_only_min_v_constraint(self): expected_cli_version = '0.0.5' expected_min_required = '0.0.1' azext_metadata = {EXT_METADATA_MINCLICOREVERSION: expected_min_required} with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, _, _, _, _ = ext_compat_with_cli(azext_metadata) self.assertTrue(is_compatible)
def test_ext_compat_with_cli_no_v_constraint(self): # An extension that does not specify any version constraint on the CLI expected_cli_version = '0.0.1' azext_metadata = None with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, cli_version, _, _ = ext_compat_with_cli(azext_metadata) self.assertTrue(is_compatible) self.assertEqual(cli_version, expected_cli_version)
def test_ext_compat_with_cli_no_v_constraint(self): # An extension that does not specify any version constraint on the CLI expected_cli_version = '0.0.1' azext_metadata = None with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, cli_version, _, _, _ = ext_compat_with_cli(azext_metadata) self.assertTrue(is_compatible) self.assertEqual(cli_version, expected_cli_version)
def _is_compatible_with_cli_version(item): is_compatible, cli_core_version, min_required, max_required = ext_compat_with_cli(item['metadata']) if is_compatible: return True logger.debug("Skipping '%s' as not compatible with this version of the CLI. " "Extension compatibility result: is_compatible=%s cli_core_version=%s min_required=%s " "max_required=%s", item['filename'], is_compatible, cli_core_version, min_required, max_required) return False
def test_ext_compat_with_cli_require_ext_min_version(self): expected_cli_version = '0.0.5' expected_min_ext_required = '0.2.0' azext_metadata = {'name': 'myext', 'version': '0.1.0'} with mock.patch('azure.cli.core.__version__', expected_cli_version), \ mock.patch('azure.cli.core.extension.EXTENSION_VERSION_REQUIREMENTS', {'myext': {'minExtVersion': expected_min_ext_required}}): is_compatible, _, _, _, min_ext_required = ext_compat_with_cli(azext_metadata) self.assertFalse(is_compatible) self.assertEqual(min_ext_required, expected_min_ext_required)
def test_ext_compat_with_cli_single_v_constraint(self): # An extension that only works with a specific version of the CLI expected_cli_version = '0.0.1' expected_min_required = '0.0.1' expected_max_required = '0.0.1' azext_metadata = {EXT_METADATA_MINCLICOREVERSION: expected_min_required, EXT_METADATA_MAXCLICOREVERSION: expected_max_required} with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, cli_version, min_required, max_required = ext_compat_with_cli(azext_metadata) self.assertTrue(is_compatible) self.assertEqual(cli_version, expected_cli_version) self.assertEqual(min_required, expected_min_required) self.assertEqual(max_required, expected_max_required)
def test_ext_compat_with_cli_single_v_constraint(self): # An extension that only works with a specific version of the CLI expected_cli_version = '0.0.1' expected_min_required = '0.0.1' expected_max_required = '0.0.1' azext_metadata = {EXT_METADATA_MINCLICOREVERSION: expected_min_required, EXT_METADATA_MAXCLICOREVERSION: expected_max_required} with mock.patch('azure.cli.core.__version__', expected_cli_version): is_compatible, cli_version, min_required, max_required, _ = ext_compat_with_cli(azext_metadata) self.assertTrue(is_compatible) self.assertEqual(cli_version, expected_cli_version) self.assertEqual(min_required, expected_min_required) self.assertEqual(max_required, expected_max_required)
def _validate_whl_cli_compat(azext_metadata): is_compatible, cli_version, min_required, max_required = ext_compat_with_cli(azext_metadata) logger.debug("Extension compatibility result: is_compatible=%s cli_version=%s min_required=%s " "max_required=%s", is_compatible, cli_version, min_required, max_required) if not is_compatible: min_max_msg_fmt = "The extension is not compatible with this version of the CLI.\n" \ "You have CLI version {} and this extension " \ "requires ".format(cli_version) if min_required and max_required: min_max_msg_fmt += 'a min of {} and max of {}.'.format(min_required, max_required) elif min_required: min_max_msg_fmt += 'a min of {}.'.format(min_required) elif max_required: min_max_msg_fmt += 'a max of {}.'.format(max_required) raise CLIError(min_max_msg_fmt)
def _validate_whl_cli_compat(azext_metadata): is_compatible, cli_core_version, min_required, max_required = ext_compat_with_cli(azext_metadata) logger.debug("Extension compatibility result: is_compatible=%s cli_core_version=%s min_required=%s " "max_required=%s", is_compatible, cli_core_version, min_required, max_required) if not is_compatible: min_max_msg_fmt = "The extension is not compatible with this version of the CLI.\n" \ "You have CLI core version {} and this extension " \ "requires ".format(cli_core_version) if min_required and max_required: min_max_msg_fmt += 'a min of {} and max of {}.'.format(min_required, max_required) elif min_required: min_max_msg_fmt += 'a min of {}.'.format(min_required) elif max_required: min_max_msg_fmt += 'a max of {}.'.format(max_required) raise CLIError(min_max_msg_fmt)
def check_version_compatibility(azext_metadata): is_compatible, cli_core_version, min_required, max_required = ext_compat_with_cli(azext_metadata) logger.debug("Extension compatibility result: is_compatible=%s cli_core_version=%s min_required=%s " "max_required=%s", is_compatible, cli_core_version, min_required, max_required) if not is_compatible: min_max_msg_fmt = "The '{}' extension is not compatible with this version of the CLI.\n" \ "You have CLI core version {} and this extension " \ "requires ".format(azext_metadata.get('name'), cli_core_version) if min_required and max_required: min_max_msg_fmt += 'a min of {} and max of {}.'.format(min_required, max_required) elif min_required: 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)
def list_versions(extension_name, index_url=None): index_data = get_index_extensions(index_url=index_url) try: exts = index_data[extension_name] except Exception: raise CLIError('Extension {} not found.'.format(extension_name)) try: installed_ext = get_extension(extension_name, ext_type=WheelExtension) except ExtensionNotInstalledException: installed_ext = None results = [] latest_compatible_version = None for ext in sorted(exts, key=lambda c: parse_version(c['metadata']['version']), reverse=True): compatible = ext_compat_with_cli(ext['metadata'])[0] ext_version = ext['metadata']['version'] if latest_compatible_version is None and compatible: latest_compatible_version = ext_version installed = ext_version == installed_ext.version if installed_ext else False if installed and parse_version( latest_compatible_version) > parse_version( installed_ext.version): installed = str(True) + ' (upgrade available)' version = ext['metadata']['version'] if latest_compatible_version == ext_version: version = version + ' (max compatible version)' results.append({ 'name': extension_name, 'version': version, 'preview': ext['metadata'].get(EXT_METADATA_ISPREVIEW, False), 'experimental': ext['metadata'].get(EXT_METADATA_ISEXPERIMENTAL, False), 'installed': installed, 'compatible': compatible }) results.reverse() return results