From b71fea0ca4cfa5ade5c966ae4857017864329b4c Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Fri, 16 Oct 2026 06:43:58 -0700 Subject: [PATCH 1/2] Match Tool Names and the all Keyword by Case in the Windows Agents Loaders bootstrap.ps1 -Agents ALL took the all keyword because -contains ignores case, while bootstrap.sh --agents ALL passed ALL through and install-agents.sh refused it as an unknown tool. The PowerShell tool-name comparisons are now case-sensitive, so both loaders treat only lowercase all as the keyword and refuse any other case as an unknown tool. Co-Authored-By: Claude Sonnet 5.5 --- host-setup/bootstrap.ps1 | 2 +- host-setup/windows/install-agents.ps1 | 6 +++--- tests/test_bootstrap.py | 16 ++++++++++++++++ tests/test_install_agents.py | 4 ++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/host-setup/bootstrap.ps1 b/host-setup/bootstrap.ps1 index 664312c5..d9afb285 100644 --- a/host-setup/bootstrap.ps1 +++ b/host-setup/bootstrap.ps1 @@ -481,7 +481,7 @@ function Invoke-Upgrade { # Installs only what is named, since the agent tools are never installed as a set nobody chose. function Invoke-AgentsInstall { if ($script:AGENT_NAMES.Count -eq 0) { die "-Agents takes the tools to install, separated by commas, or 'all'" } - if ($script:AGENT_NAMES -contains 'all') { + if ($script:AGENT_NAMES -ccontains 'all') { if ($script:AGENT_NAMES.Count -gt 1) { die "-Agents takes 'all' alone, or a list of tool names" } Invoke-Tool -Tool 'install-agents.ps1' -Arguments '-Install', '-All' return diff --git a/host-setup/windows/install-agents.ps1 b/host-setup/windows/install-agents.ps1 index 3507ea9f..ba9c875f 100644 --- a/host-setup/windows/install-agents.ps1 +++ b/host-setup/windows/install-agents.ps1 @@ -372,7 +372,7 @@ $TOOLS = @( function Get-Tool { param([string]$ToolName) - return ($script:TOOLS | Where-Object { $_.Name -eq $ToolName } | Select-Object -First 1) + return ($script:TOOLS | Where-Object { $_.Name -ceq $ToolName } | Select-Object -First 1) } # --- Report --- @@ -564,7 +564,7 @@ function Show-List { function Resolve-Selection { $names = @($script:TOOLS | ForEach-Object { $_.Name }) foreach ($candidate in $script:WANT_TOOLS) { - if ($names -notcontains $candidate) { die "Unknown tool `"$candidate`", -List names the managed tools" } + if ($names -cnotcontains $candidate) { die "Unknown tool `"$candidate`", -List names the managed tools" } } if ($script:WANT_TOOLS.Count -eq 0) { if ($script:MODE -eq 'install' -and -not $script:INSTALL_ALL) { @@ -573,7 +573,7 @@ function Resolve-Selection { return , $names } # Sorted into registry order rather than the order they were typed, so a run reads the same however it was asked for. - return , @($names | Where-Object { $script:WANT_TOOLS -contains $_ }) + return , @($names | Where-Object { $script:WANT_TOOLS -ccontains $_ }) } # The order the actions were given is not a contract, so more than one is a refusal rather than the last one winning. diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index cb99492d..96348ab8 100755 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -1699,6 +1699,13 @@ def test_agents_a_name_holding_a_space_and_all_is_not_all(self) -> None: self.assertEqual(result.returncode, 0, result.stderr) self.assertIn(f"install-agents.sh --install <{given}>", self.agents_steps()) + def test_agents_all_in_another_case_is_a_tool_name_not_the_keyword(self) -> None: + for given in ("ALL", "All"): + with self.subTest(given=given): + result = self.run_main(action="--agents", args=(given,)) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(self.agents_steps()[-1:], [f"install-agents.sh --install {given}"]) + def test_agents_drops_empty_names_from_the_list(self) -> None: result = self.run_main(action="--agents", args=("herdr,,cswap,",)) self.assertEqual(result.returncode, 0, result.stderr) @@ -1879,6 +1886,15 @@ def test_all_alone_hands_off_as_an_install_of_all(self) -> None: self.assertEqual(result.returncode, 0, result.stderr) self.assertEqual(self.steps(), ["install-agents.ps1 -Install -All"], result.stdout) + def test_all_in_another_case_is_a_tool_name_not_the_keyword(self) -> None: + for given in ("ALL", "All"): + with self.subTest(given=given): + result = self.run_agents([given]) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual( + self.steps()[-1:], [f"install-agents.ps1 -Install {given}"], result.stdout + ) + def test_all_beside_other_names_is_refused(self) -> None: result = self.run_agents(["all,herdr"]) self.assertNotEqual(result.returncode, 0) diff --git a/tests/test_install_agents.py b/tests/test_install_agents.py index 669bc443..7749c1e2 100755 --- a/tests/test_install_agents.py +++ b/tests/test_install_agents.py @@ -238,6 +238,8 @@ def test_refusals(self) -> None: (("--upgrade", "--json"), "applies only to --report"), (("--upgrade", "--all"), "--all names every tool for --install"), (("--report", "nosuch"), 'Unknown tool "nosuch"'), + (("--install", "ALL"), 'Unknown tool "ALL"'), + (("--report", "Herdr"), 'Unknown tool "Herdr"'), (("--report", "--upgrade"), "More than one action"), ]: with self.subTest(arguments=arguments): @@ -367,6 +369,8 @@ def test_refusals(self) -> None: (("-Upgrade", "-All"), "-All names every tool for -Install"), (("-Install", "-All", "herdr"), "-All and a tool name were both given"), (("-Report", "nosuch"), 'Unknown tool "nosuch"'), + (("-Install", "ALL"), 'Unknown tool "ALL"'), + (("-Report", "Herdr"), 'Unknown tool "Herdr"'), ]: with self.subTest(arguments=arguments): result = self.run_script(*arguments) From 491d9c1cd0e7ac2225b2312bd9e13bb13ab5ede1 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Fri, 16 Oct 2026 06:47:00 -0700 Subject: [PATCH 2/2] Pin the All Case Tests to the Whole Step List Co-Authored-By: Claude Sonnet 5.5 --- tests/test_bootstrap.py | 10 ++++++---- tests/test_install_agents.py | 2 -- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 96348ab8..859a12ee 100755 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -1700,11 +1700,13 @@ def test_agents_a_name_holding_a_space_and_all_is_not_all(self) -> None: self.assertIn(f"install-agents.sh --install <{given}>", self.agents_steps()) def test_agents_all_in_another_case_is_a_tool_name_not_the_keyword(self) -> None: + expected: list[str] = [] for given in ("ALL", "All"): with self.subTest(given=given): result = self.run_main(action="--agents", args=(given,)) self.assertEqual(result.returncode, 0, result.stderr) - self.assertEqual(self.agents_steps()[-1:], [f"install-agents.sh --install {given}"]) + expected.append(f"install-agents.sh --install {given}") + self.assertEqual(self.agents_steps(), expected) def test_agents_drops_empty_names_from_the_list(self) -> None: result = self.run_main(action="--agents", args=("herdr,,cswap,",)) @@ -1887,13 +1889,13 @@ def test_all_alone_hands_off_as_an_install_of_all(self) -> None: self.assertEqual(self.steps(), ["install-agents.ps1 -Install -All"], result.stdout) def test_all_in_another_case_is_a_tool_name_not_the_keyword(self) -> None: + expected: list[str] = [] for given in ("ALL", "All"): with self.subTest(given=given): result = self.run_agents([given]) self.assertEqual(result.returncode, 0, result.stderr) - self.assertEqual( - self.steps()[-1:], [f"install-agents.ps1 -Install {given}"], result.stdout - ) + expected.append(f"install-agents.ps1 -Install {given}") + self.assertEqual(self.steps(), expected, result.stdout) def test_all_beside_other_names_is_refused(self) -> None: result = self.run_agents(["all,herdr"]) diff --git a/tests/test_install_agents.py b/tests/test_install_agents.py index 7749c1e2..38fe9d08 100755 --- a/tests/test_install_agents.py +++ b/tests/test_install_agents.py @@ -238,7 +238,6 @@ def test_refusals(self) -> None: (("--upgrade", "--json"), "applies only to --report"), (("--upgrade", "--all"), "--all names every tool for --install"), (("--report", "nosuch"), 'Unknown tool "nosuch"'), - (("--install", "ALL"), 'Unknown tool "ALL"'), (("--report", "Herdr"), 'Unknown tool "Herdr"'), (("--report", "--upgrade"), "More than one action"), ]: @@ -369,7 +368,6 @@ def test_refusals(self) -> None: (("-Upgrade", "-All"), "-All names every tool for -Install"), (("-Install", "-All", "herdr"), "-All and a tool name were both given"), (("-Report", "nosuch"), 'Unknown tool "nosuch"'), - (("-Install", "ALL"), 'Unknown tool "ALL"'), (("-Report", "Herdr"), 'Unknown tool "Herdr"'), ]: with self.subTest(arguments=arguments):