Skip to content

Commit 4375cb8

Browse files
spacetime dev - Print feedback when client process exits (#4469)
The main event loop in `spacetime dev` blocked on file change events with no periodic check on the client process. If the client exited (e.g., user pressed Enter in the basic-rs template), `spacetime dev` gave no feedback at all. **Fix**: Switch from blocking `recv()` to `recv_timeout(1s)` and check client process status every second. On exit, prints: - `Client process exited. File watcher is still active.` (exit code 0) - `Warning: Client process exited with code N. File watcher is still active.` (non-zero) The file watcher continues running so module changes are still detected and published. --------- Co-authored-by: clockwork-labs-bot <clockwork-labs-bot@users.noreply.github.com> Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com> Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com>
1 parent bc4fcec commit 4375cb8

1 file changed

Lines changed: 65 additions & 31 deletions

File tree

  • crates/cli/src/subcommands

crates/cli/src/subcommands/dev.rs

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
766766
.and_then(|c| c.get_one::<String>("server").ok().flatten());
767767
let server_for_client = server_opt_client.as_deref().unwrap_or(resolved_server);
768768
let server_host_url = config.get_host_url(Some(server_for_client))?;
769-
let _client_handle = if let Some(ref cmd) = client_command {
769+
let mut client_handle = if let Some(ref cmd) = client_command {
770770
let mut child = start_client_process(cmd, &project_dir, db_name_for_client, &server_host_url)?;
771771

772772
// Give the process a moment to fail fast (e.g., command not found, missing deps)
@@ -817,40 +817,74 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
817817

818818
let mut debounce_timer;
819819
loop {
820-
if rx.recv().is_ok() {
821-
debounce_timer = std::time::Instant::now();
822-
while debounce_timer.elapsed() < Duration::from_millis(300) {
823-
if rx.recv_timeout(Duration::from_millis(100)).is_ok() {
824-
debounce_timer = std::time::Instant::now();
820+
// Use recv_timeout so we can periodically check if the client process exited
821+
match rx.recv_timeout(Duration::from_secs(1)) {
822+
Err(std::sync::mpsc::RecvTimeoutError::Disconnected) => break Ok(()),
823+
Ok(()) => {
824+
debounce_timer = std::time::Instant::now();
825+
while debounce_timer.elapsed() < Duration::from_millis(300) {
826+
if rx.recv_timeout(Duration::from_millis(100)).is_ok() {
827+
debounce_timer = std::time::Instant::now();
828+
}
825829
}
826-
}
827830

828-
println!("\n{}", "File change detected, rebuilding...".yellow());
829-
match generate_build_and_publish(
830-
&config,
831-
&project_dir,
832-
loaded_config_dir.as_deref(),
833-
&spacetimedb_dir,
834-
&module_bindings_dir,
835-
client_language,
836-
clear_database,
837-
&publish_configs,
838-
&generate_configs_from_file,
839-
using_spacetime_config,
840-
server_from_cli,
841-
force,
842-
skip_publish,
843-
skip_generate,
844-
)
845-
.await
846-
{
847-
Ok(_) => {}
848-
Err(e) => {
849-
eprintln!("{} {}", "Error:".red().bold(), e);
850-
println!("{}", "Waiting for next change...".dimmed());
831+
println!("\n{}", "File change detected, rebuilding...".yellow());
832+
match generate_build_and_publish(
833+
&config,
834+
&project_dir,
835+
loaded_config_dir.as_deref(),
836+
&spacetimedb_dir,
837+
&module_bindings_dir,
838+
client_language,
839+
clear_database,
840+
&publish_configs,
841+
&generate_configs_from_file,
842+
using_spacetime_config,
843+
server_from_cli,
844+
force,
845+
skip_publish,
846+
skip_generate,
847+
)
848+
.await
849+
{
850+
Ok(_) => {}
851+
Err(e) => {
852+
eprintln!("{} {}", "Error:".red().bold(), e);
853+
println!("{}", "Waiting for next change...".dimmed());
854+
}
851855
}
852856
}
853-
}
857+
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
858+
// No rebuild yet. Check if the client process has exited.
859+
let Some(ref mut child) = client_handle else {
860+
continue;
861+
};
862+
match child.try_wait() {
863+
Ok(None) => {}
864+
Ok(Some(status)) => {
865+
client_handle = None;
866+
let code = status
867+
.code()
868+
.map(|c| c.to_string())
869+
.unwrap_or_else(|| "unknown".to_string());
870+
println!(
871+
"\n{} {}. {}",
872+
"Client process exited with code".yellow(),
873+
code,
874+
"File watcher is still active.".dimmed()
875+
);
876+
}
877+
Err(e) => {
878+
client_handle = None;
879+
eprintln!(
880+
"\n{} Failed to check client process status: {}",
881+
"Warning:".yellow().bold(),
882+
e
883+
);
884+
}
885+
}
886+
}
887+
};
854888
}
855889
}
856890

0 commit comments

Comments
 (0)