def list_extensions(): from glob import glob azure_config = get_azure_config() dev_sources = azure_config.get('extension', 'dev_sources', None) dev_sources = dev_sources.split(',') if dev_sources else [] installed = _get_installed_dev_extensions(dev_sources) installed_names = [x['name'] for x in installed] results = [] for ext_path in find_files(dev_sources, 'setup.py'): # skip non-extension packages that may be in the extension folder (for example, from a virtual environment) try: glob_pattern = os.path.join( os.path.split(ext_path)[0], '{}*'.format(EXTENSION_PREFIX)) _ = glob(glob_pattern)[0] except IndexError: continue folder = os.path.dirname(ext_path) long_name = os.path.basename(folder) if long_name not in installed_names: results.append({'name': long_name, 'install': '', 'path': folder}) else: results.append({ 'name': long_name, 'install': 'Installed', 'path': folder }) return results
def remove_extension_repo(repos): az_config = get_azure_config() env_config = get_env_config() dev_sources = az_config.get('extension', 'dev_sources', None) dev_sources = dev_sources.split(',') if dev_sources else [] for repo in repos: try: dev_sources.remove(os.path.abspath(repo)) except ValueError: logger.warning("Repo '%s' was not found in the list of repositories to search.", os.path.abspath(repo)) az_config.set_value('extension', 'dev_sources', ','.join(dev_sources)) env_config.set_value('ext', 'repo_paths', ','.join(dev_sources)) return list_extension_repos()
def add_extension_repo(repos): from azdev.operations.setup import _check_repo az_config = get_azure_config() env_config = get_azdev_config() dev_sources = az_config.get('extension', 'dev_sources', None) dev_sources = dev_sources.split(',') if dev_sources else [] for repo in repos: repo = os.path.abspath(repo) _check_repo(repo) if repo not in dev_sources: dev_sources.append(repo) az_config.set_value('extension', 'dev_sources', ','.join(dev_sources)) env_config.set_value('ext', 'repo_paths', ','.join(dev_sources)) return list_extension_repos()
def get_ext_repo_paths(): """ Return the paths to the Azure CLI dev extensions. :returns: Path (str) to Azure CLI dev extension repos. """ from configparser import NoSectionError, NoOptionError try: return get_azure_config().get(const.EXT_SECTION, const.AZ_DEV_SRC).split(',') except NoSectionError: raise CLIError( 'Unable to retrieve extensions repo path from config. Please run `azdev setup` ' 'with -r to set an extensions repo.') except NoOptionError: raise CLIError( 'Unable to retrieve the option {} from azure config section [{}]'. format(const.AZ_DEV_SRC, const.EXT_SECTION))
def list_extensions(): azure_config = get_azure_config() dev_sources = azure_config.get('extension', 'dev_sources', None) dev_sources = dev_sources.split(',') if dev_sources else [] installed = _get_installed_dev_extensions(dev_sources) installed_names = [x['name'] for x in installed] results = [] for ext_path in find_files(dev_sources, 'setup.py'): folder = os.path.dirname(ext_path) long_name = os.path.basename(folder) if long_name not in installed_names: results.append({'name': long_name, 'inst': '', 'path': folder}) else: results.append({'name': long_name, 'inst': 'Y', 'path': folder}) return results
def setup(cli_path=None, ext_repo_path=None, ext=None, deps=None): require_virtual_env() start = time.time() heading('Azure CLI Dev Setup') ext_to_install = [] if not any([cli_path, ext_repo_path, ext]): cli_path, ext_repo_path, ext_to_install = _interactive_setup() else: if cli_path == "pypi": cli_path = None # otherwise assume programmatic setup if cli_path: CLI_SENTINEL = 'azure-cli.pyproj' if cli_path == Flag: cli_path = find_file(CLI_SENTINEL) if not cli_path: raise CLIError( 'Unable to locate your CLI repo. Things to check:' '\n Ensure you have cloned the repo. ' '\n Specify the path explicitly with `-c PATH`. ' '\n If you run with `-c` to autodetect, ensure you are running ' 'this command from a folder upstream of the repo.') if cli_path != 'EDGE': cli_path = _check_path(cli_path, CLI_SENTINEL) display('Azure CLI:\n {}\n'.format(cli_path)) else: display('Azure CLI:\n PyPI\n') # must add the necessary repo to add an extension if ext and not ext_repo_path: raise CLIError( 'usage error: --repo EXT_REPO [EXT_REPO ...] [--ext EXT_NAME ...]' ) get_azure_config().set_value('extension', 'dev_sources', '') if ext_repo_path: # add extension repo(s) add_extension_repo(ext_repo_path) display('Azure CLI extension repos:\n {}'.format('\n '.join( [os.path.abspath(x) for x in ext_repo_path]))) if ext == ['*']: ext_to_install = [x['path'] for x in list_extensions()] elif ext: # add extension(s) available_extensions = [x['name'] for x in list_extensions()] not_found = [x for x in ext if x not in available_extensions] if not_found: raise CLIError( "The following extensions were not found. Ensure you have added " "the repo using `--repo/-r PATH`.\n {}".format( '\n '.join(not_found))) ext_to_install = [ x['path'] for x in list_extensions() if x['name'] in ext ] if ext_to_install: display('\nAzure CLI extensions:\n {}'.format( '\n '.join(ext_to_install))) dev_sources = get_azure_config().get('extension', 'dev_sources', None) # save data to config files config = get_azdev_config() config.set_value('ext', 'repo_paths', dev_sources if dev_sources else '_NONE_') config.set_value('cli', 'repo_path', cli_path if cli_path else '_NONE_') # install packages subheading('Installing packages') # upgrade to latest pip pip_cmd('install --upgrade pip -q', 'Upgrading pip...') _install_cli(cli_path, deps=deps) _install_extensions(ext_to_install) _copy_config_files() end = time.time() elapsed_min = int((end - start) / 60) elapsed_sec = int(end - start) % 60 display('\nElapsed time: {} min {} sec'.format(elapsed_min, elapsed_sec)) subheading('Finished dev setup!')
def list_extension_repos(): az_config = get_azure_config() dev_sources = az_config.get('extension', 'dev_sources', None) return dev_sources.split(',') if dev_sources else dev_sources
def setup(cli_path=None, ext_repo_path=None, ext=None, deps=None, set_env=None, copy=None, use_global=None): _check_env(set_env) _check_shell() heading('Azure CLI Dev Setup') # cases for handling legacy install if not any([cli_path, ext_repo_path]) or cli_path == "pypi": display( "WARNING: Installing azdev in legacy mode. Run with atleast -c " "to install the latest azdev wihout \"pypi\"\n") return _handle_legacy(cli_path, ext_repo_path, ext, deps, time.time()) if 'CONDA_PREFIX' in os.environ: raise CLIError('CONDA virutal enviroments are not supported outside' ' of interactive mode or when -c and -r are provided') if not cli_path: cli_path = _handle_no_cli_path() _validate_input(cli_path, ext_repo_path, set_env, copy, use_global, ext) _check_paths(cli_path, ext_repo_path) if set_env: shell_cmd((const.VENV_CMD if const.IS_WINDOWS else const.VENV_CMD3) + set_env, raise_ex=False) azure_path = os.path.join(os.path.abspath(os.getcwd()), set_env) else: azure_path = os.environ.get('VIRTUAL_ENV') dot_azure_config = os.path.join(azure_path, '.azure') dot_azdev_config = os.path.join(azure_path, '.azdev') # clean up venv dirs if they already existed # and this is a reinstall/new setup if os.path.isdir(dot_azure_config): shutil.rmtree(dot_azure_config) if os.path.isdir(dot_azdev_config): shutil.rmtree(dot_azdev_config) global_az_config = os.path.expanduser(os.path.join('~', '.azure')) global_azdev_config = os.path.expanduser(os.path.join('~', '.azdev')) azure_config_path = os.path.join(dot_azure_config, const.CONFIG_NAME) azdev_config_path = os.path.join(dot_azdev_config, const.CONFIG_NAME) if os.path.isdir(global_az_config) and copy: shutil.copytree(global_az_config, dot_azure_config) if os.path.isdir(global_azdev_config): shutil.copytree(global_azdev_config, dot_azdev_config) else: os.mkdir(dot_azdev_config) file = open(azdev_config_path, "w") file.close() elif not use_global and not copy: os.mkdir(dot_azure_config) os.mkdir(dot_azdev_config) file_az, file_dev = open(azure_config_path, "w"), open(azdev_config_path, "w") file_az.close() file_dev.close() elif os.path.isdir(global_az_config): dot_azure_config, dot_azdev_config = global_az_config, global_azdev_config azure_config_path = os.path.join(dot_azure_config, const.CONFIG_NAME) else: raise CLIError( "Global AZ config is not set up, yet it was specified to be used.") # set env vars for get azure config and get azdev config os.environ['AZURE_CONFIG_DIR'], os.environ[ 'AZDEV_CONFIG_DIR'] = dot_azure_config, dot_azdev_config config = get_azure_config() if not config.get('cloud', 'name', None): config.set_value('cloud', 'name', 'AzureCloud') if ext_repo_path: config.set_value(const.EXT_SECTION, const.AZ_DEV_SRC, os.path.abspath(ext_repo_path)) venv.edit_activate(azure_path, dot_azure_config, dot_azdev_config) if cli_path: config.set_value('clipath', const.AZ_DEV_SRC, os.path.abspath(cli_path)) venv.install_cli(os.path.abspath(cli_path), azure_path) config = get_azdev_config() config.set_value( 'ext', 'repo_paths', os.path.abspath(ext_repo_path) if ext_repo_path else '_NONE_') config.set_value('cli', 'repo_path', os.path.abspath(cli_path)) _copy_config_files() if ext and ext_repo_path: venv.install_extensions(azure_path, ext) if not set_env: heading( "The setup was successful! Please run or re-run the virtual environment activation script." ) else: heading("The setup was successful!") return None
def get_path_table(include_only=None, include_whl_extensions=False): """ Returns a table containing the long and short names of different modules and extensions and the path to them. The structure looks like: { 'core': { NAME: PATH, ... }, 'mod': { NAME: PATH, ... }, 'ext': { NAME: PATH, ... } } """ config = get_azure_config() # pylint: disable=import-error try: EXTENSIONS_DIR = config.get(const.EXT_SECTION, const.AZ_DEV_SRC) os.chdir(EXTENSIONS_DIR) except (configparser.NoSectionError, configparser.NoOptionError, TypeError): display( "WARNING: No extension path found, only modules will be available. " "rerun setup with -r to make extensions available\n") EXTENSIONS_DIR = "" # determine whether the call will filter or return all if isinstance(include_only, str): include_only = [include_only] get_all = not include_only table = {} cli_repo_path = get_cli_repo_path() paths = os.path.normcase( os.path.join(cli_repo_path, 'src', 'azure-cli', 'azure', 'cli', 'command_modules', '*', '__init__.py')) modules_paths = glob(paths) core_paths = glob( os.path.normcase(os.path.join(cli_repo_path, 'src', '*', 'setup.py'))) ext_paths = [ x for x in find_files(EXTENSIONS_DIR, '*.*-info') if 'site-packages' not in x ] whl_ext_paths = [ x for x in find_files(EXTENSIONS_DIR, '*.*-info') if 'site-packages' not in x ] def _update_table(package_paths, key): if key not in table: table[key] = {} for path in package_paths: folder = os.path.dirname(path) base_name = os.path.basename(folder) if key == 'ext': short_name = base_name long_name = next((item for item in os.listdir(folder) if item.startswith(const.EXTENSION_PREFIX)), None) else: short_name = base_name long_name = '{}{}'.format(const.COMMAND_MODULE_PREFIX, base_name) if get_all: table[key][long_name if key == 'ext' else short_name] = folder elif not include_only: return # nothing left to filter else: # check and update filter if short_name in include_only: include_only.remove(short_name) table[key][short_name] = folder if long_name in include_only: # long name takes precedence to ensure path doesn't appear twice include_only.remove(long_name) table[key].pop(short_name, None) table[key][long_name] = folder _update_table(modules_paths, 'mod') _update_table(core_paths, 'core') _update_table(ext_paths, 'ext') if include_whl_extensions: _update_table(whl_ext_paths, 'ext') if include_only: whl_extensions = [ mod for whl_ext_path in whl_ext_paths for mod in include_only if mod in whl_ext_path ] if whl_extensions: err = 'extension(s): [ {} ] installed from a wheel may need --include-whl-extensions option'.format( ', '.join(whl_extensions)) raise CLIError(err) raise CLIError('unrecognized modules: [ {} ]'.format( ', '.join(include_only))) return table
def get_name_index(invert=False, include_whl_extensions=False): """ Returns a dictionary containing the long and short names of modules and extensions is {SHORT:LONG} format or {LONG:SHORT} format when invert=True. """ config = get_azure_config() # pylint: disable=import-error try: EXTENSIONS_DIR = config.get(const.EXT_SECTION, const.AZ_DEV_SRC) except (configparser.NoSectionError, configparser.NoOptionError): EXTENSIONS_DIR = "" table = {} cli_repo_path = get_cli_repo_path() # unified azure-cli package (2.0.68 and later) paths = os.path.normcase( os.path.join(cli_repo_path, 'src', 'azure-cli', 'azure', 'cli', 'command_modules', '*', '__init__.py')) modules_paths = glob(paths) core_paths = glob( os.path.normcase(os.path.join(cli_repo_path, 'src', '*', 'setup.py'))) ext_paths = [ x for x in find_files(EXTENSIONS_DIR, '*.*-info') if 'site-packages' not in x ] whl_ext_paths = [] if include_whl_extensions and EXTENSIONS_DIR: whl_ext_paths = [ x for x in find_files(EXTENSIONS_DIR, '*.*-info') if 'site-packages' not in x ] def _update_table(paths, key): folder = None long_name = None short_name = None for path in paths: folder = os.path.dirname(path) base_name = os.path.basename(folder) # determine long-names if key == 'ext': short_name = base_name for item in os.listdir(folder): if item.startswith(const.EXTENSION_PREFIX): long_name = item break elif base_name.startswith(const.COMMAND_MODULE_PREFIX): long_name = base_name short_name = base_name.replace(const.COMMAND_MODULE_PREFIX, '') or '__main__' else: short_name = base_name long_name = '{}{}'.format(const.COMMAND_MODULE_PREFIX, base_name) if not invert: table[short_name] = long_name else: table[long_name] = short_name _update_table(modules_paths, 'mod') _update_table(core_paths, 'core') _update_table(ext_paths, 'ext') _update_table(whl_ext_paths, 'ext') return table