Skip to content

Commit 9a677d8

Browse files
committed
fix(bootstrap): detect Docker Desktop socket fallback on macOS
Signed-off-by: anh nguyen <anh.nqqq@icloud.com> Signed-off-by: areporeporepo <areporeporepo@users.noreply.github.com>
1 parent 1d071b8 commit 9a677d8

File tree

1 file changed

+58
-4
lines changed

1 file changed

+58
-4
lines changed

crates/openshell-bootstrap/src/docker.rs

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,13 @@ pub struct DockerPreflight {
107107
/// - `/var/run/docker.sock` — default for Docker Desktop, `OrbStack`, Colima
108108
/// - `$HOME/.colima/docker.sock` — Colima (older installs)
109109
/// - `$HOME/.orbstack/run/docker.sock` — `OrbStack` (if symlink is missing)
110+
/// - `$HOME/Library/Containers/com.docker.docker/Data/docker-cli.sock` — Docker Desktop on macOS
110111
const WELL_KNOWN_SOCKET_PATHS: &[&str] = &[
111112
"/var/run/docker.sock",
112113
// Expanded at runtime via home_dir():
113114
// ~/.colima/docker.sock
114115
// ~/.orbstack/run/docker.sock
116+
// ~/Library/Containers/com.docker.docker/Data/docker-cli.sock
115117
];
116118

117119
/// Check that a Docker-compatible runtime is installed, running, and reachable.
@@ -120,11 +122,18 @@ const WELL_KNOWN_SOCKET_PATHS: &[&str] = &[
120122
/// deploy work begins. On failure it produces a user-friendly error with
121123
/// actionable recovery steps instead of a raw bollard connection error.
122124
pub async fn check_docker_available() -> Result<DockerPreflight> {
125+
if let Some(preflight) = try_alternative_sockets(None).await {
126+
return Ok(preflight);
127+
}
128+
123129
// Step 1: Try to connect using bollard's default resolution
124130
// (respects DOCKER_HOST, then falls back to /var/run/docker.sock).
125131
let docker = match Docker::connect_with_local_defaults() {
126132
Ok(d) => d,
127133
Err(err) => {
134+
if let Some(preflight) = try_alternative_sockets(None).await {
135+
return Ok(preflight);
136+
}
128137
return Err(docker_not_reachable_error(
129138
&format!("{err}"),
130139
"Failed to create Docker client",
@@ -134,6 +143,9 @@ pub async fn check_docker_available() -> Result<DockerPreflight> {
134143

135144
// Step 2: Ping the daemon to confirm it's responsive.
136145
if let Err(err) = docker.ping().await {
146+
if let Some(preflight) = try_alternative_sockets(Some("/var/run/docker.sock")).await {
147+
return Ok(preflight);
148+
}
137149
return Err(docker_not_reachable_error(
138150
&format!("{err}"),
139151
"Docker socket exists but the daemon is not responding",
@@ -149,6 +161,30 @@ pub async fn check_docker_available() -> Result<DockerPreflight> {
149161
Ok(DockerPreflight { docker, version })
150162
}
151163

164+
async fn try_alternative_sockets(skip_path: Option<&str>) -> Option<DockerPreflight> {
165+
if env_non_empty("DOCKER_HOST").is_some() {
166+
return None;
167+
}
168+
169+
for path in find_alternative_sockets() {
170+
if skip_path.is_some_and(|skip| skip == path) {
171+
continue;
172+
}
173+
174+
let Ok(docker) = Docker::connect_with_socket(&path, 120, API_DEFAULT_VERSION) else {
175+
continue;
176+
};
177+
if docker.ping().await.is_err() {
178+
continue;
179+
}
180+
181+
let version = docker.version().await.ok().and_then(|v| v.version);
182+
return Some(DockerPreflight { docker, version });
183+
}
184+
185+
None
186+
}
187+
152188
/// Build a rich, user-friendly error when Docker is not reachable.
153189
fn docker_not_reachable_error(raw_err: &str, summary: &str) -> miette::Report {
154190
let docker_host = std::env::var("DOCKER_HOST").ok();
@@ -218,10 +254,7 @@ fn find_alternative_sockets() -> Vec<String> {
218254

219255
// Check home-relative paths
220256
if let Some(home) = home_dir() {
221-
let home_sockets = [
222-
format!("{home}/.colima/docker.sock"),
223-
format!("{home}/.orbstack/run/docker.sock"),
224-
];
257+
let home_sockets = home_relative_socket_paths(&home);
225258
for path in &home_sockets {
226259
if std::path::Path::new(path).exists() && !found.contains(path) {
227260
found.push(path.clone());
@@ -236,6 +269,14 @@ fn home_dir() -> Option<String> {
236269
std::env::var("HOME").ok()
237270
}
238271

272+
fn home_relative_socket_paths(home: &str) -> Vec<String> {
273+
vec![
274+
format!("{home}/.colima/docker.sock"),
275+
format!("{home}/.orbstack/run/docker.sock"),
276+
format!("{home}/Library/Containers/com.docker.docker/Data/docker-cli.sock"),
277+
]
278+
}
279+
239280
/// Create an SSH Docker client from remote options.
240281
pub async fn create_ssh_docker_client(remote: &RemoteOptions) -> Result<Docker> {
241282
// Ensure destination has ssh:// prefix
@@ -1195,4 +1236,17 @@ mod tests {
11951236
"should return a reasonable number of sockets"
11961237
);
11971238
}
1239+
1240+
#[test]
1241+
fn home_relative_socket_paths_include_docker_desktop_socket() {
1242+
let home = "/tmp/test-home";
1243+
let sockets = home_relative_socket_paths(home);
1244+
1245+
assert!(
1246+
sockets.contains(&format!(
1247+
"{home}/Library/Containers/com.docker.docker/Data/docker-cli.sock"
1248+
)),
1249+
"should probe Docker Desktop's macOS socket path"
1250+
);
1251+
}
11981252
}

0 commit comments

Comments
 (0)