Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add help docs; some refacoring
  • Loading branch information
shenglol committed Feb 8, 2021
commit 266f5ecac396524b0e7ceb8d05eb3d72d8c5aadf
62 changes: 34 additions & 28 deletions src/azure-cli/azure/cli/command_modules/resource/_bicep.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,43 @@
_logger = get_logger(__name__)


def run_bicep_command(*args):
bicep_executable_path = ensure_bicep_installation()
return _run_command(bicep_executable_path, *args)
def run_bicep_command(args, auto_install=True, check_upgrade=True):
installation_path = _get_bicep_installation_path(platform.system())
installed = os.path.isfile(installation_path)

if not installed and not auto_install:
raise CLIError('Bicep CLI not found. Install it now by running "az bicep install".')

def ensure_bicep_installation(release_tag=None, check_upgrade=True):
if installed and check_upgrade:
with suppress(CLIError):
# Checking upgrade should not raise CLIError.
# Users may continue using the current installed version.
installed_version = _get_bicep_installed_version(installation_path)
latest_release_tag = get_bicep_latest_release_tag()
latest_version = _extract_semver(get_bicep_latest_release_tag())
if installed_version and latest_version and semver.compare(installed_version, latest_version) < 0:
_logger.warning(
'A new Bicep release is available: %s. Upgrade now by running "az bicep upgrade".',
latest_release_tag,
)

ensure_bicep_installation()

return _run_command(installation_path, args)


def ensure_bicep_installation(release_tag=None):
system = platform.system()
installation_path = _get_bicep_installation_path(system)

if os.path.isfile(installation_path):
if release_tag:
installed_version = _get_bicep_installed_version(installation_path)
target_version = _extract_semver(release_tag)
if installed_version and target_version and semver.compare(installed_version, target_version) == 0:
# The requested version is already installed.
return installation_path
else:
if check_upgrade:
with suppress(CLIError):
# Checking upgrade should not raise CLIError.
# Users may continue using the current installed version.
installed_version = _get_bicep_installed_version(installation_path)
latest_release_tag = get_bicep_latest_release_tag()
latest_version = _extract_semver(get_bicep_latest_release_tag())
if installed_version and latest_version and semver.compare(installed_version, latest_version) < 0:
_logger.warning(
'A new Bicep release is available: %s. Upgrade now by running "az bicep upgrade".',
latest_release_tag,
)
return installation_path
if not release_tag:
return

installed_version = _get_bicep_installed_version(installation_path)
target_version = _extract_semver(release_tag)
if installed_version and target_version and semver.compare(installed_version, target_version) == 0:
Copy link
Member

