From b82ec377a53d56a67dac27d700396c255f605ca0 Mon Sep 17 00:00:00 2001 From: Fnu Abdullah Date: Fri, 19 Jun 2026 05:17:13 -0500 Subject: [PATCH 1/4] Disable Windows glob expansion for CLI args --- src/google/adk/cli/cli_tools_click.py | 11 ++++++++++- tests/unittests/cli/utils/test_cli_tools_click.py | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index f884bce61a..1ea05de5b3 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -46,6 +46,12 @@ case_sensitive=False, ) +class _NoWindowsGlobExpansionGroup(click.Group): + """Click group that disables Windows glob expansion for CLI arguments.""" + + def main(self, *args, **kwargs): + kwargs.setdefault("windows_expand_args", False) + return super().main(*args, **kwargs) def _logging_options(): """Decorator to add logging options to click commands.""" @@ -235,7 +241,10 @@ def _warn_if_with_ui(with_ui: bool) -> None: click.secho(f"WARNING: {_ADK_WEB_WARNING}", fg="yellow", err=True) -@click.group(context_settings={"max_content_width": 240}) +@click.group( + cls=_NoWindowsGlobExpansionGroup, + context_settings={"max_content_width": 240}, +) @click.version_option(version.__version__) def main(): """Agent Development Kit CLI tools.""" diff --git a/tests/unittests/cli/utils/test_cli_tools_click.py b/tests/unittests/cli/utils/test_cli_tools_click.py index 71a52d6d46..8124e016ea 100644 --- a/tests/unittests/cli/utils/test_cli_tools_click.py +++ b/tests/unittests/cli/utils/test_cli_tools_click.py @@ -88,6 +88,14 @@ def _mute_click(request, monkeypatch: pytest.MonkeyPatch) -> None: # monkeypatch.setattr(click, "secho", lambda *a, **k: None) +def test_main_disables_click_windows_glob_expansion() -> None: + """Verifies the ADK CLI disables Click's Windows glob expansion.""" + with mock.patch.object(click.Group, "main", return_value=None) as mock_main: + cli_tools_click.main.main(args=["web", ".", "--allow_origins", "*"]) + + assert mock_main.call_args.kwargs["windows_expand_args"] is False + + # validate_exclusive def test_validate_exclusive_allows_single() -> None: """Providing exactly one exclusive option should pass.""" From 82c7c16e3ececd6a6ff81559e28175909fc10e84 Mon Sep 17 00:00:00 2001 From: Fnu Abdullah Date: Thu, 25 Jun 2026 02:46:29 -0500 Subject: [PATCH 2/4] Fix Windows glob expansion typing --- src/google/adk/cli/cli_tools_click.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index 1ea05de5b3..a820098d44 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -46,6 +46,7 @@ case_sensitive=False, ) + class _NoWindowsGlobExpansionGroup(click.Group): """Click group that disables Windows glob expansion for CLI arguments.""" @@ -53,6 +54,7 @@ def main(self, *args, **kwargs): kwargs.setdefault("windows_expand_args", False) return super().main(*args, **kwargs) + def _logging_options(): """Decorator to add logging options to click commands.""" From 009259805f408f73030da1e257a072ca0eaa6821 Mon Sep 17 00:00:00 2001 From: Fnu Abdullah Date: Thu, 25 Jun 2026 03:04:07 -0500 Subject: [PATCH 3/4] Improve typing for Windows Click glob expansion --- src/google/adk/cli/cli_tools_click.py | 38 ++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index a820098d44..0b601a3efc 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -26,6 +26,8 @@ import sys import tempfile import textwrap +from typing import Any +from typing import cast import click from click.core import ParameterSource @@ -46,11 +48,13 @@ case_sensitive=False, ) +_ClickGroup = cast(type[Any], click.Group) -class _NoWindowsGlobExpansionGroup(click.Group): + +class _NoWindowsGlobExpansionGroup(_ClickGroup): """Click group that disables Windows glob expansion for CLI arguments.""" - def main(self, *args, **kwargs): + def main(self, *args: Any, **kwargs: Any) -> Any: kwargs.setdefault("windows_expand_args", False) return super().main(*args, **kwargs) @@ -2011,6 +2015,7 @@ def cli_api_server( "cloud_run", context_settings={ "allow_extra_args": True, + "allow_interspersed_args": False, }, ) @click.option( @@ -2187,7 +2192,34 @@ def cli_deploy_cloud_run( _warn_if_with_ui(with_ui) - gcloud_args = ctx.args + # Parse arguments to separate gcloud args (after --) from regular args + gcloud_args = [] + if "--" in ctx.args: + separator_index = ctx.args.index("--") + gcloud_args = ctx.args[separator_index + 1 :] + regular_args = ctx.args[:separator_index] + + # If there are regular args before --, that's an error + if regular_args: + click.secho( + "Error: Unexpected arguments after agent path and before '--':" + f" {' '.join(regular_args)}. \nOnly arguments after '--' are passed" + " to gcloud.", + fg="red", + err=True, + ) + ctx.exit(2) + else: + # No -- separator, treat all args as an error to enforce the new behavior + if ctx.args: + click.secho( + f"Error: Unexpected arguments: {' '.join(ctx.args)}. \nUse '--' to" + " separate gcloud arguments, e.g.: adk deploy cloud_run [options]" + " agent_path -- --min-instances=2", + fg="red", + err=True, + ) + ctx.exit(2) try: from . import cli_deploy From b1e0fa342acb6deed0f8a986a9286bdc30a5c53b Mon Sep 17 00:00:00 2001 From: Fnu Abdullah Date: Fri, 26 Jun 2026 05:27:43 -0500 Subject: [PATCH 4/4] fix(cli): disable Click Windows glob expansion at entry point and fix Mypy type redefinition errors --- src/google/adk/cli/__init__.py | 11 +++- src/google/adk/cli/cli_tools_click.py | 52 ++----------------- .../cli/utils/test_cli_tools_click.py | 3 +- 3 files changed, 16 insertions(+), 50 deletions(-) diff --git a/src/google/adk/cli/__init__.py b/src/google/adk/cli/__init__.py index 8766f6186e..ea270ea886 100644 --- a/src/google/adk/cli/__init__.py +++ b/src/google/adk/cli/__init__.py @@ -12,4 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .cli_tools_click import main +from typing import Any + +from .cli_tools_click import main as _main + + +def main(args: Any = None, **kwargs: Any) -> Any: + """Package entry point that disables Windows glob expansion for all CLI commands.""" + + kwargs.setdefault("windows_expand_args", False) + return _main.main(args=args, **kwargs) diff --git a/src/google/adk/cli/cli_tools_click.py b/src/google/adk/cli/cli_tools_click.py index 0b601a3efc..3ea7473d60 100644 --- a/src/google/adk/cli/cli_tools_click.py +++ b/src/google/adk/cli/cli_tools_click.py @@ -26,8 +26,7 @@ import sys import tempfile import textwrap -from typing import Any -from typing import cast +from typing import Optional import click from click.core import ParameterSource @@ -48,16 +47,6 @@ case_sensitive=False, ) -_ClickGroup = cast(type[Any], click.Group) - - -class _NoWindowsGlobExpansionGroup(_ClickGroup): - """Click group that disables Windows glob expansion for CLI arguments.""" - - def main(self, *args: Any, **kwargs: Any) -> Any: - kwargs.setdefault("windows_expand_args", False) - return super().main(*args, **kwargs) - def _logging_options(): """Decorator to add logging options to click commands.""" @@ -247,10 +236,7 @@ def _warn_if_with_ui(with_ui: bool) -> None: click.secho(f"WARNING: {_ADK_WEB_WARNING}", fg="yellow", err=True) -@click.group( - cls=_NoWindowsGlobExpansionGroup, - context_settings={"max_content_width": 240}, -) +@click.group(context_settings={"max_content_width": 240}) @click.version_option(version.__version__) def main(): """Agent Development Kit CLI tools.""" @@ -1159,7 +1145,7 @@ def cli_eval( inference_requests=inference_requests, eval_service=eval_service ) ) - eval_results = asyncio.run( + eval_results: list[EvalCaseResult] = asyncio.run( _collect_eval_results( inference_results=inference_results, eval_service=eval_service, @@ -1175,7 +1161,6 @@ def cli_eval( eval_run_summary = {} for eval_result in eval_results: - eval_result: EvalCaseResult if eval_result.eval_set_id not in eval_run_summary: eval_run_summary[eval_result.eval_set_id] = [0, 0] @@ -1193,7 +1178,6 @@ def cli_eval( if print_detailed_results: for eval_result in eval_results: - eval_result: EvalCaseResult click.echo( "********************************************************************" ) @@ -2015,7 +1999,6 @@ def cli_api_server( "cloud_run", context_settings={ "allow_extra_args": True, - "allow_interspersed_args": False, }, ) @click.option( @@ -2192,34 +2175,7 @@ def cli_deploy_cloud_run( _warn_if_with_ui(with_ui) - # Parse arguments to separate gcloud args (after --) from regular args - gcloud_args = [] - if "--" in ctx.args: - separator_index = ctx.args.index("--") - gcloud_args = ctx.args[separator_index + 1 :] - regular_args = ctx.args[:separator_index] - - # If there are regular args before --, that's an error - if regular_args: - click.secho( - "Error: Unexpected arguments after agent path and before '--':" - f" {' '.join(regular_args)}. \nOnly arguments after '--' are passed" - " to gcloud.", - fg="red", - err=True, - ) - ctx.exit(2) - else: - # No -- separator, treat all args as an error to enforce the new behavior - if ctx.args: - click.secho( - f"Error: Unexpected arguments: {' '.join(ctx.args)}. \nUse '--' to" - " separate gcloud arguments, e.g.: adk deploy cloud_run [options]" - " agent_path -- --min-instances=2", - fg="red", - err=True, - ) - ctx.exit(2) + gcloud_args = ctx.args try: from . import cli_deploy diff --git a/tests/unittests/cli/utils/test_cli_tools_click.py b/tests/unittests/cli/utils/test_cli_tools_click.py index 8124e016ea..c021dc050a 100644 --- a/tests/unittests/cli/utils/test_cli_tools_click.py +++ b/tests/unittests/cli/utils/test_cli_tools_click.py @@ -91,8 +91,9 @@ def _mute_click(request, monkeypatch: pytest.MonkeyPatch) -> None: def test_main_disables_click_windows_glob_expansion() -> None: """Verifies the ADK CLI disables Click's Windows glob expansion.""" with mock.patch.object(click.Group, "main", return_value=None) as mock_main: - cli_tools_click.main.main(args=["web", ".", "--allow_origins", "*"]) + from google.adk.cli import main + main(args=["web", ".", "--allow_origins", "*"]) assert mock_main.call_args.kwargs["windows_expand_args"] is False