From 9142fce467e7c930fc3ce754113c04fd7f4c8277 Mon Sep 17 00:00:00 2001 From: Amber Amos Date: Thu, 10 Sep 2026 11:10:58 -0700 Subject: [PATCH 1/2] update resolution of dotnet --- .../cli/command_modules/appservice/custom.py | 2 ++ .../latest/test_webapp_commands_thru_mock.py | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/custom.py b/src/azure-cli/azure/cli/command_modules/appservice/custom.py index a3b809afa62..c8c97c82148 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/custom.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/custom.py @@ -7635,6 +7635,8 @@ def resolve(self, display_name, linux=False): old_to_new_linux = { "dotnet|5.0": "dotnetcore|5.0", "dotnet|6.0": "dotnetcore|6.0", + "dotnet|11": "dotnetcore|11.0", + "dotnetcore|11.0": "dotnet|11", } if linux: display_name = old_to_new_linux.get(display_name) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index ec6a96e6502..474da0c3ff5 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -1643,6 +1643,37 @@ def __len__(self): return len(self._data) +class TestStackRuntimeDotnetLinux(unittest.TestCase): + + @staticmethod + def _new_helper(runtime_name): + from azure.cli.command_modules.appservice.custom import _StackRuntimeHelper + helper = _StackRuntimeHelper.__new__(_StackRuntimeHelper) + helper._stacks = [ + helper.Runtime( + display_name=runtime_name, + configs={'linux_fx_version': runtime_name}, + linux=True) + ] + return helper + + def test_resolve_legacy_dotnet_11_with_canonical_catalog(self): + helper = self._new_helper('dotnet|11') + + runtime = helper.resolve('DOTNETCORE|11.0', linux=True) + + self.assertEqual(runtime.display_name, 'dotnet|11') + self.assertEqual(runtime.configs['linux_fx_version'], 'dotnet|11') + + def test_resolve_canonical_dotnet_11_with_legacy_catalog(self): + helper = self._new_helper('DOTNETCORE|11.0') + + runtime = helper.resolve('dotnet|11', linux=True) + + self.assertEqual(runtime.display_name, 'DOTNETCORE|11.0') + self.assertEqual(runtime.configs['linux_fx_version'], 'DOTNETCORE|11.0') + + class TestStackRuntimeJavaSELinux(unittest.TestCase): """Regression tests for `az webapp list-runtimes` Linux Java SE parsing. From cb860df177ace57f16ceda09b2314f9f3b6ee447 Mon Sep 17 00:00:00 2001 From: Amber Amos Date: Thu, 10 Sep 2026 12:02:19 -0700 Subject: [PATCH 2/2] update standardization --- .../cli/command_modules/appservice/custom.py | 32 ++++++++--- .../latest/test_webapp_commands_thru_mock.py | 56 +++++++++++++++++-- 2 files changed, 76 insertions(+), 12 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/custom.py b/src/azure-cli/azure/cli/command_modules/appservice/custom.py index ed38acb6f71..8d5925bdf24 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/custom.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/custom.py @@ -91,7 +91,8 @@ from ._constants import (FUNCTIONS_STACKS_API_KEYS, FUNCTIONS_LINUX_RUNTIME_VERSION_REGEX, FUNCTIONS_WINDOWS_RUNTIME_VERSION_REGEX, PUBLIC_CLOUD, LINUX_GITHUB_ACTIONS_WORKFLOW_TEMPLATE_PATH, WINDOWS_GITHUB_ACTIONS_WORKFLOW_TEMPLATE_PATH, - DOTNET_RUNTIME_NAME, NETCORE_RUNTIME_NAME, ASPDOTNET_RUNTIME_NAME, LINUX_OS_NAME, + DOTNET_RUNTIME_NAME, NETCORE_RUNTIME_NAME, ASPDOTNET_RUNTIME_NAME, NODE_RUNTIME_NAME, + LINUX_OS_NAME, WINDOWS_OS_NAME, LINUX_FUNCTIONAPP_GITHUB_ACTIONS_WORKFLOW_TEMPLATE_PATH, WINDOWS_FUNCTIONAPP_GITHUB_ACTIONS_WORKFLOW_TEMPLATE_PATH, DEFAULT_CENTAURI_IMAGE, VERSION_2022_09_01, FLEX_SUBNET_DELEGATION, @@ -8297,7 +8298,7 @@ def remove_delimiters(cls, runtime): return cls.DEFAULT_DELIMETER.join(filter(None, runtime)) def resolve(self, display_name, linux=False): - display_name = self.standardize_node_runtime_name(display_name).lower() + display_name = self.standardize_runtime_name(display_name).lower() stack = next((s for s in self.stacks if s.linux == linux and s.display_name.lower() == display_name), None) if stack is None: # help convert previously acceptable stack names into correct ones if runtime not found old_to_new_windows = { @@ -8314,8 +8315,6 @@ def resolve(self, display_name, linux=False): old_to_new_linux = { "dotnet|5.0": "dotnetcore|5.0", "dotnet|6.0": "dotnetcore|6.0", - "dotnet|11": "dotnetcore|11.0", - "dotnetcore|11.0": "dotnet|11", } if linux: display_name = old_to_new_linux.get(display_name) @@ -8395,6 +8394,25 @@ def standardize_node_runtime_name(runtime_name): return "NODE|{}".format(match.group(1)) return runtime_name + @staticmethod + def standardize_dotnet_runtime_name(runtime_name): + match = re.fullmatch(r'dotnet(?:core)?\|(\d+)(?:\.0)?', runtime_name, re.IGNORECASE) + if match and int(match.group(1)) >= 11: + return "dotnet|{}".format(match.group(1)) + return runtime_name + + @classmethod + def standardize_runtime_name(cls, runtime_name): + if not runtime_name: + return runtime_name + + runtime_family = runtime_name.split(cls.DEFAULT_DELIMETER, 1)[0].lower() + if runtime_family == NODE_RUNTIME_NAME: + return cls.standardize_node_runtime_name(runtime_name) + if runtime_family in (DOTNET_RUNTIME_NAME, NETCORE_RUNTIME_NAME): + return cls.standardize_dotnet_runtime_name(runtime_name) + return runtime_name + @classmethod def _is_valid_runtime_setting(cls, runtime_setting, include_eol=False): # Using datetime module imported at the top level @@ -8596,7 +8614,7 @@ def _parse_major_version_windows(self, major_version, parsed_results, config_map eol_date = self._format_eol_date(getattr(settings, 'end_of_life_date', None)) if "Java" not in minor_version.display_text: runtime_name = self._format_windows_display_text(minor_version.display_text) - runtime_name = self.standardize_node_runtime_name(runtime_name) + runtime_name = self.standardize_runtime_name(runtime_name) runtime = self.Runtime(display_name=runtime_name, linux=False, os="Windows", runtime_family=runtime_family, @@ -8708,7 +8726,7 @@ def _parse_major_version_linux(self, major_version, parsed_results, seen_runtime major_version, linux=True, java=False, include_eol=self._include_eol) for minor_version in minor_versions: settings = minor_version.stack_settings.linux_runtime_settings - runtime_name = self.standardize_node_runtime_name(settings.runtime_version) + runtime_name = self.standardize_runtime_name(settings.runtime_version) runtime = self.Runtime(display_name=runtime_name, configs={"linux_fx_version": runtime_name}, linux=True, @@ -12549,7 +12567,7 @@ def add_github_actions(cmd, resource_group, name, repo, runtime=None, token=None branch='master', login_with_github=False, force=False): runtime = _StackRuntimeHelper(cmd).remove_delimiters(runtime) # normalize "runtime:version" if runtime: - runtime = _StackRuntimeHelper.standardize_node_runtime_name(runtime) + runtime = _StackRuntimeHelper.standardize_runtime_name(runtime) if not token and not login_with_github: raise_missing_token_suggestion() elif not token: diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index fd3c3bfcc00..6f3fe3716e2 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -2325,6 +2325,7 @@ class TestStackRuntimeDotnetLinux(unittest.TestCase): def _new_helper(runtime_name): from azure.cli.command_modules.appservice.custom import _StackRuntimeHelper helper = _StackRuntimeHelper.__new__(_StackRuntimeHelper) + helper._include_eol = False helper._stacks = [ helper.Runtime( display_name=runtime_name, @@ -2341,13 +2342,58 @@ def test_resolve_legacy_dotnet_11_with_canonical_catalog(self): self.assertEqual(runtime.display_name, 'dotnet|11') self.assertEqual(runtime.configs['linux_fx_version'], 'dotnet|11') - def test_resolve_canonical_dotnet_11_with_legacy_catalog(self): - helper = self._new_helper('DOTNETCORE|11.0') + def test_standardize_dotnet_runtime_name(self): + from azure.cli.command_modules.appservice.custom import _StackRuntimeHelper + test_cases = { + 'DOTNETCORE|11.0': 'dotnet|11', + 'dotnet|11': 'dotnet|11', + 'DOTNET|11.0': 'dotnet|11', + 'DOTNETCORE|12.0': 'dotnet|12', + 'DOTNETCORE|10.0': 'DOTNETCORE|10.0', + } + + for runtime_name, expected in test_cases.items(): + with self.subTest(runtime_name=runtime_name): + self.assertEqual( + _StackRuntimeHelper.standardize_dotnet_runtime_name(runtime_name), + expected) + + def test_standardize_runtime_name_selects_dotnet_standardizer(self): + from azure.cli.command_modules.appservice.custom import _StackRuntimeHelper + + with mock.patch.object( + _StackRuntimeHelper, + 'standardize_node_runtime_name', + wraps=_StackRuntimeHelper.standardize_node_runtime_name) as node_standardizer: + self.assertEqual( + _StackRuntimeHelper.standardize_runtime_name('DOTNETCORE|11.0'), + 'dotnet|11') + + node_standardizer.assert_not_called() + + def test_parse_legacy_dotnet_11_catalog_as_canonical(self): + helper = self._new_helper('placeholder') + parsed_results = [] + github_settings = types.SimpleNamespace(is_supported=True, supported_version='11.x') + settings = types.SimpleNamespace( + runtime_version='DOTNETCORE|11.0', + end_of_life_date=None, + git_hub_action_settings=github_settings) + minor_version = types.SimpleNamespace( + display_text='.NET 11 (STS)', + stack_settings=types.SimpleNamespace(linux_runtime_settings=settings)) + + with mock.patch.object(helper, '_get_valid_minor_versions', side_effect=[[], [minor_version]]): + helper._parse_major_version_linux( + types.SimpleNamespace(display_text='.NET 11'), + parsed_results, + set(), + runtime_family='.NET') + + self.assertEqual(parsed_results[0].display_name, 'dotnet|11') + self.assertEqual(parsed_results[0].configs['linux_fx_version'], 'dotnet|11') - runtime = helper.resolve('dotnet|11', linux=True) - self.assertEqual(runtime.display_name, 'DOTNETCORE|11.0') - self.assertEqual(runtime.configs['linux_fx_version'], 'DOTNETCORE|11.0') class TestStackRuntimeNodeStandardization(unittest.TestCase): @staticmethod def _new_helper():