@jiasli jiasli May 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shenglol, if bicep has no fancy versioning schema and complies with PEP 440, can semver be replaced by packaging.version? packaging.version is used by az extension (#17667):

def ext_compat_with_cli(azext_metadata):
from azure.cli.core import __version__ as core_version
from packaging.version import parse
is_compatible, min_required, max_required = (True, None, None)
if azext_metadata:
min_required = azext_metadata.get(EXT_METADATA_MINCLICOREVERSION)
max_required = azext_metadata.get(EXT_METADATA_MAXCLICOREVERSION)
parsed_cli_version = parse(core_version)
if min_required and parsed_cli_version < parse(min_required):
is_compatible = False
elif max_required and parsed_cli_version > parse(max_required):
is_compatible = False

This will help us reduce Azure CLI’s packaging maintenance cost (#26523).

return

installation_dir = os.path.dirname(installation_path)
if not os.path.exists(installation_dir):
Expand All @@ -64,7 +71,6 @@ def ensure_bicep_installation(release_tag=None, check_upgrade=True):
f.write(request.read())

print(f'Successfully installed Bicep CLI to "{installation_path}".')
return installation_path
except IOError as err:
raise CLIError(f"Error while attempting to download Bicep CLI: {err}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change those CLIError to some more specific error?
Please refer to azure.cli.core.azclierror

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed them to more specific error types.


Expand All @@ -90,7 +96,7 @@ def get_bicep_latest_release_tag():


def _get_bicep_installed_version(bicep_executable_path):
installed_version_output = _run_command(bicep_executable_path, "--version")
installed_version_output = _run_command(bicep_executable_path, ["--version"])
return _extract_semver(installed_version_output)


Expand Down Expand Up @@ -123,8 +129,8 @@ def _extract_semver(text):
return semver_match.group(0) if semver_match else None


def _run_command(executable_path, *args):
process = subprocess.run([rf"{executable_path}"] + list(args), capture_output=True)
def _run_command(bicep_installation_path, args):
process = subprocess.run([rf"{bicep_installation_path}"] + args, capture_output=True)

try:
process.check_returncode()
Expand Down
63 changes: 49 additions & 14 deletions src/azure-cli/azure/cli/command_modules/resource/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand All @@ -278,7 +278,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -392,7 +392,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -420,7 +420,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -456,7 +456,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -564,7 +564,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -594,7 +594,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -640,7 +640,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicpe file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -750,7 +750,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -780,7 +780,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -815,7 +815,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -920,7 +920,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -948,7 +948,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -981,7 +981,7 @@
Parameters may be supplied from a file using the `@{path}` syntax, a JSON string, or as <KEY=VALUE> pairs. Parameters are evaluated in order, so when a value is assigned twice, the latter value will be used.
It is recommended that you supply your parameters file first, and then override selectively using KEY=VALUE syntax.
- name: --template-file -f
short-summary: The path to the template file.
short-summary: The path to the template file or Bicep file.
- name: --template-uri -u
short-summary: The URI to the template file.
- name: --template-spec -s
Expand Down Expand Up @@ -2297,3 +2297,38 @@
- name: List all versions of parent template spec.
text: az ts list -g MyResourceGroup -n TemplateSpecName
"""

helps['bicep install'] = """
type: command
short-summary: Install Bicep CLI.
examples:
- name: Install Bicep CLI.
text: az bicep install
- name: Install a specific version of Bicep CLI.
text: az bicep install --version v0.2.212
"""

helps['bicep upgrade'] = """
type: command
short-summary: Upgrade Bicep CLI to the latest version.
"""

helps['bicep build'] = """
type: command
short-summary: Build a Bicep file.
examples:
- name: Build a Bicep file.
text: az bicep build --file <bicep_file>
- name: Build a Bicep file and prints all output to stdout.
text: az bicep build --file <bicep_file> --stdout
"""

helps['bicep version'] = """
type: command
short-summary: Show the installed version of Bicep CLI.
"""

helps['bicep list-versions'] = """
type: command
short-summary: List out all available versions of Bicep CLI.
"""
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def load_arguments(self, _):
deployment_create_name_type = CLIArgumentType(options_list=['--name', '-n'], required=False, help='The deployment name. Default to template file base name')
management_group_id_type = CLIArgumentType(options_list=['--management-group-id', '-m'], required=True, help='The management group id.')
deployment_template_file_type = CLIArgumentType(options_list=['--template-file', '-f'], completer=FilesCompleter(), type=file_type,
help="a template file path in the file system")
help="a path to a template file or Bicep file in the file system")
deployment_template_uri_type = CLIArgumentType(options_list=['--template-uri', '-u'], help='a uri to a remote template file')
deployment_template_spec_type = CLIArgumentType(options_list=['--template-spec', '-s'], is_preview=True, min_api='2019-06-01', help="The template spec resource id.")
deployment_query_string_type = CLIArgumentType(options_list=['--query-string', '-q'], is_preview=True, help="The query string (a SAS token) to be used with the template-uri in the case of linked templates.")
Expand Down
12 changes: 6 additions & 6 deletions src/azure-cli/azure/cli/command_modules/resource/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ def _deploy_arm_template_core_unmodified(cmd, resource_group_name, template_file
template_obj = _remove_comments_from_json(_urlretrieve(template_uri).decode('utf-8'), file_path=template_uri)
else:
template_content = (
run_bicep_command("build", "--stdout", template_file)
run_bicep_command(["build", "--stdout", template_file])
if is_bicep_file(template_file)
else read_file_content(template_file)
)
Expand Down Expand Up @@ -896,7 +896,7 @@ def _prepare_deployment_properties_unmodified(cmd, template_file=None, template_
template_obj = show_resource(cmd=cmd, resource_ids=[template_spec]).properties['template']
else:
template_content = (
run_bicep_command("build", "--stdout", template_file)
run_bicep_command(["build", "--stdout", template_file])
if is_bicep_file(template_file)
else read_file_content(template_file)
)
Expand Down Expand Up @@ -3181,20 +3181,20 @@ def _resolve_api_version_by_id(rcf, resource_id, latest_include_preview=False):

def install_bicep_cli(cmd, version=None):
# The parameter version is actually a git tag here.
ensure_bicep_installation(release_tag=version, check_upgrade=False)
ensure_bicep_installation(release_tag=version)

def upgrade_bicep_cli(cmd):
latest_release_tag = get_bicep_latest_release_tag()
ensure_bicep_installation(release_tag=latest_release_tag)

def build_bicep_file(cmd, file, stdout=None):
if stdout:
print(run_bicep_command("build", file, "--stdout"))
print(run_bicep_command(["build", file, "--stdout"]))
else:
run_bicep_command("build", file)
run_bicep_command(["build", file])

def show_bicep_cli_version(cmd):
print(run_bicep_command("--version"))
print(run_bicep_command(["--version"], auto_install=False))

def list_bicep_cli_versions(cmd):
return get_bicep_available_release_tags()
Original file line number Diff line number Diff line change
@@ -1,45 +1,53 @@
import unittest
import mock

from azure.cli.command_modules.resource._bicep import ensure_bicep_installation
from knack.util import CLIError
from azure.cli.command_modules.resource._bicep import ensure_bicep_installation, run_bicep_command


class TestBicep(unittest.TestCase):
@mock.patch("os.path.isfile")
def test_run_bicep_command_raise_error_if_not_installed_and_not_auto_install(self, isfile_stub):
isfile_stub.return_value = False

with self.assertRaisesRegex(CLIError, 'Bicep CLI not found. Install it now by running "az bicep install".'):
run_bicep_command(["--version"], auto_install=False)

@mock.patch("azure.cli.command_modules.resource._bicep._logger.warning")
@mock.patch("azure.cli.command_modules.resource._bicep._run_command")
@mock.patch("azure.cli.command_modules.resource._bicep.ensure_bicep_installation")
@mock.patch("azure.cli.command_modules.resource._bicep.get_bicep_latest_release_tag")
@mock.patch("azure.cli.command_modules.resource._bicep._get_bicep_installed_version")
@mock.patch("azure.cli.command_modules.resource._bicep._logger.warning")
def test_ensure_bicep_installation_check_upgrade(
self, warning_mock, _get_bicep_installed_version_stub, get_bicep_latest_release_tag_stub, isfile_stub
@mock.patch("os.path.isfile")
def test_run_bicep_command_check_upgrade(
self,
isfile_stub,
_get_bicep_installed_version_stub,
get_bicep_latest_release_tag_stub,
ensure_bicep_installation_mock,
_run_command_mock,
warning_mock,
):
# Arrange.
isfile_stub.return_value = True
_get_bicep_installed_version_stub.return_value = "1.0.0"
get_bicep_latest_release_tag_stub.return_value = "v2.0.0"
isfile_stub.return_value = True

# Act.
installation_path = ensure_bicep_installation(check_upgrade=True)
run_bicep_command(["--version"], check_upgrade=True)

# Assert.
warning_mock.assert_called_once_with(
'A new Bicep release is available: %s. Upgrade now by running "az bicep upgrade".',
"v2.0.0",
)
self.assertIsNotNone(installation_path)

@mock.patch("os.path.isfile")
@mock.patch("azure.cli.command_modules.resource._bicep._get_bicep_installed_version")
@mock.patch("os.path.dirname")
def test_ensure_bicep_installation_skip_download_if_installed_version_matches_release_tag(
self, dirname_mock, _get_bicep_installed_version_stub, isfile_stub
):
# Arrange.
_get_bicep_installed_version_stub.return_value = "0.1.0"
isfile_stub.return_value = True

# Act.
installation_path = ensure_bicep_installation(release_tag='v0.1.0')
ensure_bicep_installation(release_tag="v0.1.0")

# Assert
dirname_mock.assert_not_called()
self.assertIsNotNone(installation_path)
dirname_mock.assert_not_called()