fix(deploy): prevent Git Bash path conversion of default docker socket on Windows (#5400) (#5402)

* fix(deploy): prevent Git Bash path conversion of default docker socket on Windows (#5400)

* fix(deploy): resolve docker socket from dotenv and assert custom socket preservation

* docs: trim backend guidance to satisfy inherited size budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
Dan Caldr 2026-09-14 00:51:27 +02:00 committed by GitHub
parent c3adc51ec9
commit f66cb8e376
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 161 additions and 7 deletions

View File

@ -610,3 +610,151 @@ def test_aio_deploy_socket_preflight_rejects_windows_when_docker_unreachable(tmp
assert result.returncode == 1, result.stdout + result.stderr
assert "Docker socket not found" in result.stdout + result.stderr
def test_aio_deploy_socket_unsets_default_on_windows(tmp_path):
"""deploy.sh on Windows Git Bash unsets default /var/run/docker.sock before calling Compose."""
worktree = _setup_deploy_worktree(tmp_path)
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
capture_socket_env = tmp_path / "docker_socket_env.txt"
docker = bin_dir / "docker"
docker.write_text(
f'#!/usr/bin/env sh\nif [ "$1" = "info" ]; then exit 0; fi\nprintf "%s" "$DEER_FLOW_DOCKER_SOCKET" > "{capture_socket_env}"\nexit 0\n',
encoding="utf-8",
)
docker.chmod(0o755)
bash_env = tmp_path / "env.sh"
bash_env.write_text("uname() { echo 'MINGW64_NT-10.0'; }\n", encoding="utf-8")
env = os.environ.copy()
env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}"
env["BASH_ENV"] = str(bash_env)
env["DEER_FLOW_DOCKER_SOCKET"] = "/var/run/docker.sock"
env["BETTER_AUTH_SECRET"] = "test-secret"
env["DEER_FLOW_INTERNAL_AUTH_TOKEN"] = "test-token"
env["UV_EXTRAS"] = "redis"
result = subprocess.run(
[BASH_EXECUTABLE, str(worktree / "scripts" / "deploy.sh"), "start"],
cwd=worktree,
env=env,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
assert capture_socket_env.read_text(encoding="utf-8") == ""
def test_aio_deploy_socket_preserves_unset_default_on_windows(tmp_path):
"""deploy.sh does not export default /var/run/docker.sock when unset initially."""
worktree = _setup_deploy_worktree(tmp_path)
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
capture_socket_env = tmp_path / "docker_socket_env.txt"
docker = bin_dir / "docker"
docker.write_text(
f'#!/usr/bin/env sh\nif [ "$1" = "info" ]; then exit 0; fi\nprintf "%s" "$DEER_FLOW_DOCKER_SOCKET" > "{capture_socket_env}"\nexit 0\n',
encoding="utf-8",
)
docker.chmod(0o755)
bash_env = tmp_path / "env.sh"
bash_env.write_text("uname() { echo 'MINGW64_NT-10.0'; }\n", encoding="utf-8")
env = os.environ.copy()
env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}"
env["BASH_ENV"] = str(bash_env)
env.pop("DEER_FLOW_DOCKER_SOCKET", None)
env["BETTER_AUTH_SECRET"] = "test-secret"
env["DEER_FLOW_INTERNAL_AUTH_TOKEN"] = "test-token"
env["UV_EXTRAS"] = "redis"
result = subprocess.run(
[BASH_EXECUTABLE, str(worktree / "scripts" / "deploy.sh"), "start"],
cwd=worktree,
env=env,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
assert capture_socket_env.read_text(encoding="utf-8") == ""
def test_aio_deploy_socket_preserves_custom_socket_on_windows(tmp_path):
"""deploy.sh preserves and exports custom DEER_FLOW_DOCKER_SOCKET on Windows."""
worktree = _setup_deploy_worktree(tmp_path)
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
capture_socket_env = tmp_path / "docker_socket_env.txt"
docker = bin_dir / "docker"
docker.write_text(
f'#!/usr/bin/env sh\nif [ "$1" = "info" ]; then exit 0; fi\nprintf "%s" "$DEER_FLOW_DOCKER_SOCKET" > "{capture_socket_env}"\nexit 0\n',
encoding="utf-8",
)
docker.chmod(0o755)
bash_env = tmp_path / "env.sh"
bash_env.write_text(
'uname() { echo "MINGW64_NT-10.0"; }\n[() {\n if [[ "$1" == "!" && "$2" == "-S" ]]; then\n return 1\n fi\n builtin [ "$@"\n}\n',
encoding="utf-8",
)
env = os.environ.copy()
env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}"
env["BASH_ENV"] = str(bash_env)
env["DEER_FLOW_DOCKER_SOCKET"] = "/custom/docker.sock"
env["BETTER_AUTH_SECRET"] = "test-secret"
env["DEER_FLOW_INTERNAL_AUTH_TOKEN"] = "test-token"
env["UV_EXTRAS"] = "redis"
result = subprocess.run(
[BASH_EXECUTABLE, str(worktree / "scripts" / "deploy.sh"), "start"],
cwd=worktree,
env=env,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
assert capture_socket_env.read_text(encoding="utf-8") == "/custom/docker.sock"
def test_aio_deploy_socket_reads_from_dotenv(tmp_path):
"""deploy.sh resolves DEER_FLOW_DOCKER_SOCKET from .env when unset in shell environment."""
worktree = _setup_deploy_worktree(tmp_path)
(worktree / ".env").write_text("DEER_FLOW_DOCKER_SOCKET=/var/run/docker.sock\n", encoding="utf-8")
bin_dir = tmp_path / "bin"
bin_dir.mkdir()
capture_socket_env = tmp_path / "docker_socket_env.txt"
docker = bin_dir / "docker"
docker.write_text(
f'#!/usr/bin/env sh\nif [ "$1" = "info" ]; then exit 0; fi\nprintf "%s" "$DEER_FLOW_DOCKER_SOCKET" > "{capture_socket_env}"\nexit 0\n',
encoding="utf-8",
)
docker.chmod(0o755)
bash_env = tmp_path / "env.sh"
bash_env.write_text("uname() { echo 'MINGW64_NT-10.0'; }\n", encoding="utf-8")
env = os.environ.copy()
env["PATH"] = f"{bin_dir}{os.pathsep}{env['PATH']}"
env["BASH_ENV"] = str(bash_env)
env.pop("DEER_FLOW_DOCKER_SOCKET", None)
env["BETTER_AUTH_SECRET"] = "test-secret"
env["DEER_FLOW_INTERNAL_AUTH_TOKEN"] = "test-token"
env["UV_EXTRAS"] = "redis"
result = subprocess.run(
[BASH_EXECUTABLE, str(worktree / "scripts" / "deploy.sh"), "start"],
cwd=worktree,
env=env,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stdout + result.stderr
assert capture_socket_env.read_text(encoding="utf-8") == ""

View File

@ -367,23 +367,29 @@ fi
# appended here, so the default (local) and provisioner modes never expose the
# host daemon. Mounting the socket = root-equivalent host control; see SECURITY.md.
if [ -z "$DEER_FLOW_DOCKER_SOCKET" ]; then
export DEER_FLOW_DOCKER_SOCKET="/var/run/docker.sock"
fi
docker_socket="$(read_dotenv_value DEER_FLOW_DOCKER_SOCKET)"
docker_socket="${docker_socket:-/var/run/docker.sock}"
if [ "$sandbox_mode" = "aio" ]; then
if [ ! -S "$DEER_FLOW_DOCKER_SOCKET" ]; then
if [ ! -S "$docker_socket" ]; then
# On Windows (Git Bash / MSYS), Docker Desktop mounts the default
# /var/run/docker.sock into containers even though no host socket file exists.
if [ "$DEER_FLOW_DOCKER_SOCKET" = "/var/run/docker.sock" ] && [[ "$(uname -s)" =~ ^(MINGW|MSYS|CYGWIN) ]] && docker info >/dev/null 2>&1; then
if [ "$docker_socket" = "/var/run/docker.sock" ] && [[ "$(uname -s)" =~ ^(MINGW|MSYS|CYGWIN) ]] && docker info >/dev/null 2>&1; then
:
else
echo -e "${RED}⚠ Docker socket not found at $DEER_FLOW_DOCKER_SOCKET${NC}"
echo -e "${RED}⚠ Docker socket not found at $docker_socket${NC}"
echo " AioSandboxProvider (DooD) will not work."
exit 1
fi
fi
echo -e "${GREEN}✓ Docker socket: $DEER_FLOW_DOCKER_SOCKET${NC}"
# On Windows (Git Bash / MSYS), exporting /var/run/docker.sock causes MSYS to
# convert it to C:\Program Files\Git\var\run\docker.sock when invoking native
# docker compose, triggering mkdir errors. Unsetting the default allows Compose
# to evaluate its own default literal fallback (${DEER_FLOW_DOCKER_SOCKET:-/var/run/docker.sock}).
if [[ "$(uname -s)" =~ ^(MINGW|MSYS|CYGWIN) ]] && [ "$DEER_FLOW_DOCKER_SOCKET" = "/var/run/docker.sock" ]; then
unset DEER_FLOW_DOCKER_SOCKET
fi
echo -e "${GREEN}✓ Docker socket: $docker_socket${NC}"
echo -e "${YELLOW} Mounting host Docker socket into gateway (DooD = host root-equivalent). See SECURITY.md.${NC}"
COMPOSE_CMD+=(-f "$DOCKER_DIR/docker-compose.dood.yaml")
fi