|
9 | 9 | import json |
10 | 10 | import os |
11 | 11 | import re |
| 12 | +import subprocess |
12 | 13 |
|
13 | 14 | from knack.log import get_logger |
14 | 15 | from knack.prompting import prompt_y_n, prompt |
15 | 16 | from knack.util import CLIError |
16 | 17 |
|
| 18 | +import azdev.utilities.const as const |
| 19 | + |
17 | 20 | from azdev.utilities import ( |
18 | | - pip_cmd, display, heading, COMMAND_MODULE_PREFIX, EXTENSION_PREFIX, get_cli_repo_path, get_ext_repo_paths, |
19 | | - find_files) |
| 21 | + pip_cmd, shell_cmd, display, heading, COMMAND_MODULE_PREFIX, EXTENSION_PREFIX, get_cli_repo_path, |
| 22 | + get_ext_repo_paths, find_files, require_virtual_env) |
| 23 | + |
| 24 | +from urllib import request, error |
| 25 | + |
20 | 26 |
|
21 | 27 | logger = get_logger(__name__) |
22 | 28 |
|
@@ -55,24 +61,26 @@ def create_module(mod_name='test', display_name=None, display_name_plural=None, |
55 | 61 | _display_success_message(COMMAND_MODULE_PREFIX + mod_name, mod_name) |
56 | 62 |
|
57 | 63 |
|
58 | | -def create_extension(ext_name='test', repo_name='azure-cli-extensions', |
59 | | - display_name=None, display_name_plural=None, |
60 | | - required_sdk=None, client_name=None, operation_name=None, sdk_property=None, |
61 | | - not_preview=False, github_alias=None, local_sdk=None): |
62 | | - repo_path = None |
| 64 | +def create_extension(ext_name, azure_rest_api_specs=const.GITHUB_SWAGGER_REPO_URL, branch=None, use=None): |
| 65 | + if not azure_rest_api_specs.startswith('http') and branch: |
| 66 | + raise CLIError('Cannot specify azure-rest-api-specs repo branch when using local one.') |
| 67 | + if not branch: |
| 68 | + branch = json.load( |
| 69 | + request.urlopen('https://api.github.com/repos/Azure/azure-rest-api-specs')).get('default_branch') |
| 70 | + require_virtual_env() |
63 | 71 | repo_paths = get_ext_repo_paths() |
64 | | - repo_path = next((x for x in repo_paths if x.endswith(repo_name)), None) |
65 | | - |
| 72 | + repo_path = next( |
| 73 | + (x for x in repo_paths if x.endswith(const.EXT_REPO_NAME) or x.endswith(const.EXT_REPO_NAME + '\\')), None) |
66 | 74 | if not repo_path: |
67 | 75 | raise CLIError('Unable to find `{}` repo. Have you cloned it and added ' |
68 | | - 'with `azdev extension repo add`?'.format(repo_name)) |
69 | | - |
70 | | - _create_package(EXTENSION_PREFIX, os.path.join(repo_path, 'src'), True, ext_name, display_name, |
71 | | - display_name_plural, required_sdk, client_name, operation_name, sdk_property, not_preview, |
72 | | - local_sdk) |
73 | | - _add_to_codeowners(repo_path, EXTENSION_PREFIX, ext_name, github_alias) |
| 76 | + 'with `azdev extension repo add`?'.format(const.EXT_REPO_NAME)) |
| 77 | + if not os.path.isdir(repo_path): |
| 78 | + raise CLIError("Invalid path {} in .azure config.".format(repo_path)) |
| 79 | + swagger_readme_file_path = _get_swagger_readme_file_path(ext_name, azure_rest_api_specs, branch) |
| 80 | + _generate_extension(ext_name, repo_path, swagger_readme_file_path, use) |
| 81 | + _add_extension(ext_name, repo_path) |
74 | 82 |
|
75 | | - _display_success_message(EXTENSION_PREFIX + ext_name, ext_name) |
| 83 | + _display_success_message(ext_name, ext_name) |
76 | 84 |
|
77 | 85 |
|
78 | 86 | def _display_success_message(package_name, group_name): |
@@ -300,3 +308,75 @@ def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, d |
300 | 308 | result = pip_cmd('install -e {}'.format(new_package_path), "Installing `{}{}`...".format(prefix, name)) |
301 | 309 | if result.error: |
302 | 310 | raise result.error # pylint: disable=raising-bad-type |
| 311 | + |
| 312 | + |
| 313 | +def _get_swagger_readme_file_path(ext_name, swagger_repo, branch): |
| 314 | + swagger_readme_file_path = None |
| 315 | + if swagger_repo == const.GITHUB_SWAGGER_REPO_URL or \ |
| 316 | + (swagger_repo.startswith('https://') and swagger_repo.endswith('azure-rest-api-specs')): |
| 317 | + swagger_readme_file_path = '{}/blob/{}/specification/{}/resource-manager'.format( |
| 318 | + swagger_repo, branch, ext_name) |
| 319 | + # validate URL |
| 320 | + try: |
| 321 | + request.urlopen(swagger_readme_file_path) |
| 322 | + except error.HTTPError as ex: |
| 323 | + raise CLIError( |
| 324 | + 'HTTPError: {}\nNo swagger readme file found in this URL: {}'.format(ex.code, swagger_readme_file_path)) |
| 325 | + else: |
| 326 | + swagger_readme_file_path = os.path.join(swagger_repo, 'specification', ext_name, 'resource-manager') |
| 327 | + if not os.path.isdir(swagger_readme_file_path): |
| 328 | + raise CLIError("The path {} does not exist.".format(swagger_readme_file_path)) |
| 329 | + return swagger_readme_file_path |
| 330 | + |
| 331 | + |
| 332 | +# pylint: disable=too-many-statements |
| 333 | +def _generate_extension(ext_name, repo_path, swagger_readme_file_path, use): |
| 334 | + heading('Start generating extension {}.'.format(ext_name)) |
| 335 | + # check if npm is installed |
| 336 | + try: |
| 337 | + shell_cmd('npm --version', stdout=subprocess.DEVNULL, raise_ex=False) |
| 338 | + except CLIError as ex: |
| 339 | + raise CLIError('{}\nPlease install npm.'.format(ex)) |
| 340 | + display('Installing autorest...\n') |
| 341 | + if const.IS_WINDOWS: |
| 342 | + try: |
| 343 | + shell_cmd('npm install -g autorest', raise_ex=False) |
| 344 | + except CLIError as ex: |
| 345 | + raise CLIError("Failed to install autorest.\n{}".format(ex)) |
| 346 | + else: |
| 347 | + try: |
| 348 | + shell_cmd('npm install -g autorest', stderr=subprocess.DEVNULL, raise_ex=False) |
| 349 | + except CLIError as ex: |
| 350 | + path = os.environ['PATH'] |
| 351 | + # check if npm is installed through nvm |
| 352 | + if os.environ.get('NVM_DIR'): |
| 353 | + raise ex |
| 354 | + # check if user using specific node version and manually add it to the os env PATH |
| 355 | + node_version = shell_cmd('node --version', capture_output=True).result |
| 356 | + if 'node/' + node_version + '/bin' in path: |
| 357 | + raise ex |
| 358 | + # create a new directory for npm global installations, to avoid using sudo in installing autorest |
| 359 | + npm_path = os.path.join(os.environ['HOME'], '.npm-packages') |
| 360 | + if not os.path.isdir(npm_path): |
| 361 | + os.mkdir(npm_path) |
| 362 | + npm_prefix = shell_cmd('npm prefix -g', capture_output=True).result |
| 363 | + shell_cmd('npm config set prefix ' + npm_path) |
| 364 | + os.environ['PATH'] = path + ':' + os.path.join(npm_path, 'bin') |
| 365 | + os.environ['MANPATH'] = os.path.join(npm_path, 'share', 'man') |
| 366 | + shell_cmd('npm install -g autorest') |
| 367 | + shell_cmd('npm config set prefix ' + npm_prefix) |
| 368 | + # update autorest core |
| 369 | + shell_cmd('autorest --latest') |
| 370 | + if not use: |
| 371 | + cmd = 'autorest --az --azure-cli-extension-folder={} {}'.format(repo_path, swagger_readme_file_path) |
| 372 | + else: |
| 373 | + cmd = 'autorest --az --azure-cli-extension-folder={} {} --use={}'.format( |
| 374 | + repo_path, swagger_readme_file_path, use) |
| 375 | + shell_cmd(cmd, message=True) |
| 376 | + |
| 377 | + |
| 378 | +def _add_extension(ext_name, repo_path): |
| 379 | + new_package_path = os.path.join(repo_path, 'src', ext_name) |
| 380 | + result = pip_cmd('install -e {}'.format(new_package_path), "Adding extension `{}`...".format(new_package_path)) |
| 381 | + if result.error: |
| 382 | + raise result.error |
0 commit comments