feat(docker): add macOS Docker Desktop socket auto-detection

Probe ~/.docker/run/docker.sock for RuntimeDocker and RuntimeAuto
before falling back to /var/run/docker.sock. This lets the agent
connect on macOS without requiring DOCKER_HOST to be set manually.

Ref #1200
This commit is contained in:
Surendra Raika 2026-02-18 19:23:14 +05:30
parent 5666d6a9e8
commit f663aade53
3 changed files with 85 additions and 2 deletions

View file

@ -180,6 +180,18 @@ You can also toggle "Hide Docker Update Buttons" from the UI: **Settings → Age
---
## 🍎 macOS Docker Desktop Socket
On macOS, Docker Desktop exposes a socket at `~/.docker/run/docker.sock`. Pulse auto-detects this path when `--enable-docker` is used with `RuntimeDocker` or `RuntimeAuto`, so no extra configuration is needed.
To override, set `DOCKER_HOST`:
```bash
export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock
```
---
## 🛠️ Troubleshooting
- **Forgot Password?**

View file

@ -11,6 +11,7 @@ import (
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
@ -508,6 +509,14 @@ func buildRuntimeCandidates(preference RuntimeKind) []runtimeCandidate {
label: "docker rootless socket",
})
// macOS Docker Desktop socket
if home := os.Getenv("HOME"); home != "" {
add(runtimeCandidate{
host: "unix://" + filepath.Join(home, ".docker", "run", "docker.sock"),
label: "docker desktop socket",
})
}
add(runtimeCandidate{
host: "unix:///var/run/docker.sock",
label: "default docker socket",

View file

@ -927,12 +927,12 @@ func TestBuildRuntimeCandidates(t *testing.T) {
{
name: "auto includes both podman and docker sockets",
preference: RuntimeAuto,
wantMin: 3, // at least env defaults + podman rootless + docker default
wantMin: 4, // at least env defaults + podman rootless + docker rootless + docker desktop + docker default
},
{
name: "docker preference includes docker socket",
preference: RuntimeDocker,
wantMin: 2, // at least env defaults + docker default
wantMin: 3, // at least env defaults + docker rootless + docker desktop + docker default
},
{
name: "podman preference includes podman sockets",
@ -1052,6 +1052,68 @@ func TestBuildRuntimeCandidatesDockerRootlessOrder(t *testing.T) {
})
}
func TestBuildRuntimeCandidatesDockerDesktop(t *testing.T) {
t.Run("docker includes docker desktop socket", func(t *testing.T) {
candidates := buildRuntimeCandidates(RuntimeDocker)
found := false
for _, c := range candidates {
if c.label == "docker desktop socket" {
found = true
break
}
}
if !found {
t.Error("docker preference should include docker desktop socket")
}
})
t.Run("auto includes docker desktop socket", func(t *testing.T) {
candidates := buildRuntimeCandidates(RuntimeAuto)
found := false
for _, c := range candidates {
if c.label == "docker desktop socket" {
found = true
break
}
}
if !found {
t.Error("auto preference should include docker desktop socket")
}
})
t.Run("podman excludes docker desktop socket", func(t *testing.T) {
candidates := buildRuntimeCandidates(RuntimePodman)
for _, c := range candidates {
if c.label == "docker desktop socket" {
t.Error("podman preference should not include docker desktop socket")
}
}
})
t.Run("desktop socket comes before default socket", func(t *testing.T) {
candidates := buildRuntimeCandidates(RuntimeDocker)
desktopIdx := -1
defaultIdx := -1
for i, c := range candidates {
if c.label == "docker desktop socket" && desktopIdx < 0 {
desktopIdx = i
}
if c.label == "default docker socket" && defaultIdx < 0 {
defaultIdx = i
}
}
if desktopIdx < 0 {
t.Fatal("expected docker desktop socket candidate")
}
if defaultIdx < 0 {
t.Fatal("expected default docker socket candidate")
}
if desktopIdx > defaultIdx {
t.Fatalf("expected docker desktop socket before default; desktopIdx=%d defaultIdx=%d", desktopIdx, defaultIdx)
}
})
}
func TestBuildRuntimeCandidatesEnv(t *testing.T) {
t.Setenv("DOCKER_HOST", "unix:///tmp/docker.sock")
t.Setenv("CONTAINER_HOST", "unix:///tmp/container.sock")