Skip to content

Commit 3511311

Browse files
committed
fix: suggest dockerless dev when docker unavailable
1 parent cef8367 commit 3511311

1 file changed

Lines changed: 112 additions & 1 deletion

File tree

apps/framework-cli/src/cli.rs

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,42 @@ async fn run_local_infrastructure_with_timeout(
473473
}
474474
}
475475

476+
const DEV_DOCKERLESS_HINT: &str =
477+
"Docker is unavailable. For local development without Docker, rerun: moose dev --dockerless";
478+
479+
fn format_local_infrastructure_error(
480+
error: &anyhow::Error,
481+
include_dockerless_hint: bool,
482+
) -> String {
483+
let diagnostics = format!("{error:?}");
484+
let existing_message = format!("Failed to run local infrastructure: {diagnostics}");
485+
486+
if include_dockerless_hint && is_container_runtime_unavailable_error(&diagnostics) {
487+
format!("{DEV_DOCKERLESS_HINT}\n\n{existing_message}")
488+
} else {
489+
existing_message
490+
}
491+
}
492+
493+
fn is_container_runtime_unavailable_error(diagnostics: &str) -> bool {
494+
let diagnostics = diagnostics.to_ascii_lowercase();
495+
[
496+
"failed to run docker commands",
497+
"to ensure docker is running",
498+
"is docker running",
499+
"cannot connect to the docker daemon",
500+
"docker daemon",
501+
"no such file or directory: docker",
502+
"no such file or directory: finch",
503+
"os error 2: docker",
504+
"os error 2: finch",
505+
"finch is not running",
506+
"cannot connect to the finch vm",
507+
]
508+
.iter()
509+
.any(|phrase| diagnostics.contains(phrase))
510+
}
511+
476512
pub async fn top_command_handler(
477513
settings: Settings,
478514
commands: &Commands,
@@ -744,7 +780,7 @@ pub async fn top_command_handler(
744780
.map_err(|e| {
745781
RoutineFailure::error(Message {
746782
action: "Dev".to_string(),
747-
details: format!("Failed to run local infrastructure: {e:?}"),
783+
details: format_local_infrastructure_error(&e, !*dockerless),
748784
})
749785
})?;
750786
} else {
@@ -2389,6 +2425,81 @@ mod tests {
23892425

23902426
use super::*;
23912427

2428+
#[test]
2429+
fn dev_infrastructure_error_suggests_dockerless_when_docker_binary_is_missing() {
2430+
let error = anyhow::anyhow!("Failed: No such file or directory: docker (os error 2)");
2431+
2432+
let details = format_local_infrastructure_error(&error, true);
2433+
2434+
assert!(details.contains("moose dev --dockerless"));
2435+
assert!(details.contains("Failed: No such file or directory: docker (os error 2)"));
2436+
}
2437+
2438+
#[test]
2439+
fn dev_infrastructure_error_suggests_dockerless_when_wrapped_docker_check_fails() {
2440+
let error = anyhow::anyhow!(
2441+
"Failed: to ensure docker is running\nCaused by:\n No such file or directory (os error 2)"
2442+
);
2443+
2444+
let details = format_local_infrastructure_error(&error, true);
2445+
2446+
assert!(details.contains("moose dev --dockerless"));
2447+
assert!(details.contains("to ensure docker is running"));
2448+
}
2449+
2450+
#[test]
2451+
fn dev_infrastructure_error_suggests_dockerless_when_docker_daemon_is_unreachable() {
2452+
let error = anyhow::anyhow!(
2453+
"Failed: Cannot connect to the Docker daemon at unix:///var/run/docker.sock"
2454+
);
2455+
2456+
let details = format_local_infrastructure_error(&error, true);
2457+
2458+
assert!(details.contains("moose dev --dockerless"));
2459+
assert!(details.contains("Cannot connect to the Docker daemon"));
2460+
}
2461+
2462+
#[test]
2463+
fn local_infrastructure_error_does_not_suggest_dockerless_for_non_dev_paths() {
2464+
let error = anyhow::anyhow!("Failed: Cannot connect to the Docker daemon");
2465+
2466+
let details = format_local_infrastructure_error(&error, false);
2467+
2468+
assert!(!details.contains("moose dev --dockerless"));
2469+
assert_eq!(
2470+
details,
2471+
"Failed to run local infrastructure: Failed: Cannot connect to the Docker daemon"
2472+
);
2473+
}
2474+
2475+
#[test]
2476+
fn dev_infrastructure_error_does_not_suggest_dockerless_for_non_runtime_errors() {
2477+
let error = anyhow::anyhow!("Failed: ClickHouse container did not become healthy");
2478+
2479+
let details = format_local_infrastructure_error(&error, true);
2480+
2481+
assert!(!details.contains("moose dev --dockerless"));
2482+
assert_eq!(
2483+
details,
2484+
"Failed to run local infrastructure: Failed: ClickHouse container did not become healthy"
2485+
);
2486+
}
2487+
2488+
#[test]
2489+
fn dev_infrastructure_error_does_not_suggest_dockerless_for_unrelated_missing_files() {
2490+
let error = anyhow::anyhow!(
2491+
"Failed: unable to read config.yaml: No such file or directory (os error 2)"
2492+
);
2493+
2494+
let details = format_local_infrastructure_error(&error, true);
2495+
2496+
assert!(!details.contains("moose dev --dockerless"));
2497+
assert_eq!(
2498+
details,
2499+
"Failed to run local infrastructure: Failed: unable to read config.yaml: No such file or directory (os error 2)"
2500+
);
2501+
}
2502+
23922503
fn set_test_temp_dir() {
23932504
let test_dir = "tests/tmp";
23942505
// check that the directory isn't already set to test_dir

0 commit comments

Comments
 (0)