diff --git a/.changes/unreleased/fixed-20260726-120000.yaml b/.changes/unreleased/fixed-20260726-120000.yaml new file mode 100644 index 00000000..0ac0bd27 --- /dev/null +++ b/.changes/unreleased/fixed-20260726-120000.yaml @@ -0,0 +1,6 @@ +kind: fixed +body: Set User Agent suffix for Fabric CLI deploy command. +time: 2026-07-26T12:00:00Z +custom: + Author: aviatcohen + AuthorLink: https://github.com/aviatcohen diff --git a/src/fabric_cli/commands/fs/deploy/fab_fs_deploy_config_file.py b/src/fabric_cli/commands/fs/deploy/fab_fs_deploy_config_file.py index 1a8e3fbc..655c2259 100644 --- a/src/fabric_cli/commands/fs/deploy/fab_fs_deploy_config_file.py +++ b/src/fabric_cli/commands/fs/deploy/fab_fs_deploy_config_file.py @@ -42,6 +42,11 @@ def deploy_with_config_file(args: Namespace) -> None: except json.JSONDecodeError: # If it's not a valid JSON string, keep it as is pass + + deploy_parameters["host_app"] = ( + f"{fab_constant.API_USER_AGENT}/{fab_constant.FAB_VERSION}" + ) + result = deploy_with_config( config_file_path=deploy_config_file, environment=args.target_env, diff --git a/tests/test_commands/test_deploy.py b/tests/test_commands/test_deploy.py index 2b8e9e8f..4697548d 100644 --- a/tests/test_commands/test_deploy.py +++ b/tests/test_commands/test_deploy.py @@ -3,7 +3,8 @@ import os import platform -from unittest.mock import patch +from argparse import Namespace +from unittest.mock import MagicMock, patch import pytest import yaml @@ -354,3 +355,68 @@ def test_deploy_with_home_directory_path_success( mock_print_done.assert_called() assert "Deployment completed successfully" in str( mock_print_done.call_args) + + def _run_deploy_with_config_file(self, deploy_with_config, params=None): + """Invoke deploy_with_config_file with fabric-cicd symbols patched (no network).""" + from fabric_cli.commands.fs.deploy import ( + fab_fs_deploy_config_file as deploy_mod, + ) + + args = Namespace( + config="config.yml", + target_env="dev", + command_path="deploy", + params=params if params is not None else [], + ) + + with ( + patch.object(deploy_mod, "deploy_with_config", deploy_with_config), + patch.object( + deploy_mod, "create_fabric_token_credential", MagicMock()), + patch.object(deploy_mod, "append_feature_flag", MagicMock()), + patch.object(deploy_mod, "disable_file_logging", MagicMock()), + patch.object( + deploy_mod, "configure_external_file_logging", MagicMock()), + patch.object( + deploy_mod.fab_state_config, "get_config", return_value="false" + ), + patch.object(deploy_mod.fab_ui, + "print_output_format", MagicMock()), + ): + deploy_mod.deploy_with_config_file(args) + + def _capture_deploy_host_app(self, params=None): + """Run deploy_with_config_file and return the host_app passed to fabric-cicd.""" + captured = {} + + def fake_deploy_with_config( + *, + config_file_path, + token_credential, + environment="N/A", + config_override=None, + host_app=None, + ): + captured["host_app"] = host_app + return MagicMock(message="Deployment completed successfully") + + self._run_deploy_with_config_file( + fake_deploy_with_config, params=params) + + return captured["host_app"] + + def test_deploy_passes_host_app_success(self): + """CLI passes host_app as 'ms-fabric-cli/'.""" + from fabric_cli.core import fab_constant + + host_app = self._capture_deploy_host_app() + + assert host_app == f"{fab_constant.API_USER_AGENT}/{fab_constant.FAB_VERSION}" + + def test_deploy_host_app_cannot_be_spoofed_via_params_success(self): + """A user-supplied host_app (via -P) is overridden by the CLI-controlled value.""" + from fabric_cli.core import fab_constant + + host_app = self._capture_deploy_host_app(params=["host_app=spoofed"]) + + assert host_app == f"{fab_constant.API_USER_AGENT}/{fab_constant.FAB_VERSION}" diff --git a/tests/test_utils/test_fab_deploy_bulk_publish.py b/tests/test_utils/test_fab_deploy_bulk_publish.py index 0886554d..fed1be5b 100644 --- a/tests/test_utils/test_fab_deploy_bulk_publish.py +++ b/tests/test_utils/test_fab_deploy_bulk_publish.py @@ -27,6 +27,7 @@ def _run_deploy(self, tmp_path, bulk_publish, mock_fab_set_state_config): target_env="dev", params=None, bulk_publish=bulk_publish, + command_path="deploy", ) with (