@@ -315,11 +315,36 @@ fn runtime_not_reachable_error(
315315 "No container runtime socket found and {} is not set." ,
316316 host_env
317317 ) ) ;
318- hints. push ( format ! (
319- "Install and start {}. See the support matrix \
320- in the OpenShell docs for tested configurations.",
321- runtime. display_name( )
322- ) ) ;
318+ // Check if the binary is installed but the socket is missing —
319+ // this typically means the systemd socket unit needs activation.
320+ if crate :: container_runtime:: has_binary ( runtime. binary_name ( ) ) {
321+ match runtime {
322+ ContainerRuntime :: Podman => {
323+ hints. push (
324+ "Podman is installed but its API socket is not active.\n \n \
325+ Enable the Podman socket with:\n \n \
326+ sudo systemctl enable --now podman.socket\n \n \
327+ For rootless Podman, run as your regular user:\n \n \
328+ systemctl --user enable --now podman.socket"
329+ . to_string ( ) ,
330+ ) ;
331+ }
332+ ContainerRuntime :: Docker => {
333+ hints. push (
334+ "Docker is installed but its daemon is not running.\n \n \
335+ Start Docker with:\n \n \
336+ sudo systemctl start docker"
337+ . to_string ( ) ,
338+ ) ;
339+ }
340+ }
341+ } else {
342+ hints. push ( format ! (
343+ "Install and start {}. See the support matrix \
344+ in the OpenShell docs for tested configurations.",
345+ runtime. display_name( )
346+ ) ) ;
347+ }
323348 } else {
324349 // Found sockets for possibly a different runtime
325350 let socket_list: Vec < String > = alt_sockets
@@ -1508,6 +1533,84 @@ mod tests {
15081533 ) ;
15091534 }
15101535
1536+ #[ test]
1537+ fn runtime_not_reachable_error_podman_socket_hint ( ) {
1538+ // When Podman is installed but no socket is found, the error should
1539+ // suggest enabling the systemd socket unit instead of "Install Podman".
1540+ //
1541+ // The exact hint branch depends on system state:
1542+ // - Podman installed + sockets exist → "Found container runtime socket(s)" hint
1543+ // - Podman installed + no sockets → "systemctl enable --now podman.socket" hint
1544+ // - Podman not installed → "Install and start Podman" hint
1545+ //
1546+ // Clear env vars that would cause the function to take the "env var
1547+ // is set" branch instead of the "no socket" branch (other tests in
1548+ // this module may set DOCKER_HOST concurrently).
1549+ let prev_container_host = std:: env:: var ( "CONTAINER_HOST" ) . ok ( ) ;
1550+ let prev_docker_host = std:: env:: var ( "DOCKER_HOST" ) . ok ( ) ;
1551+ // SAFETY: test-only, single-threaded for this test
1552+ unsafe {
1553+ std:: env:: remove_var ( "CONTAINER_HOST" ) ;
1554+ std:: env:: remove_var ( "DOCKER_HOST" ) ;
1555+ }
1556+
1557+ let has_podman = crate :: container_runtime:: has_binary ( "podman" ) ;
1558+ let sockets = find_all_sockets ( ) ;
1559+
1560+ let err = runtime_not_reachable_error (
1561+ ContainerRuntime :: Podman ,
1562+ "no Podman socket found at standard locations" ,
1563+ "Failed to connect to Podman" ,
1564+ ) ;
1565+ let msg = format ! ( "{err:?}" ) ;
1566+
1567+ // Restore env
1568+ // SAFETY: test-only, restoring previous state
1569+ unsafe {
1570+ match prev_container_host {
1571+ Some ( val) => std:: env:: set_var ( "CONTAINER_HOST" , val) ,
1572+ None => std:: env:: remove_var ( "CONTAINER_HOST" ) ,
1573+ }
1574+ match prev_docker_host {
1575+ Some ( val) => std:: env:: set_var ( "DOCKER_HOST" , val) ,
1576+ None => std:: env:: remove_var ( "DOCKER_HOST" ) ,
1577+ }
1578+ }
1579+
1580+ if !sockets. is_empty ( ) {
1581+ // Sockets exist — the function takes the "found sockets" branch
1582+ assert ! (
1583+ msg. contains( "Found container runtime socket" ) ,
1584+ "should list found sockets when they exist: {msg}"
1585+ ) ;
1586+ } else if has_podman {
1587+ // No sockets, but Podman binary is on PATH — new hint path
1588+ assert ! (
1589+ msg. contains( "systemctl" ) ,
1590+ "should suggest systemctl when Podman is installed but socket is missing: {msg}"
1591+ ) ;
1592+ assert ! (
1593+ msg. contains( "podman.socket" ) ,
1594+ "should mention podman.socket unit: {msg}"
1595+ ) ;
1596+ assert ! (
1597+ !msg. contains( "Install and start Podman" ) ,
1598+ "should NOT say 'Install and start' when Podman is already installed: {msg}"
1599+ ) ;
1600+ } else {
1601+ // No sockets, no Podman binary — fallback install hint
1602+ assert ! (
1603+ msg. contains( "Install and start Podman" ) ,
1604+ "should say 'Install and start' when Podman is not installed: {msg}"
1605+ ) ;
1606+ }
1607+ // Regardless of system state, the verification hint must be present
1608+ assert ! (
1609+ msg. contains( "podman info" ) ,
1610+ "should always suggest 'podman info' verification: {msg}"
1611+ ) ;
1612+ }
1613+
15111614 #[ test]
15121615 fn runtime_not_reachable_error_with_env_var ( ) {
15131616 // Simulate: DOCKER_HOST is set but daemon unresponsive.
0 commit comments