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
예제 #2
0
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()
예제 #4
0
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))
예제 #5
0
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
예제 #6
0
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
예제 #8
0
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
예제 #9
0
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
예제 #10
0
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