Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #272 Splitting repo paths on comma rather than whitespace #273

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions azdev.pyproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<Compile Include="azdev\operations\__init__.py" />
<Compile Include="azdev\params.py" />
<Compile Include="azdev\utilities\config.py" />
<Compile Include="azdev\utilities\venv.py" />
<Compile Include="azdev\utilities\const.py">
<SubType>Code</SubType>
</Compile>
Expand Down
27 changes: 19 additions & 8 deletions azdev/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,31 @@
helps['setup'] = """
short-summary: Set up your environment for development of Azure CLI command modules and/or extensions.
examples:
- name: Fully interactive setup.
- name: Fully interactive setup (Must be run in an existing virtual environment).
text: azdev setup

- name: Install only the CLI in dev mode and search for the existing repo.
text: azdev setup -c
- name: Install CLI and setup an extensions repo in an existing virtual environment. Will create a azure directory and config in the current virtual environment.
Note the existing virtual environment could created by VENV or PYENV.
text: azdev setup -c azure-cli -r azure-cli-extensions

- name: Install public CLI and setup an extensions repo. Do not install any extensions.
- name: Same as above, but install the `alias` extension in the existing virtual environment too.
text: azdev setup -c azure-cli -r azure-cli-extensions -e alias

- name: Same as above, but will use the CLI repo path in local .azdev config, or the one in global .azdev config if not found the local one.
text: azdev setup -r azure-cli-extensions

- name: Install CLI in dev mode, along with the extensions repo. Auto-find the CLI repo and install the `alias` extension in dev mode.
text: azdev setup -c -r azure-cli-extensions -e alias
- name: Same as above, but only install CLI without setup an extensions repo.
text: azdev setup -c azure-cli

- name: Install CLI and setup an extensions repo in a new virtual environment. Will create a azure directory and config in the current virtual environment.
Note -s is using VENV to create a new virtual environment, should un-install PYENV if you have.
text: azdev setup -c azure-cli -r azure-cli-extensions -s env1

- name: Same as above, but do not setup new azure directory and config in this virtual environment
text: azdev setup -c azure-cli -r azure-cli-extensions -s env1 -g

- name: Install only the CLI in dev mode and resolve dependencies from setup.py.
text: azdev setup -c -d setup.py
- name: Same as above, but copy over system level azure settings into new virtual environment azure settings
text: azdev setup -c azure-cli -r azure-cli-extensions -s env1 --copy
"""


Expand Down
3 changes: 2 additions & 1 deletion azdev/operations/help/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
from azure.cli.core.extension.operations import list_available_extensions, list_extensions as list_cli_extensions # pylint: disable=import-error
from azdev.utilities import (
display, heading, subheading,
get_cli_repo_path, get_path_table
get_cli_repo_path, get_path_table,
require_virtual_env
)

from azdev.utilities.tools import require_azure_cli
Expand Down
4 changes: 3 additions & 1 deletion azdev/operations/pypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

from azdev.utilities import (
display, heading, subheading, cmd, py_cmd, get_path_table,
pip_cmd, COMMAND_MODULE_PREFIX, require_azure_cli, find_files)
pip_cmd, COMMAND_MODULE_PREFIX, require_azure_cli, require_virtual_env,
find_files)

logger = get_logger(__name__)

Expand Down Expand Up @@ -131,6 +132,7 @@ def verify_versions():
import tempfile
import shutil

require_virtual_env()
require_azure_cli()

heading('Verify CLI Versions')
Expand Down
180 changes: 169 additions & 11 deletions azdev/operations/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@

import os
from shutil import copytree, rmtree
import shutil
import time
import sys

from knack.log import get_logger
from knack.util import CLIError

from azdev.operations.extensions import (
list_extensions, add_extension_repo, remove_extension)
from azdev.params import Flag
import azdev.utilities.const as const
import azdev.utilities.venv as venv
from azdev.utilities import (
display, heading, subheading, pip_cmd, find_file,
get_azdev_config_dir, get_azdev_config, require_virtual_env, get_azure_config)
display, heading, subheading, pip_cmd, find_file, get_env_path,
get_azdev_config_dir, get_azdev_config, get_azure_config, shell_cmd)

