From 322e3ed57609cd8099debd1437ebbdba339a5968 Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Fri, 14 Mar 2025 13:44:24 -0700 Subject: [PATCH 1/7] Check use_binary_from_path in ensure_bicep_installation --- .../azure/cli/command_modules/resource/_bicep.py | 10 ++++++++++ .../resource/tests/latest/test_resource_bicep.py | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py index 2840e0bcc2c..8b66fe6332d 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py @@ -108,6 +108,16 @@ def run_bicep_command(cli_ctx, args, auto_install=True, custom_env=None): def ensure_bicep_installation(cli_ctx, release_tag=None, target_platform=None, stdout=True): + if _use_binary_from_path(cli_ctx): + from shutil import which + + if which("bicep") is None: + raise ValidationError( + 'Could not find the "bicep" executable on PATH. To install Bicep via Azure CLI, set the "bicep.use_binary_from_path" configuration to False and run "az bicep install".' # pylint: disable=line-too-long + ) + + return + system = platform.system() machine = platform.machine() installation_path = _get_bicep_installation_path(system) diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py index 49799ec539e..fd7cfac4634 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py @@ -163,6 +163,18 @@ def test_ensure_bicep_installation_skip_download_if_installed_version_matches_re ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") dirname_mock.assert_not_called() + + @mock.patch("azure.cli.command_modules.resource._bicep._get_bicep_installation_path") + @mock.patch("shutil.which") + def test_ensure_bicep_installation_skip_download_if_use_binary_from_path_is_true( + self, which_stub, _get_bicep_installation_path_mock + ): + which_stub.return_value = True + self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "true") + + ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") + + _get_bicep_installation_path_mock.assert_not_called() def test_validate_target_scope_raise_error_if_target_scope_does_not_match_deployment_scope(self): with self.assertRaisesRegex( From 85c0b435bf14f44d3418a7ad436aee96b6093485 Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Fri, 14 Mar 2025 13:57:19 -0700 Subject: [PATCH 2/7] Fix arm64 architecture detection --- .../azure/cli/command_modules/resource/_bicep.py | 10 +++++++--- .../resource/tests/latest/test_resource_bicep.py | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py index 8b66fe6332d..906b993476f 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py @@ -285,18 +285,22 @@ def _get_bicep_installed_version(bicep_executable_path): return _extract_version(installed_version_output) +def _is_arm_architecture(machine): + return machine in ("arm64", "aarch64", "aarch64_be", "armv8b", "armv8l") + + def _has_musl_library_only(): return os.path.exists("/lib/ld-musl-x86_64.so.1") and not os.path.exists("/lib/x86_64-linux-gnu/libc.so.6") def _get_bicep_download_url(system, machine, release_tag, target_platform=None): if not target_platform: - if system == "Windows" and machine == "arm64": + if system == "Windows" and _is_arm_architecture(machine): target_platform = "win-arm64" elif system == "Windows": # default to x64 target_platform = "win-x64" - elif system == "Linux" and machine == "arm64": + elif system == "Linux" and _is_arm_architecture(machine): target_platform = "linux-arm64" elif system == "Linux" and _has_musl_library_only(): # check for alpine linux @@ -304,7 +308,7 @@ def _get_bicep_download_url(system, machine, release_tag, target_platform=None): elif system == "Linux": # default to x64 target_platform = "linux-x64" - elif system == "Darwin" and machine == "arm64": + elif system == "Darwin" and _is_arm_architecture(machine): target_platform = "osx-arm64" elif system == "Darwin": # default to x64 diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py index fd7cfac4634..300fe8b1d58 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py @@ -220,6 +220,9 @@ def test_get_bicep_download_url_returns_correct_urls(self): download_url = _get_bicep_download_url("Linux", "arm64", "v0.26.54") self.assertEqual(download_url, "https://downloads.bicep.azure.com/v0.26.54/bicep-linux-arm64") + download_url = _get_bicep_download_url("Linux", "aarch64", "v0.26.54") + self.assertEqual(download_url, "https://downloads.bicep.azure.com/v0.26.54/bicep-linux-arm64") + download_url = _get_bicep_download_url("Linux", "x64", "v0.26.54") self.assertEqual(download_url, "https://downloads.bicep.azure.com/v0.26.54/bicep-linux-x64") From 6ee63a454ccfd902c7ee40d98c8a596ede891a53 Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Fri, 14 Mar 2025 15:47:35 -0700 Subject: [PATCH 3/7] Attemp to fix test flakiness --- .../tests/latest/test_resource_bicep.py | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py index 300fe8b1d58..5f2ae7544e5 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py @@ -24,18 +24,19 @@ class TestBicep(unittest.TestCase): def setUp(self): - self.cli_ctx = DummyCli(random_config_dir=True) + self.default_cli = DummyCli(random_config_dir=True) @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 + cli = DummyCli(random_config_dir=True) with contextlib.suppress(FileNotFoundError): - remove_bicep_installation(self.cli_ctx) + remove_bicep_installation(cli) - self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "false") + cli.config.set_value("bicep", "use_binary_from_path", "false") with self.assertRaisesRegex(CLIError, 'Bicep CLI not found. Install it now by running "az bicep install".'): - run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) + run_bicep_command(cli, ["--version"], auto_install=False) @mock.patch("os.chmod") @mock.patch("os.stat") @@ -62,22 +63,21 @@ def test_use_bicep_cli_from_path_false_after_install(self, isfile_stub, dirname_ response.read.return_value = b"test" urlopen_stub.return_value = response - ensure_bicep_installation(self.cli_ctx, release_tag="v0.14.85", stdout=False) + ensure_bicep_installation(self.default_cli, release_tag="v0.14.85", stdout=False) - self.assertTrue(self.cli_ctx.config.get("bicep", "use_binary_from_path") == "false") + self.assertTrue(self.default_cli.config.get("bicep", "use_binary_from_path") == "false") @mock.patch("shutil.which") def test_run_bicep_command_raise_error_if_bicep_cli_not_found_when_use_binary_from_path_is_true(self, which_stub): which_stub.return_value = None - self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "true") + cli = DummyCli(random_config_dir=True) + cli.config.set_value("bicep", "use_binary_from_path", "true") with self.assertRaisesRegex( CLIError, 'Could not find the "bicep" executable on PATH. To install Bicep via Azure CLI, set the "bicep.use_binary_from_path" configuration to False and run "az bicep install".', ): - run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) - - self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "false") + run_bicep_command(cli, ["--version"], auto_install=False) @mock.patch.dict(os.environ, {"GITHUB_ACTIONS": "true"}, clear=True) @mock.patch("azure.cli.command_modules.resource._bicep._logger.debug") @@ -86,9 +86,10 @@ def test_run_bicep_command_raise_error_if_bicep_cli_not_found_when_use_binary_fr def test_run_bicep_command_use_bicep_cli_from_path_in_ci(self, which_stub, run_command_stub, debug_mock): which_stub.return_value = True run_command_stub.return_value = "Bicep CLI version 0.13.1 (e3ac80d678)" - self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "if_found_in_ci") + cli = DummyCli(random_config_dir=True) + cli.config.set_value("bicep", "use_binary_from_path", "if_found_in_ci") - run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) + run_bicep_command(cli, ["--version"], auto_install=False) debug_mock.assert_called_with( "Using Bicep CLI from PATH. %s", @@ -114,9 +115,10 @@ def test_run_bicep_command_check_version( _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("1.0.0") get_bicep_latest_release_tag_stub.return_value = "v2.0.0" - self.cli_ctx.config.set_value("bicep", "check_version", "True") - self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "false") - run_bicep_command(self.cli_ctx, ["--version"]) + cli = DummyCli(random_config_dir=True) + cli.config.set_value("bicep", "check_version", "True") + cli.config.set_value("bicep", "use_binary_from_path", "false") + run_bicep_command(cli, ["--version"]) warning_mock.assert_called_once_with( 'A new Bicep release is available: %s. Upgrade now by running "az bicep upgrade".', @@ -145,7 +147,7 @@ def test_run_bicep_command_check_version_cache_read_write( _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("1.0.0") get_bicep_latest_release_tag_stub.return_value = "v2.0.0" - run_bicep_command(self.cli_ctx, ["--version"]) + run_bicep_command(self.default_cli, ["--version"]) self.assertTrue(os.path.isfile(_bicep_version_check_file_path)) finally: @@ -160,7 +162,7 @@ def test_ensure_bicep_installation_skip_download_if_installed_version_matches_re _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("0.1.0") isfile_stub.return_value = True - ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") + ensure_bicep_installation(self.default_cli, release_tag="v0.1.0") dirname_mock.assert_not_called() @@ -170,9 +172,10 @@ def test_ensure_bicep_installation_skip_download_if_use_binary_from_path_is_true self, which_stub, _get_bicep_installation_path_mock ): which_stub.return_value = True - self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "true") + cli = DummyCli(random_config_dir=True) + cli.config.set_value("bicep", "use_binary_from_path", "true") - ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") + ensure_bicep_installation(cli, release_tag="v0.1.0") _get_bicep_installation_path_mock.assert_not_called() From fd7268de54eec17e2e814c316b5f510ccd987aca Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Fri, 14 Mar 2025 17:21:05 -0700 Subject: [PATCH 4/7] Attemp to fix flaky tests with mocks --- .../cli/command_modules/resource/_bicep.py | 22 +++--- .../command_modules/resource/_bicep_config.py | 19 ++++++ .../tests/latest/test_resource_bicep.py | 68 ++++++++++++------- 3 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py index 906b993476f..da1311deda4 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py @@ -30,6 +30,13 @@ ) from azure.cli.core.util import should_disable_connection_verify +from ._bicep_config import ( + get_check_version_config, + get_use_binary_from_path_config, + remove_use_binary_from_path_config, + set_use_binary_from_path_config +) + # See: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string _semver_pattern = r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?" # pylint: disable=line-too-long @@ -48,7 +55,6 @@ _requests_verify = not should_disable_connection_verify() - def validate_bicep_target_scope(template_schema, deployment_scope): target_scope = _template_schema_to_target_scope(template_schema) if target_scope != deployment_scope: @@ -78,7 +84,7 @@ def run_bicep_command(cli_ctx, args, auto_install=True, custom_env=None): installed = os.path.isfile(installation_path) _logger.debug("Bicep CLI installed: %s.", installed) - check_version = cli_ctx.config.getboolean("bicep", "check_version", True) + check_version = get_check_version_config(cli_ctx) if not installed: if auto_install: @@ -155,10 +161,10 @@ def ensure_bicep_installation(cli_ctx, release_tag=None, target_platform=None, s os.chmod(installation_path, os.stat(installation_path).st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) - use_binary_from_path = cli_ctx.config.get("bicep", "use_binary_from_path", "if_found_in_ci").lower() + use_binary_from_path = get_use_binary_from_path_config(cli_ctx) if use_binary_from_path not in ["0", "no", "false", "off"]: _logger.warning("The configuration value of bicep.use_binary_from_path has been set to 'false'.") - cli_ctx.config.set_value("bicep", "use_binary_from_path", "false") + set_use_binary_from_path_config(cli_ctx, "false") if stdout: print(f'Successfully installed Bicep CLI to "{installation_path}".') @@ -180,10 +186,10 @@ def remove_bicep_installation(cli_ctx): if os.path.exists(_bicep_version_check_file_path): os.remove(_bicep_version_check_file_path) - use_binary_from_path = cli_ctx.config.get("bicep", "use_binary_from_path", "if_found_in_ci").lower() + use_binary_from_path = get_use_binary_from_path_config(cli_ctx) if use_binary_from_path in ["0", "no", "false", "off"]: _logger.warning("The configuration value of bicep.use_binary_from_path has been reset") - cli_ctx.config.remove_option("bicep", "use_binary_from_path") + remove_use_binary_from_path_config(cli_ctx) def is_bicep_file(file_path): @@ -233,7 +239,7 @@ def _bicep_installed_in_ci(): def _use_binary_from_path(cli_ctx): - use_binary_from_path = cli_ctx.config.get("bicep", "use_binary_from_path", "if_found_in_ci").lower() + use_binary_from_path = get_use_binary_from_path_config(cli_ctx) _logger.debug('Current value of "use_binary_from_path": %s.', use_binary_from_path) @@ -388,4 +394,4 @@ def _template_schema_to_target_scope(template_schema): return "managementGroup" if template_type_lower == "tenantdeployment": return "tenant" - return None + return None \ No newline at end of file diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py new file mode 100644 index 00000000000..c4830e42cb0 --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py @@ -0,0 +1,19 @@ +_config_section = "bicep" + +_use_binary_from_path_config_key = "use_binary_from_path" +_use_binary_from_path_config_default_value = "if_found_in_ci" + +_check_version_config_key = "check_version" +_check_version_config_default_value = True + +def get_use_binary_from_path_config(cli_ctx): + return cli_ctx.config.get(_config_section, _use_binary_from_path_config_key, _use_binary_from_path_config_default_value).lower() + +def set_use_binary_from_path_config(cli_ctx, value): + cli_ctx.config.set_value(_config_section, _use_binary_from_path_config_key, value) + +def remove_use_binary_from_path_config(cli_ctx): + cli_ctx.config.remove_option(_config_section, _use_binary_from_path_config_key) + +def get_check_version_config(cli_ctx): + return cli_ctx.config.getboolean(_config_section, _check_version_config_key, _check_version_config_default_value) diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py index 5f2ae7544e5..37553c96ca6 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py @@ -24,20 +24,21 @@ class TestBicep(unittest.TestCase): def setUp(self): - self.default_cli = DummyCli(random_config_dir=True) + self.cli_ctx = DummyCli(random_config_dir=True) @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 - cli = DummyCli(random_config_dir=True) with contextlib.suppress(FileNotFoundError): - remove_bicep_installation(cli) + remove_bicep_installation(self.cli_ctx) - cli.config.set_value("bicep", "use_binary_from_path", "false") with self.assertRaisesRegex(CLIError, 'Bicep CLI not found. Install it now by running "az bicep install".'): - run_bicep_command(cli, ["--version"], auto_install=False) + run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) + + @mock.patch("azure.cli.command_modules.resource._bicep.set_use_binary_from_path_config") + @mock.patch("azure.cli.command_modules.resource._bicep.get_use_binary_from_path_config") @mock.patch("os.chmod") @mock.patch("os.stat") @mock.patch("io.BufferedWriter") @@ -46,7 +47,8 @@ def test_run_bicep_command_raise_error_if_not_installed_and_not_auto_install(sel @mock.patch("os.path.exists") @mock.patch("os.path.dirname") @mock.patch("os.path.isfile") - def test_use_bicep_cli_from_path_false_after_install(self, isfile_stub, dirname_stub, exists_stub, urlopen_stub, open_stub, buffered_writer_stub, stat_stub, chmod_stub): + def test_use_bicep_cli_from_path_false_after_install(self, isfile_stub, dirname_stub, exists_stub, urlopen_stub, open_stub, buffered_writer_stub, stat_stub, chmod_stub, get_use_binary_from_path_config_stub, set_use_binary_from_path_config_mock): + # Arrange isfile_stub.return_value = False dirname_stub.return_value = "tmp" exists_stub.return_value = True @@ -62,40 +64,49 @@ def test_use_bicep_cli_from_path_false_after_install(self, isfile_stub, dirname_ response.getcode.return_value = 200 response.read.return_value = b"test" urlopen_stub.return_value = response + + get_use_binary_from_path_config_stub.return_value = "if_found_in_ci" - ensure_bicep_installation(self.default_cli, release_tag="v0.14.85", stdout=False) + # Act + ensure_bicep_installation(self.cli_ctx, release_tag="v0.14.85", stdout=False) - self.assertTrue(self.default_cli.config.get("bicep", "use_binary_from_path") == "false") + # Assert + set_use_binary_from_path_config_mock.assert_called_once_with(self.cli_ctx, "false") + + @mock.patch("azure.cli.command_modules.resource._bicep.get_use_binary_from_path_config") @mock.patch("shutil.which") - def test_run_bicep_command_raise_error_if_bicep_cli_not_found_when_use_binary_from_path_is_true(self, which_stub): + def test_run_bicep_command_raise_error_if_bicep_cli_not_found_when_use_binary_from_path_is_true(self, which_stub, get_use_binary_from_path_config_stub): which_stub.return_value = None - cli = DummyCli(random_config_dir=True) - cli.config.set_value("bicep", "use_binary_from_path", "true") + get_use_binary_from_path_config_stub.return_value = "true" with self.assertRaisesRegex( CLIError, 'Could not find the "bicep" executable on PATH. To install Bicep via Azure CLI, set the "bicep.use_binary_from_path" configuration to False and run "az bicep install".', ): - run_bicep_command(cli, ["--version"], auto_install=False) + run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) + @mock.patch.dict(os.environ, {"GITHUB_ACTIONS": "true"}, clear=True) + @mock.patch("azure.cli.command_modules.resource._bicep.get_use_binary_from_path_config") @mock.patch("azure.cli.command_modules.resource._bicep._logger.debug") @mock.patch("azure.cli.command_modules.resource._bicep._run_command") @mock.patch("shutil.which") - def test_run_bicep_command_use_bicep_cli_from_path_in_ci(self, which_stub, run_command_stub, debug_mock): + def test_run_bicep_command_use_bicep_cli_from_path_in_ci(self, which_stub, run_command_stub, debug_mock, get_use_binary_from_path_config_stub): which_stub.return_value = True run_command_stub.return_value = "Bicep CLI version 0.13.1 (e3ac80d678)" - cli = DummyCli(random_config_dir=True) - cli.config.set_value("bicep", "use_binary_from_path", "if_found_in_ci") + get_use_binary_from_path_config_stub.return_value = "if_found_in_ci" - run_bicep_command(cli, ["--version"], auto_install=False) + run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) debug_mock.assert_called_with( "Using Bicep CLI from PATH. %s", "Bicep CLI version 0.13.1 (e3ac80d678)", ) + + @mock.patch("azure.cli.command_modules.resource._bicep.get_check_version_config") + @mock.patch("azure.cli.command_modules.resource._bicep.get_use_binary_from_path_config") @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") @@ -110,21 +121,23 @@ def test_run_bicep_command_check_version( ensure_bicep_installation_mock, _run_command_mock, warning_mock, + get_use_binary_from_path_config_stub, + get_check_version_config_stub, ): isfile_stub.return_value = True _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("1.0.0") get_bicep_latest_release_tag_stub.return_value = "v2.0.0" + get_check_version_config_stub.return_value = "true" + get_use_binary_from_path_config_stub.return_value = "false" - cli = DummyCli(random_config_dir=True) - cli.config.set_value("bicep", "check_version", "True") - cli.config.set_value("bicep", "use_binary_from_path", "false") - run_bicep_command(cli, ["--version"]) + run_bicep_command(self.cli_ctx, ["--version"]) warning_mock.assert_called_once_with( 'A new Bicep release is available: %s. Upgrade now by running "az bicep upgrade".', "v2.0.0", ) + @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") @@ -147,12 +160,13 @@ def test_run_bicep_command_check_version_cache_read_write( _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("1.0.0") get_bicep_latest_release_tag_stub.return_value = "v2.0.0" - run_bicep_command(self.default_cli, ["--version"]) + run_bicep_command(self.cli_ctx, ["--version"]) self.assertTrue(os.path.isfile(_bicep_version_check_file_path)) finally: self._remove_bicep_version_check_file() + @mock.patch("os.path.isfile") @mock.patch("azure.cli.command_modules.resource._bicep._get_bicep_installed_version") @mock.patch("os.path.dirname") @@ -162,23 +176,25 @@ def test_ensure_bicep_installation_skip_download_if_installed_version_matches_re _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("0.1.0") isfile_stub.return_value = True - ensure_bicep_installation(self.default_cli, release_tag="v0.1.0") + ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") dirname_mock.assert_not_called() + + @mock.patch("azure.cli.command_modules.resource._bicep.get_use_binary_from_path_config") @mock.patch("azure.cli.command_modules.resource._bicep._get_bicep_installation_path") @mock.patch("shutil.which") def test_ensure_bicep_installation_skip_download_if_use_binary_from_path_is_true( - self, which_stub, _get_bicep_installation_path_mock + self, which_stub, _get_bicep_installation_path_mock, get_use_binary_from_path_config_stub ): which_stub.return_value = True - cli = DummyCli(random_config_dir=True) - cli.config.set_value("bicep", "use_binary_from_path", "true") + get_use_binary_from_path_config_stub.return_value = "true" - ensure_bicep_installation(cli, release_tag="v0.1.0") + ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") _get_bicep_installation_path_mock.assert_not_called() + def test_validate_target_scope_raise_error_if_target_scope_does_not_match_deployment_scope(self): with self.assertRaisesRegex( InvalidTemplateError, 'The target scope "tenant" does not match the deployment scope "subscription".' From e8af4ffffc904482976c81794a76d86508b8c3bf Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Fri, 14 Mar 2025 18:10:48 -0700 Subject: [PATCH 5/7] Fix tests --- .../tests/latest/test_resource_bicep.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py index 37553c96ca6..d3858ccf9b5 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/tests/latest/test_resource_bicep.py @@ -33,10 +33,12 @@ def test_run_bicep_command_raise_error_if_not_installed_and_not_auto_install(sel with contextlib.suppress(FileNotFoundError): remove_bicep_installation(self.cli_ctx) + self.cli_ctx.config.set_value("bicep", "use_binary_from_path", "false") with self.assertRaisesRegex(CLIError, 'Bicep CLI not found. Install it now by running "az bicep install".'): run_bicep_command(self.cli_ctx, ["--version"], auto_install=False) + @mock.patch("azure.cli.command_modules.resource._bicep._use_binary_from_path") @mock.patch("azure.cli.command_modules.resource._bicep.set_use_binary_from_path_config") @mock.patch("azure.cli.command_modules.resource._bicep.get_use_binary_from_path_config") @mock.patch("os.chmod") @@ -47,7 +49,20 @@ def test_run_bicep_command_raise_error_if_not_installed_and_not_auto_install(sel @mock.patch("os.path.exists") @mock.patch("os.path.dirname") @mock.patch("os.path.isfile") - def test_use_bicep_cli_from_path_false_after_install(self, isfile_stub, dirname_stub, exists_stub, urlopen_stub, open_stub, buffered_writer_stub, stat_stub, chmod_stub, get_use_binary_from_path_config_stub, set_use_binary_from_path_config_mock): + def test_use_bicep_cli_from_path_false_after_install( + self, + isfile_stub, + dirname_stub, + exists_stub, + urlopen_stub, + open_stub, + buffered_writer_stub, + stat_stub, + chmod_stub, + get_use_binary_from_path_config_stub, + set_use_binary_from_path_config_mock, + user_binary_from_path_stub, + ): # Arrange isfile_stub.return_value = False dirname_stub.return_value = "tmp" @@ -65,6 +80,7 @@ def test_use_bicep_cli_from_path_false_after_install(self, isfile_stub, dirname_ response.read.return_value = b"test" urlopen_stub.return_value = response + user_binary_from_path_stub.return_value = False get_use_binary_from_path_config_stub.return_value = "if_found_in_ci" # Act @@ -167,14 +183,16 @@ def test_run_bicep_command_check_version_cache_read_write( self._remove_bicep_version_check_file() + @mock.patch("azure.cli.command_modules.resource._bicep._use_binary_from_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 + self, dirname_mock, _get_bicep_installed_version_stub, isfile_stub, user_binary_from_path_stub ): _get_bicep_installed_version_stub.return_value = semver.VersionInfo.parse("0.1.0") isfile_stub.return_value = True + user_binary_from_path_stub.return_value = False ensure_bicep_installation(self.cli_ctx, release_tag="v0.1.0") From 03c3642802f5f6fe8205dfb63958d174c6fd0262 Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Fri, 14 Mar 2025 18:35:51 -0700 Subject: [PATCH 6/7] Add license header --- .../azure/cli/command_modules/resource/_bicep_config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py index c4830e42cb0..e74b265569a 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py @@ -1,3 +1,8 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + _config_section = "bicep" _use_binary_from_path_config_key = "use_binary_from_path" From e1a57175bae379e44368db8c32039519f8abf85a Mon Sep 17 00:00:00 2001 From: Shenglong Li Date: Mon, 17 Mar 2025 09:58:43 -0700 Subject: [PATCH 7/7] Fix style issues --- .../azure/cli/command_modules/resource/_bicep.py | 3 ++- .../azure/cli/command_modules/resource/_bicep_config.py | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py index da1311deda4..bbea23a7908 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/_bicep.py +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep.py @@ -55,6 +55,7 @@ _requests_verify = not should_disable_connection_verify() + def validate_bicep_target_scope(template_schema, deployment_scope): target_scope = _template_schema_to_target_scope(template_schema) if target_scope != deployment_scope: @@ -394,4 +395,4 @@ def _template_schema_to_target_scope(template_schema): return "managementGroup" if template_type_lower == "tenantdeployment": return "tenant" - return None \ No newline at end of file + return None diff --git a/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py b/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py index e74b265569a..a48db77b6e5 100644 --- a/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py +++ b/src/azure-cli/azure/cli/command_modules/resource/_bicep_config.py @@ -11,14 +11,20 @@ _check_version_config_key = "check_version" _check_version_config_default_value = True + def get_use_binary_from_path_config(cli_ctx): - return cli_ctx.config.get(_config_section, _use_binary_from_path_config_key, _use_binary_from_path_config_default_value).lower() + return cli_ctx.config.get( + _config_section, _use_binary_from_path_config_key, _use_binary_from_path_config_default_value + ).lower() + def set_use_binary_from_path_config(cli_ctx, value): cli_ctx.config.set_value(_config_section, _use_binary_from_path_config_key, value) + def remove_use_binary_from_path_config(cli_ctx): cli_ctx.config.remove_option(_config_section, _use_binary_from_path_config_key) + def get_check_version_config(cli_ctx): return cli_ctx.config.getboolean(_config_section, _check_version_config_key, _check_version_config_default_value)