diff --git a/crates/openshell-driver-docker/src/lib.rs b/crates/openshell-driver-docker/src/lib.rs index a74609fb5..7069347db 100644 --- a/crates/openshell-driver-docker/src/lib.rs +++ b/crates/openshell-driver-docker/src/lib.rs @@ -1099,7 +1099,7 @@ fn docker_gateway_route( }; } - if is_docker_desktop(info) { + if is_compat_docker_runtime(info) { DockerGatewayRoute::HostGateway } else { DockerGatewayRoute::Bridge { @@ -1109,7 +1109,15 @@ fn docker_gateway_route( } } -fn is_docker_desktop(info: &SystemInfo) -> bool { +/// Detect Docker Desktop and behaviourally compatible runtimes — Colima, +/// Lima, Rancher Desktop, and `OrbStack` — that share Docker Desktop's +/// routing constraint: the bridge gateway IP is reachable from inside +/// containers but not from the `OpenShell` server process running on the +/// host, so callbacks must traverse `host-gateway`. +/// +/// Each runtime is detected via the daemon's reported OS string or hostname, +/// supplemented by labels where the runtime publishes them. +fn is_compat_docker_runtime(info: &SystemInfo) -> bool { let operating_system = info .operating_system .as_deref() @@ -1119,10 +1127,25 @@ fn is_docker_desktop(info: &SystemInfo) -> bool { return true; } + let name = info + .name + .as_deref() + .unwrap_or_default() + .to_ascii_lowercase(); + if name.starts_with("colima") + || name.starts_with("lima-") + || name.starts_with("rancher-desktop") + || name.starts_with("orbstack") + { + return true; + } + info.labels.as_ref().is_some_and(|labels| { - labels - .iter() - .any(|label| label.starts_with("com.docker.desktop.")) + labels.iter().any(|label| { + label.starts_with("com.docker.desktop.") + || label.starts_with("dev.rancherdesktop.") + || label.starts_with("dev.orbstack.") + }) }) } diff --git a/crates/openshell-driver-docker/src/tests.rs b/crates/openshell-driver-docker/src/tests.rs index c89019398..5a4a86eb8 100644 --- a/crates/openshell-driver-docker/src/tests.rs +++ b/crates/openshell-driver-docker/src/tests.rs @@ -164,6 +164,88 @@ fn docker_gateway_route_uses_host_gateway_for_docker_desktop() { ); } +#[test] +fn docker_gateway_route_uses_host_gateway_for_colima() { + let info = SystemInfo { + operating_system: Some("Ubuntu 24.04 LTS".to_string()), + name: Some("colima".to_string()), + ..Default::default() + }; + + assert_eq!( + docker_gateway_route( + &info, + IpAddr::V4(Ipv4Addr::new(172, 18, 0, 1)), + DEFAULT_SERVER_PORT, + None, + ), + DockerGatewayRoute::HostGateway + ); +} + +#[test] +fn docker_gateway_route_uses_host_gateway_for_colima_named_profile() { + let info = SystemInfo { + operating_system: Some("Ubuntu 24.04 LTS".to_string()), + // `colima start --profile ` sets the daemon hostname to + // `colima-`; the prefix match still catches it. + name: Some("colima-default".to_string()), + ..Default::default() + }; + + assert_eq!( + docker_gateway_route( + &info, + IpAddr::V4(Ipv4Addr::new(172, 18, 0, 1)), + DEFAULT_SERVER_PORT, + None, + ), + DockerGatewayRoute::HostGateway + ); +} + +#[test] +fn docker_gateway_route_uses_host_gateway_for_rancher_desktop() { + let info = SystemInfo { + operating_system: Some("Alpine Linux v3.20".to_string()), + name: Some("lima-rancher-desktop".to_string()), + labels: Some(vec![ + "dev.rancherdesktop.profile=Rancher Desktop".to_string(), + ]), + ..Default::default() + }; + + assert_eq!( + docker_gateway_route( + &info, + IpAddr::V4(Ipv4Addr::new(172, 18, 0, 1)), + DEFAULT_SERVER_PORT, + None, + ), + DockerGatewayRoute::HostGateway + ); +} + +#[test] +fn docker_gateway_route_uses_host_gateway_for_orbstack() { + let info = SystemInfo { + operating_system: Some("OrbStack".to_string()), + name: Some("orbstack".to_string()), + labels: Some(vec!["dev.orbstack.machine_type=docker".to_string()]), + ..Default::default() + }; + + assert_eq!( + docker_gateway_route( + &info, + IpAddr::V4(Ipv4Addr::new(172, 18, 0, 1)), + DEFAULT_SERVER_PORT, + None, + ), + DockerGatewayRoute::HostGateway + ); +} + #[test] fn docker_gateway_route_uses_bridge_gateway_for_linux_docker() { let info = SystemInfo { diff --git a/mise.lock b/mise.lock index 600d64a37..f5d959069 100644 --- a/mise.lock +++ b/mise.lock @@ -244,8 +244,8 @@ backend = "core:python" precompiled_flavor = "install_only_stripped" [tools.python."platforms.linux-arm64"] -checksum = "sha256:de8a04be3e5caf443fe913fc3ac41f8e79ce93229015998b6153dcebf058c3be" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260508/cpython-3.13.13+20260508-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" +checksum = "sha256:67c837838c56a7d16187d1be9fad326a617e0b1ee2687e1a0dda0c85053dac33" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260510/cpython-3.13.13+20260510-aarch64-unknown-linux-gnu-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.linux-arm64-musl"] @@ -254,8 +254,8 @@ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20 provenance = "github-attestations" [tools.python."platforms.linux-x64"] -checksum = "sha256:ca958bfae9707fa66c8ad943b9ea068377cae0af471a70a30875ef99a6827586" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260508/cpython-3.13.13+20260508-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" +checksum = "sha256:bbe27549e475fe5f22d42a8e0d553dc79d80d8a00e05712599637857d287360e" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260510/cpython-3.13.13+20260510-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.linux-x64-musl"] @@ -264,8 +264,8 @@ url = "https://github.com/astral-sh/python-build-standalone/releases/download/20 provenance = "github-attestations" [tools.python."platforms.macos-arm64"] -checksum = "sha256:81482c1aef389d5d31349aa43a406453c0ac43c9ac02953390107c2a8b124230" -url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260508/cpython-3.13.13+20260508-aarch64-apple-darwin-install_only_stripped.tar.gz" +checksum = "sha256:16d2332d950178968534e65fe09f01f876d13af1147176fd0c77a74c9e4d1a4b" +url = "https://github.com/astral-sh/python-build-standalone/releases/download/20260510/cpython-3.13.13+20260510-aarch64-apple-darwin-install_only_stripped.tar.gz" provenance = "github-attestations" [tools.python."platforms.macos-x64"]