logger = get_logger(__name__)

Expand Down Expand Up @@ -196,8 +200,8 @@ def add_ext_repo(path):
# repo directory. To use multiple extension repos or identify a repo outside the cwd, they must specify
# the path.
if prompt_y_n('\nDo you plan to develop CLI extensions?'):
display('\nGreat! Input the paths for the extension repos you wish to develop for, one per '
'line. You can add as many repos as you like. (TIP: to quickly get started, press RETURN to '
display('\nGreat! Input the path for the extension repos you wish to develop for. '
'(TIP: to quickly get started, press RETURN to '
'use your current working directory).')
first_repo = True
while True:
Expand Down Expand Up @@ -245,14 +249,170 @@ def add_ext_repo(path):
raise CLIError('Installation aborted.')


def setup(cli_path=None, ext_repo_path=None, ext=None, deps=None):
def _validate_input(cli_path, ext_repo_path, set_env, copy, use_global, ext):
if copy and use_global:
raise CLIError("Copy and use global are mutally exlcusive.")
if cli_path == "pypi" and any([use_global, copy, set_env]):
raise CLIError("pypi for cli path is mutally exlcusive with global copy and set env")
if not cli_path and any([use_global, copy, set_env]):
raise CLIError("if global, copy, or set env are set then both an extensions repo "
" and a cli repo must be specified")
if not ext_repo_path and ext:
raise CLIError("Extesions provided to be installed but no extensions path was given")

require_virtual_env()

start = time.time()
def _check_paths(cli_path, ext_repo_path):
if not os.path.isdir(cli_path):
raise CLIError("The cli path is not a valid directory, please check the path")
if ext_repo_path and not os.path.isdir(ext_repo_path):
raise CLIError("The cli extensions path is not a valid directory, please check the path")


def _check_shell():
if 'SHELL' in os.environ and const.IS_WINDOWS and 'bash.exe' in os.environ['SHELL']:
heading("WARNING: You are running bash in Windows, the setup may not work correctly and "
"command may have unexpected behavior")
from knack.prompting import prompt_y_n
if not prompt_y_n('Would you like to continue with the install?'):
sys.exit(0)


def _check_env(set_env):
if not set_env:
if not get_env_path():
raise CLIError('You are not running in a virtual enviroment and have not chosen to set one up.')
_check_pyenv()
elif 'VIRTUAL_ENV' in os.environ:
raise CLIError("You are already running in a virtual enviroment, yet you want to set a new one up")


def _check_pyenv():
if 'PYENV_VIRTUAL_ENV' in os.environ:
if const.IS_WINDOWS:
raise CLIError('AZDEV does not support setup in a pyenv-win virtual environment.')
activate_path = os.path.join(
os.environ['PYENV_ROOT'], 'plugins', 'pyenv-virtualenv', 'bin', 'pyenv-sh-activate')
venv.edit_pyenv_activate(activate_path)


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_azdev_cli_path(config_file_path):
if not os.path.exists(config_file_path):
return None

import configparser
with open(config_file_path, "r") as file:
config_parser = configparser.RawConfigParser()
config_parser.read_string(file.read())
if config_parser.has_section('cli') and config_parser.has_option('cli', 'repo_path'):
return config_parser.get('cli', 'repo_path')
return None


def _handle_no_cli_path():
local_azdev_config = os.path.join(os.environ.get('VIRTUAL_ENV'), '.azdev', const.CONFIG_NAME)
cli_path = _get_azdev_cli_path(local_azdev_config)
if cli_path is None:
display('Not found cli path in local azdev config file: ' + local_azdev_config)
display('Will use the one in global azdev config.')
global_azdev_config = os.path.expanduser(os.path.join('~', '.azdev', const.CONFIG_NAME))
cli_path = _get_azdev_cli_path(global_azdev_config)
if cli_path is None:
raise CLIError('Not found cli path in global azdev config file: ' + global_azdev_config)
display('cli_path: ' + cli_path)
return cli_path


def _handle_legacy(cli_path, ext_repo_path, ext, deps, start):
ext_repo_path = [ext_repo_path] if ext_repo_path else None
ext_to_install = []
if not any([cli_path, ext_repo_path, ext]):
cli_path, ext_repo_path, ext_to_install = _interactive_setup()
Expand All @@ -279,7 +439,6 @@ def setup(cli_path=None, ext_repo_path=None, ext=None, deps=None):
# 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)
Expand Down Expand Up @@ -313,11 +472,10 @@ def setup(cli_path=None, ext_repo_path=None, ext=None, deps=None):

# upgrade to latest pip
pip_cmd('install --upgrade pip -q', 'Upgrading pip...')

_install_cli(cli_path, deps=deps)
_install_extensions(ext_to_install)
if ext_repo_path:
_install_extensions(ext_to_install)
_copy_config_files()

end = time.time()
elapsed_min = int((end - start) / 60)
elapsed_sec = int(end - start) % 60
Expand Down
2 changes: 1 addition & 1 deletion azdev/operations/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def _config_file_path(style_type="pylint"):

ext_repo_path = filter(
lambda x: "azure-cli-extension" in x,
get_azdev_config().get("ext", "repo_paths").split(),
get_azdev_config().get("ext", "repo_paths").split(','),
)
try:
ext_repo_path = next(ext_repo_path)
Expand Down
7 changes: 5 additions & 2 deletions azdev/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ def load_arguments(self, _):
c.argument('git_repo', options_list='--repo', arg_group='Git', help='Path to the Git repo to check.')

with ArgumentsContext(self, 'setup') as c:
c.argument('cli_path', options_list=['--cli', '-c'], nargs='?', const=Flag, help="Path to an existing Azure CLI repo. Omit value to search for the repo or use special value 'EDGE' to install the latest developer edge build.")
c.argument('ext_repo_path', options_list=['--repo', '-r'], nargs='+', help='Space-separated list of paths to existing Azure CLI extensions repos.')
c.argument('cli_path', options_list=['--cli', '-c'], type=str, help="Path to an existing Azure CLI repo. Use special value 'EDGE' to install the latest developer edge build. Note: if not provide, will use the one in local .azdev config, if not exist will use the one in global .azdev config.")
c.argument('ext_repo_path', options_list=['--repo', '-r'], type=str, help='Path to existing Azure CLI extensions repos.')
c.argument('ext', options_list=['--ext', '-e'], nargs='+', help="Space-separated list of extensions to install initially. Use '*' to install all extensions.")
c.argument('deps', options_list=['--deps-from', '-d'], choices=['requirements.txt', 'setup.py'], default='requirements.txt', help="Choose the file to resolve dependencies.")
c.argument('set_env', options_list=['--set-env', '-s'], type=str, help="Will create a virtual enviroment with the given env name")
c.argument('copy', options_list='--copy', action='store_true', help="Will copy entire global .azure diretory to the newly created virtual enviroment .azure direcotry if it exist")
c.argument('use_global', options_list=['--use-global', '-g'], action='store_true', help="Will use the default global system .azure config")

with ArgumentsContext(self, 'test') as c:
c.argument('discover', options_list='--discover', action='store_true', help='Build an index of test names so that you don\'t need to specify fully qualified test paths.')
Expand Down
7 changes: 3 additions & 4 deletions azdev/utilities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@
call,
cmd,
py_cmd,
pip_cmd
pip_cmd,
shell_cmd
)
from .const import (
COMMAND_MODULE_PREFIX,
EXTENSION_PREFIX,
IS_WINDOWS,
ENV_VAR_TEST_MODULES,
ENV_VAR_TEST_LIVE,
ENV_VAR_VIRTUAL_ENV,
EXT_REPO_NAME
ENV_VAR_VIRTUAL_ENV
)
from .display import (
display,
Expand Down Expand Up @@ -76,7 +76,6 @@
'ENV_VAR_TEST_MODULES',
'ENV_VAR_TEST_LIVE',
'ENV_VAR_VIRTUAL_ENV',
'EXT_REPO_NAME',
'IS_WINDOWS',
'extract_module_name',
'find_file',
Expand Down
Loading