drydock-cli 3.0.108__tar.gz → 3.0.110__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {drydock_cli-3.0.108/drydock_cli.egg-info → drydock_cli-3.0.110}/PKG-INFO +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tools/__init__.py +35 -12
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tui/app.py +26 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tui/widgets.py +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.110/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/pyproject.toml +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_windows_shell.py +35 -8
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/LICENSE +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/NOTICE +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/README.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/__init__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/__main__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/advisor.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/agent.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/__init__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/rmf-categorize.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/rmf-control.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/rmf-poam.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/rmf-review.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/stig-assess.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/builtin_skills/stig-remediate.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/cci.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/cli.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/compaction.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/config.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/extract.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/gittools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/graphrag.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/guards.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/mcp.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/poam.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/providers.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/rmf.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/rmf_graph.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/skills.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/stig.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/tuning.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock/web.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock_cli.egg-info/SOURCES.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/setup.cfg +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_advisor.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_approval.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_back_command.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_background.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_binary_output.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_crossplatform.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_output_bounding.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_process_group.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_sanitize.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_shell.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_stdin.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_stop_partial.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_timeout_network.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_bash_timeout_param.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_cci.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_compact_command.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_compaction.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_config.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_config_migration.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_context_limit_config.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_dispatch.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_e2e_connected.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_edit_replace_all.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_extract.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_failure_loop.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_first_run_setup.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_gittools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_glob_edit_edges.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_graphify_example.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_graphrag.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_grep_and_read_robust.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_mcp.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_oneshot_unreachable.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_overthink_interrupt.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_poam.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_read_index.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_rmf.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_rmf_graph.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_rmf_stig_graph.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_runaway_repetition.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_server_probe.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_skills.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_stall_retry.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_stig.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_stop.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_subagent.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_system_prompt_help.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_todo.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_tool_arg_coercion.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_tool_arg_coercion_more.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_tui.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_tuning.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_viewimage.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_vision_input.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_web_tools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_write_content_coerce.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.110}/tests/test_xccdf.py +0 -0
|
@@ -6,6 +6,7 @@ Each tool is a function (params, config) -> str.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
|
+
import sys
|
|
9
10
|
import re
|
|
10
11
|
import difflib
|
|
11
12
|
import glob as _glob
|
|
@@ -92,28 +93,50 @@ def _detect_bash() -> str | None:
|
|
|
92
93
|
return None
|
|
93
94
|
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
def _is_windows_env(os_name: str, platform: str, environ) -> bool:
|
|
97
|
+
"""Treat as Windows if ANY signal says so — native CPython (os.name 'nt' /
|
|
98
|
+
sys.platform 'win32') AND odd Pythons run from Git-Bash/MSYS/Cygwin (which
|
|
99
|
+
report posix but where Windows always exports WINDIR/SystemRoot). WSL (real
|
|
100
|
+
Linux) does not set WINDIR, so it correctly stays POSIX/bash."""
|
|
101
|
+
return (
|
|
102
|
+
os_name == "nt"
|
|
103
|
+
or platform in ("win32", "cygwin", "msys")
|
|
104
|
+
or bool(environ.get("WINDIR") or environ.get("SystemRoot"))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
_IS_WINDOWS = _is_windows_env(os.name, sys.platform, os.environ)
|
|
96
109
|
|
|
97
110
|
|
|
98
111
|
def _detect_shell() -> tuple[str, str]:
|
|
99
112
|
"""Pick the shell tool_bash runs commands in — returns (kind, path).
|
|
100
113
|
|
|
114
|
+
Override with env DRYDOCK_SHELL=powershell|cmd|bash to force one. Otherwise:
|
|
101
115
|
POSIX → bash (so the model's bash-isms work), else /bin/sh.
|
|
102
116
|
Windows → PowerShell (what users actually launch drydock in — pwsh, else
|
|
103
|
-
Windows PowerShell), else cmd.exe. WSL / Git-Bash is
|
|
104
|
-
if you'd rather use them, install bash and it's picked up on POSIX-like paths.
|
|
117
|
+
Windows PowerShell), else cmd.exe. WSL / Git-Bash is NEVER required on Windows.
|
|
105
118
|
"""
|
|
106
119
|
import shutil
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
|
|
121
|
+
def _powershell():
|
|
122
|
+
p = shutil.which("pwsh") or shutil.which("powershell")
|
|
123
|
+
return ("powershell", p) if p else None
|
|
124
|
+
|
|
125
|
+
def _bash():
|
|
126
|
+
b = _detect_bash()
|
|
127
|
+
return ("bash", b) if b else ("sh", shutil.which("sh") or "/bin/sh")
|
|
128
|
+
|
|
129
|
+
forced = os.environ.get("DRYDOCK_SHELL", "").strip().lower()
|
|
130
|
+
if forced in ("powershell", "pwsh"):
|
|
131
|
+
return _powershell() or ("cmd", shutil.which("cmd") or "cmd.exe")
|
|
132
|
+
if forced == "cmd":
|
|
112
133
|
return ("cmd", shutil.which("cmd") or "cmd.exe")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
134
|
+
if forced in ("bash", "sh"):
|
|
135
|
+
return _bash()
|
|
136
|
+
|
|
137
|
+
if _IS_WINDOWS:
|
|
138
|
+
return _powershell() or ("cmd", shutil.which("cmd") or "cmd.exe")
|
|
139
|
+
return _bash()
|
|
117
140
|
|
|
118
141
|
|
|
119
142
|
# Resolved once at import (in whatever environment drydock runs — Linux/macOS,
|
|
@@ -501,6 +501,8 @@ class DrydockApp(App):
|
|
|
501
501
|
self._cmd_compact()
|
|
502
502
|
elif cmd == "/context":
|
|
503
503
|
self._cmd_context(arg)
|
|
504
|
+
elif cmd == "/shell":
|
|
505
|
+
self._cmd_shell()
|
|
504
506
|
elif cmd == "/advisor":
|
|
505
507
|
self._cmd_advisor(arg)
|
|
506
508
|
elif cmd == "/ask":
|
|
@@ -807,6 +809,30 @@ class DrydockApp(App):
|
|
|
807
809
|
else:
|
|
808
810
|
self._begin(prompt)
|
|
809
811
|
|
|
812
|
+
def _cmd_shell(self) -> None:
|
|
813
|
+
"""Show exactly which shell the Bash tool runs commands through, plus the
|
|
814
|
+
platform signals — so a 'still using bash on Windows' report is instantly
|
|
815
|
+
diagnosable. (The tool is NAMED 'Bash' on every OS; on Windows it actually
|
|
816
|
+
invokes PowerShell/cmd underneath.)"""
|
|
817
|
+
import os as _os
|
|
818
|
+
import sys as _sys
|
|
819
|
+
|
|
820
|
+
from drydock import tools
|
|
821
|
+
kind, path = tools._detect_shell()
|
|
822
|
+
lines = [
|
|
823
|
+
f"The Bash tool runs your commands through: {kind.upper()}",
|
|
824
|
+
f" shell path : {path}",
|
|
825
|
+
f" os.name={_os.name} sys.platform={_sys.platform} detected_windows={tools._IS_WINDOWS}",
|
|
826
|
+
f" DRYDOCK_SHELL env = {_os.environ.get('DRYDOCK_SHELL') or '(unset)'}",
|
|
827
|
+
f" bash on system = {tools._detect_bash() or '(none)'}",
|
|
828
|
+
]
|
|
829
|
+
if kind == "bash" and tools._IS_WINDOWS:
|
|
830
|
+
lines.append(" ⚠ on Windows but using bash — set $env:DRYDOCK_SHELL=\"powershell\" and restart.")
|
|
831
|
+
elif kind == "powershell":
|
|
832
|
+
lines.append(" ✓ using PowerShell (the 'Bash' label is just the tool's name).")
|
|
833
|
+
lines.append(" Force a shell any time: set env DRYDOCK_SHELL to powershell / cmd / bash.")
|
|
834
|
+
self._info("\n".join(lines))
|
|
835
|
+
|
|
810
836
|
def _cmd_context(self, arg: str) -> None:
|
|
811
837
|
"""View or change the context-window budget (tokens) — the cap that drives
|
|
812
838
|
the ctx gauge + auto-compaction. `/context` shows it + its source; `/context
|
|
@@ -153,7 +153,7 @@ class PromptHistory:
|
|
|
153
153
|
# completes the prefix.
|
|
154
154
|
SLASH_COMMANDS = [
|
|
155
155
|
"/help", "/model", "/cwd", "/undo", "/back", "/stop", "/status",
|
|
156
|
-
"/compact", "/context", "/advisor", "/ask", "/ask!", "/graphrag", "/skills",
|
|
156
|
+
"/compact", "/context", "/shell", "/advisor", "/ask", "/ask!", "/graphrag", "/skills",
|
|
157
157
|
"/loop", "/mcp", "/rmf", "/stig",
|
|
158
158
|
"/clear", "/quit",
|
|
159
159
|
]
|
|
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
|
|
|
7
7
|
# PyPI distribution name is drydock-cli (the established install name, continued
|
|
8
8
|
# from the retired v2 fork); the import package + CLI command stay `drydock`.
|
|
9
9
|
name = "drydock-cli"
|
|
10
|
-
version = "3.0.
|
|
10
|
+
version = "3.0.110"
|
|
11
11
|
description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.11"
|
|
@@ -32,21 +32,16 @@ def test_powershell_invocation_argv(monkeypatch):
|
|
|
32
32
|
"""When the shell is PowerShell, Popen is called [pwsh -NoProfile
|
|
33
33
|
-NonInteractive -Command <cmd>] with shell=False — not shell=True."""
|
|
34
34
|
seen = {}
|
|
35
|
-
real = subprocess.Popen
|
|
36
35
|
|
|
37
|
-
class FakeProc:
|
|
38
|
-
stdout = None
|
|
39
|
-
pid = 1
|
|
40
|
-
def wait(self): return 0
|
|
41
|
-
def poll(self): return 0
|
|
42
36
|
def spy(popen_cmd, *a, **kw):
|
|
43
|
-
seen["cmd"] = popen_cmd
|
|
37
|
+
seen["cmd"] = popen_cmd
|
|
38
|
+
seen["shell"] = kw.get("shell")
|
|
44
39
|
raise RuntimeError("stop-after-capture") # don't actually run pwsh on Linux
|
|
45
40
|
|
|
46
41
|
monkeypatch.setattr(T, "_SHELL_KIND", "powershell")
|
|
47
42
|
monkeypatch.setattr(T, "_SHELL_PATH", r"C:\ps\pwsh.exe")
|
|
48
43
|
monkeypatch.setattr(subprocess, "Popen", spy)
|
|
49
|
-
|
|
44
|
+
T.tool_bash({"command": "Get-ChildItem", "timeout": 5}, {"cwd": "."})
|
|
50
45
|
assert seen["shell"] is False
|
|
51
46
|
assert seen["cmd"][:4] == [r"C:\ps\pwsh.exe", "-NoProfile", "-NonInteractive", "-Command"]
|
|
52
47
|
assert seen["cmd"][4] == "Get-ChildItem"
|
|
@@ -65,3 +60,35 @@ def test_prompt_note_windows_cmd():
|
|
|
65
60
|
|
|
66
61
|
def test_prompt_note_empty_on_posix():
|
|
67
62
|
assert tuning._shell_env_note() == ""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_env_override_forces_powershell():
|
|
66
|
+
with mock.patch.dict("os.environ", {"DRYDOCK_SHELL": "powershell"}), \
|
|
67
|
+
mock.patch("shutil.which", side_effect=lambda n: r"C:\pwsh.exe" if n in ("pwsh", "powershell") else None):
|
|
68
|
+
assert T._detect_shell()[0] == "powershell"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_env_override_forces_cmd():
|
|
72
|
+
with mock.patch.dict("os.environ", {"DRYDOCK_SHELL": "cmd"}), \
|
|
73
|
+
mock.patch("shutil.which", return_value=r"C:\cmd.exe"):
|
|
74
|
+
assert T._detect_shell()[0] == "cmd"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_env_override_forces_bash():
|
|
78
|
+
with mock.patch.dict("os.environ", {"DRYDOCK_SHELL": "bash"}):
|
|
79
|
+
assert T._detect_shell()[0] in ("bash", "sh")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_is_windows_detection_signals():
|
|
83
|
+
from drydock.tools import _is_windows_env
|
|
84
|
+
# native Windows
|
|
85
|
+
assert _is_windows_env("nt", "win32", {})
|
|
86
|
+
# MSYS/Git-Bash Python: posix os.name but WINDIR set
|
|
87
|
+
assert _is_windows_env("posix", "msys", {"WINDIR": r"C:\Windows"})
|
|
88
|
+
# Cygwin
|
|
89
|
+
assert _is_windows_env("posix", "cygwin", {})
|
|
90
|
+
# SystemRoot fallback
|
|
91
|
+
assert _is_windows_env("posix", "linux", {"SystemRoot": r"C:\Windows"})
|
|
92
|
+
# real Linux / WSL (no Windows env)
|
|
93
|
+
assert not _is_windows_env("posix", "linux", {})
|
|
94
|
+
assert not _is_windows_env("posix", "linux", {"HOME": "/home/x"})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|