From f8b6ec1e9ec11da3ec445e6907d116dd49eee2d5 Mon Sep 17 00:00:00 2001 From: Henrik Kvamme Date: Fri, 17 Jul 2026 15:46:07 +0200 Subject: [PATCH] Stop non-Git projects with down all --- crates/cli/src/main.rs | 8 ++++ crates/cli/tests/down_cli.rs | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 crates/cli/tests/down_cli.rs diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 6e81f5f..a716432 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1712,6 +1712,14 @@ async fn down_all(timeout_secs: u64) -> anyhow::Result<()> { } } + // A freshly composed project is intentionally usable before `git init`. + // Worktree discovery has nothing to enumerate in that state, so always + // try the current instance socket as a fallback. If discovery already + // stopped it, reconnecting simply returns false. + if teardown_daemon(&socket_path(), timeout_secs, None).await? { + any = true; + } + // Every instance daemon is down now, so the shared services are free to // stop unconditionally (no sibling can still be relying on them). if let Ok(shared_sock) = devme_config::paths::shared_socket(&cwd) diff --git a/crates/cli/tests/down_cli.rs b/crates/cli/tests/down_cli.rs new file mode 100644 index 0000000..dfd05ca --- /dev/null +++ b/crates/cli/tests/down_cli.rs @@ -0,0 +1,72 @@ +use std::process::{Command, Output}; +use std::time::{Duration, Instant}; + +use tempfile::TempDir; + +fn bin() -> &'static str { + env!("CARGO_BIN_EXE_devme") +} + +fn fixture() -> TempDir { + let dir = tempfile::Builder::new() + .prefix("devme-down-nongit-") + .tempdir_in("/tmp") + .unwrap(); + std::fs::write( + dir.path().join("devme.toml"), + r#"schema_version = 1 + +[service.worker] +cmd = "touch started; sleep 30" +stop = "touch stopped" +"#, + ) + .unwrap(); + std::fs::create_dir(dir.path().join("runtime")).unwrap(); + dir +} + +fn run(dir: &TempDir, args: &[&str]) -> Output { + Command::new(bin()) + .args(args) + .current_dir(dir.path()) + .env("HOME", dir.path()) + .env("XDG_RUNTIME_DIR", dir.path().join("runtime")) + .output() + .unwrap() +} + +fn wait_for(path: &std::path::Path) { + let deadline = Instant::now() + Duration::from_secs(5); + while !path.exists() { + assert!( + Instant::now() < deadline, + "timed out waiting for {}", + path.display() + ); + std::thread::sleep(Duration::from_millis(20)); + } +} + +#[test] +fn down_all_stops_the_current_non_git_project() { + let dir = fixture(); + + let up = run(&dir, &["up", "-d"]); + assert!( + up.status.success(), + "stdout: {}\nstderr: {}", + String::from_utf8_lossy(&up.stdout), + String::from_utf8_lossy(&up.stderr), + ); + wait_for(&dir.path().join("started")); + + let down = run(&dir, &["down", "--all"]); + assert!( + down.status.success(), + "stdout: {}\nstderr: {}", + String::from_utf8_lossy(&down.stdout), + String::from_utf8_lossy(&down.stderr), + ); + wait_for(&dir.path().join("stopped")); +}