diff --git a/CHANGELOG.md b/CHANGELOG.md index 1809f506..4e9a69b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ Upon updating Pueue and restarting the daemon, the previous state will be wiped, - **Breaking**: Redesigned task editing process [#553](https://github.com/Nukesor/pueue/issues/553). Pueue now allows editing all properties a task in one editor session. There're two modes to do so: `toml` and `files`. - Revisited, fixed and cleaned up CLI help texts. +- Print most of Pueue's info/log messages to `stderr`. Only keep useful stuff like json and task log output on `stdout`. ### Add diff --git a/pueue/src/bin/pueued.rs b/pueue/src/bin/pueued.rs index 94926075..2f066cfa 100644 --- a/pueue/src/bin/pueued.rs +++ b/pueue/src/bin/pueued.rs @@ -18,7 +18,7 @@ async fn main() -> Result<()> { // subcommand to install the service #[cfg(target_os = "windows")] if opt.service.is_some() { - println!("daemonize flag cannot be used with service subcommand"); + eprintln!("daemonize flag cannot be used with service subcommand"); return Ok(()); } @@ -120,7 +120,7 @@ fn fork_daemon(opt: &CliArguments) -> Result<()> { let current_exe = if let Ok(path) = std::env::current_exe() { path.to_string_lossy().clone().to_string() } else { - println!("Couldn't detect path of current binary. Falling back to 'pueue' in $PATH"); + eprintln!("Couldn't detect path of current binary. Falling back to 'pueue' in $PATH"); "pueued".to_string() }; diff --git a/pueue/src/client/client.rs b/pueue/src/client/client.rs index bef14f04..763e6eb0 100644 --- a/pueue/src/client/client.rs +++ b/pueue/src/client/client.rs @@ -352,7 +352,7 @@ impl Client { .map(|t| format!("task{t}")) .collect::>() .join(", "); - println!("You are trying to {action}: {task_ids}",); + eprintln!("You are trying to {action}: {task_ids}",); let mut input = String::new(); @@ -364,7 +364,7 @@ impl Client { match input.chars().next().unwrap() { 'N' | 'n' => { - println!("Aborted!"); + eprintln!("Aborted!"); std::process::exit(1); } '\n' | 'Y' | 'y' => { diff --git a/pueue/src/client/commands/restart.rs b/pueue/src/client/commands/restart.rs index c9083fc1..749f9a31 100644 --- a/pueue/src/client/commands/restart.rs +++ b/pueue/src/client/commands/restart.rs @@ -165,7 +165,7 @@ pub async fn restart( println!("Restarted tasks: {:?}", filtered_tasks.matching_ids); } if !filtered_tasks.non_matching_ids.is_empty() { - println!( + eprintln!( "Couldn't restart tasks: {:?}", filtered_tasks.non_matching_ids ); diff --git a/pueue/src/client/commands/wait.rs b/pueue/src/client/commands/wait.rs index 5be63f57..7ca3c173 100644 --- a/pueue/src/client/commands/wait.rs +++ b/pueue/src/client/commands/wait.rs @@ -58,7 +58,7 @@ pub async fn wait( let tasks = get_tasks(&state, &selection); if tasks.is_empty() { - println!("No tasks found for selection {selection:?}"); + eprintln!("No tasks found for selection {selection:?}"); return Ok(()); } diff --git a/pueue/src/client/display/follow.rs b/pueue/src/client/display/follow.rs index c0be9988..72c7d561 100644 --- a/pueue/src/client/display/follow.rs +++ b/pueue/src/client/display/follow.rs @@ -29,7 +29,7 @@ pub async fn follow_local_task_logs( // Ensure that it exists and is started. loop { let Some(task) = get_task(stream, task_id).await? else { - println!("Pueue: The task to be followed doesn't exist."); + eprintln!("Pueue: The task to be followed doesn't exist."); std::process::exit(1); }; // Task started up, we can start to follow. @@ -42,7 +42,7 @@ pub async fn follow_local_task_logs( let mut handle = match get_log_file_handle(task_id, pueue_directory) { Ok(stdout) => stdout, Err(err) => { - println!("Failed to get log file handles: {err}"); + eprintln!("Failed to get log file handles: {err}"); return Ok(()); } }; @@ -58,7 +58,7 @@ pub async fn follow_local_task_logs( // The loop following this section will then only copy those last lines to stdout. if let Some(lines) = lines { if let Err(err) = seek_to_last_lines(&mut handle, lines) { - println!("Error seeking to last lines from log: {err}"); + eprintln!("Error seeking to last lines from log: {err}"); } } @@ -73,17 +73,17 @@ pub async fn follow_local_task_logs( loop { // Check whether the file still exists. Exit if it doesn't. if !path.exists() { - println!("Pueue: Log file has gone away. Has the task been removed?"); + eprintln!("Pueue: Log file has gone away. Has the task been removed?"); return Ok(()); } // Read the next chunk of text from the last position. if let Err(err) = io::copy(&mut handle, &mut stdout) { - println!("Pueue: Error while reading file: {err}"); + eprintln!("Pueue: Error while reading file: {err}"); return Ok(()); }; // Flush the stdout buffer to actually print the output. if let Err(err) = stdout.flush() { - println!("Pueue: Error while flushing stdout: {err}"); + eprintln!("Pueue: Error while flushing stdout: {err}"); return Ok(()); }; @@ -94,7 +94,7 @@ pub async fn follow_local_task_logs( // In case either is not, exit. if (last_check % task_check_interval) == 0 { let Some(task) = get_task(stream, task_id).await? else { - println!("Pueue: The followed task has been removed."); + eprintln!("Pueue: The followed task has been removed."); std::process::exit(1); }; // Task exited by itself. We can stop following. diff --git a/pueue/src/client/display/log/local.rs b/pueue/src/client/display/log/local.rs index 70cf0795..9946c1fb 100644 --- a/pueue/src/client/display/log/local.rs +++ b/pueue/src/client/display/log/local.rs @@ -19,7 +19,7 @@ pub fn print_local_log( let mut file = match get_log_file_handle(task_id, &settings.shared.pueue_directory()) { Ok(file) => file, Err(err) => { - println!("Failed to get log file handle: {err}"); + eprintln!("Failed to get log file handle: {err}"); return; } }; @@ -47,7 +47,7 @@ fn print_local_file(stdout: &mut Stdout, file: &mut File, lines: &Option, match seek_to_last_lines(file, *lines) { Ok(complete) => output_complete = complete, Err(err) => { - println!("Failed reading local log file: {err}"); + eprintln!("Failed reading local log file: {err}"); return; } } @@ -61,11 +61,11 @@ fn print_local_file(stdout: &mut Stdout, file: &mut File, lines: &Option, } // Print a newline between the task information and the first output. - println!("\n{header}{line_info}"); + eprintln!("\n{header}{line_info}"); // Print everything if let Err(err) = io::copy(file, stdout) { - println!("Failed reading local log file: {err}"); + eprintln!("Failed reading local log file: {err}"); }; } } diff --git a/pueue/src/client/display/log/mod.rs b/pueue/src/client/display/log/mod.rs index 9b8accb7..de424b66 100644 --- a/pueue/src/client/display/log/mod.rs +++ b/pueue/src/client/display/log/mod.rs @@ -74,15 +74,15 @@ pub fn print_logs( if task_logs.is_empty() { match selection { TaskSelection::TaskIds(_) => { - println!("There are no finished tasks for your specified ids"); + eprintln!("There are no finished tasks for your specified ids"); return; } TaskSelection::Group(group) => { - println!("There are no finished tasks for group '{group}'"); + eprintln!("There are no finished tasks for group '{group}'"); return; } TaskSelection::All => { - println!("There are no finished tasks"); + eprintln!("There are no finished tasks"); return; } } @@ -174,7 +174,7 @@ fn print_task_info(task: &Task, style: &OutputStyle) { if style.enabled { table.enforce_styling(); } - println!("{table}"); + eprintln!("{table}"); // All other information is aligned and styled by using a separate table. let mut table = Table::new(); @@ -218,5 +218,5 @@ fn print_task_info(task: &Task, style: &OutputStyle) { first_column.set_cell_alignment(CellAlignment::Right); first_column.set_padding((0, 0)); - println!("{table}"); + eprintln!("{table}"); } diff --git a/pueue/src/client/display/log/remote.rs b/pueue/src/client/display/log/remote.rs index 32aa7732..12bd7ade 100644 --- a/pueue/src/client/display/log/remote.rs +++ b/pueue/src/client/display/log/remote.rs @@ -26,7 +26,7 @@ pub fn print_remote_log(task_log: &TaskLogMessage, style: &OutputStyle, lines: O println!("\n{header}{line_info}"); if let Err(err) = decompress_and_print_remote_log(bytes) { - println!("Error while parsing stdout: {err}"); + eprintln!("Error while parsing stdout: {err}"); } } } diff --git a/pueue/src/client/display/mod.rs b/pueue/src/client/display/mod.rs index 29f1d17b..0c497af6 100644 --- a/pueue/src/client/display/mod.rs +++ b/pueue/src/client/display/mod.rs @@ -27,5 +27,5 @@ pub fn print_success(_style: &OutputStyle, message: &str) { /// Used to style any generic failure message from the daemon. pub fn print_error(style: &OutputStyle, message: &str) { let styled = style.style_text(message, Some(Color::Red), None); - println!("{styled}"); + eprintln!("{styled}"); } diff --git a/pueue/src/daemon/mod.rs b/pueue/src/daemon/mod.rs index c226f9ca..e75c210f 100644 --- a/pueue/src/daemon/mod.rs +++ b/pueue/src/daemon/mod.rs @@ -155,14 +155,14 @@ fn setup_signal_panic_handling(settings: &Settings, state: SharedState) -> Resul // Cleanup the pid file if let Err(error) = pid::cleanup_pid_file(&settings_clone.shared.pid_path()) { - println!("Failed to cleanup pid after panic."); - println!("{error}"); + eprintln!("Failed to cleanup pid after panic."); + eprintln!("{error}"); } // Remove the unix socket. if let Err(error) = socket_cleanup(&settings_clone.shared) { - println!("Failed to cleanup socket after panic."); - println!("{error}"); + eprintln!("Failed to cleanup socket after panic."); + eprintln!("{error}"); } std::process::exit(1); diff --git a/pueue/src/daemon/network/message_handler/log.rs b/pueue/src/daemon/network/message_handler/log.rs index 07e1f6a0..2bb68ae3 100644 --- a/pueue/src/daemon/network/message_handler/log.rs +++ b/pueue/src/daemon/network/message_handler/log.rs @@ -140,7 +140,7 @@ pub async fn follow_log( // The loop following this section will then only copy those last lines to stdout. if let Some(lines) = message.lines { if let Err(err) = seek_to_last_lines(&mut handle, lines) { - println!("Error seeking to last lines from log: {err}"); + eprintln!("Error seeking to last lines from log: {err}"); } } diff --git a/pueue/src/daemon/service.rs b/pueue/src/daemon/service.rs index 0ae38d04..eac8873f 100644 --- a/pueue/src/daemon/service.rs +++ b/pueue/src/daemon/service.rs @@ -191,8 +191,8 @@ pub fn start_service() -> Result<()> { service.start::(&[])?; println!("Successfully started service"); } - ServiceState::StartPending => println!("Service is already starting"), - ServiceState::Running => println!("Service is already running"), + ServiceState::StartPending => eprintln!("Service is already starting"), + ServiceState::Running => eprintln!("Service is already running"), _ => (), } @@ -211,8 +211,8 @@ pub fn stop_service() -> Result<()> { let service = service_manager.open_service(SERVICE_NAME, service_access)?; match service.query_status()?.current_state { - ServiceState::Stopped => println!("Service is already stopped"), - ServiceState::StartPending => println!("Service cannot stop because it is starting (please wait until it fully started to stop it)"), + ServiceState::Stopped => eprintln!("Service is already stopped"), + ServiceState::StartPending => eprintln!("Service cannot stop because it is starting (please wait until it fully started to stop it)"), ServiceState::Running => { service.stop()?; println!("Successfully stopped service"); diff --git a/pueue/src/daemon/task_handler.rs b/pueue/src/daemon/task_handler.rs index 939186f6..c80e3037 100644 --- a/pueue/src/daemon/task_handler.rs +++ b/pueue/src/daemon/task_handler.rs @@ -80,14 +80,14 @@ fn handle_shutdown(settings: &Settings, state: &mut LockedState) { // Remove the unix socket. if let Err(error) = socket_cleanup(&settings.shared) { - println!("Failed to cleanup socket during shutdown."); - println!("{error}"); + eprintln!("Failed to cleanup socket during shutdown."); + eprintln!("{error}"); } // Cleanup the pid file if let Err(error) = cleanup_pid_file(&settings.shared.pid_path()) { - println!("Failed to cleanup pid during shutdown."); - println!("{error}"); + eprintln!("Failed to cleanup pid during shutdown."); + eprintln!("{error}"); } // Actually exit the program the way we're supposed to. diff --git a/pueue/tests/client/helper/compare_output.rs b/pueue/tests/client/helper/compare_output.rs index 8a092560..440e6f1b 100644 --- a/pueue/tests/client/helper/compare_output.rs +++ b/pueue/tests/client/helper/compare_output.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::process::Output; use anyhow::{bail, Context, Result}; use chrono::Local; @@ -84,16 +85,19 @@ pub async fn get_task_context(settings: &Settings) -> Result, + output: Output, context: HashMap, ) -> Result<()> { + let mut combined_output = output.stderr.clone(); + combined_output.append(&mut output.stdout.clone()); + let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests") .join("client") .join("_templates") .join(name); - let actual = String::from_utf8(stdout).context("Got invalid utf8 as stdout!")?; + let actual = String::from_utf8(combined_output).context("Got invalid utf8 as output!")?; let Ok(mut expected) = read_to_string(&path) else { println!("Actual output:\n{actual}"); @@ -119,7 +123,7 @@ pub fn assert_template_matches( } /// Convenience wrapper to compare process stdout with snapshots. -pub fn assert_snapshot_matches_stdout(name: &str, stdout: Vec) -> Result<()> { +pub fn assert_snapshot_matches_output(name: &str, stdout: Vec) -> Result<()> { let actual = String::from_utf8(stdout).context("Got invalid utf8 as stdout!")?; assert_snapshot_matches(name, actual) } diff --git a/pueue/tests/client/integration/follow.rs b/pueue/tests/client/integration/follow.rs index 96481c5e..d9feacc2 100644 --- a/pueue/tests/client/integration/follow.rs +++ b/pueue/tests/client/integration/follow.rs @@ -35,7 +35,7 @@ async fn default(#[case] read_local_logs: bool) -> Result<()> { // This will result in the client receiving the streamed output until the task finished. let output = run_client_command(shared, &["follow"])?; - assert_snapshot_matches_stdout("follow__default", output.stdout)?; + assert_snapshot_matches_output("follow__default", output.stdout)?; Ok(()) } @@ -58,7 +58,7 @@ async fn last_lines(#[case] read_local_logs: bool) -> Result<()> { // Follow the task, but only print the last 4 lines of the output. let output = run_client_command(shared, &["follow", "--lines=4"])?; - assert_snapshot_matches_stdout("follow__last_lines", output.stdout)?; + assert_snapshot_matches_output("follow__last_lines", output.stdout)?; Ok(()) } @@ -79,7 +79,7 @@ async fn wait_for_task(#[case] read_local_logs: bool) -> Result<()> { // Wait for the task to start and follow until it finisheds. let output = run_client_command(shared, &["follow", "0"])?; - assert_snapshot_matches_stdout("follow__default", output.stdout)?; + assert_snapshot_matches_output("follow__default", output.stdout)?; Ok(()) } @@ -98,7 +98,7 @@ async fn fail_on_non_existing(#[case] read_local_logs: bool) -> Result<()> { // The client should exit with exit code `1`. let output = run_client_command(shared, &["follow", "0"])?; assert!(!output.status.success(), "follow got an unexpected exit 0"); - assert_snapshot_matches_stdout("follow__fail_on_non_existing", output.stdout)?; + assert_snapshot_matches_output("follow__fail_on_non_existing", output.stderr)?; Ok(()) } diff --git a/pueue/tests/client/integration/group.rs b/pueue/tests/client/integration/group.rs index 55342536..ccedb151 100644 --- a/pueue/tests/client/integration/group.rs +++ b/pueue/tests/client/integration/group.rs @@ -19,7 +19,7 @@ async fn default() -> Result<()> { // Get the group status output let output = run_client_command(shared, &["group"])?; - assert_snapshot_matches_stdout("group__default", output.stdout)?; + assert_snapshot_matches_output("group__default", output.stdout)?; Ok(()) } @@ -48,7 +48,7 @@ async fn colored() -> Result<()> { // Get the group status output let output = run_client_command(shared, &["--color", "always", "group"])?; - assert_snapshot_matches_stdout("group__colored", output.stdout)?; + assert_snapshot_matches_output("group__colored", output.stdout)?; Ok(()) } diff --git a/pueue/tests/client/integration/log.rs b/pueue/tests/client/integration/log.rs index e86d2e0d..6338a88c 100644 --- a/pueue/tests/client/integration/log.rs +++ b/pueue/tests/client/integration/log.rs @@ -34,7 +34,7 @@ async fn read(#[case] read_local_logs: bool) -> Result<()> { let output = run_client_command(shared, &["log"])?; let context = get_task_context(&daemon.settings).await?; - assert_template_matches("log__default", output.stdout, context)?; + assert_template_matches("log__default", output, context)?; Ok(()) } @@ -65,7 +65,7 @@ async fn read_truncated(#[case] read_local_logs: bool) -> Result<()> { let output = run_client_command(shared, &["log", "--lines=5"])?; let context = get_task_context(&daemon.settings).await?; - assert_template_matches("log__last_lines", output.stdout, context)?; + assert_template_matches("log__last_lines", output, context)?; Ok(()) } @@ -83,7 +83,7 @@ async fn task_with_label() -> Result<()> { let output = run_client_command(shared, &["log"])?; let context = get_task_context(&daemon.settings).await?; - assert_template_matches("log__with_label", output.stdout, context)?; + assert_template_matches("log__with_label", output, context)?; Ok(()) } @@ -101,7 +101,7 @@ async fn colored() -> Result<()> { let output = run_client_command(shared, &["--color", "always", "log"])?; let context = get_task_context(&daemon.settings).await?; - assert_template_matches("log__colored", output.stdout, context)?; + assert_template_matches("log__colored", output, context)?; Ok(()) } diff --git a/pueue/tests/client/integration/status.rs b/pueue/tests/client/integration/status.rs index ca28dcaf..7ab9bc40 100644 --- a/pueue/tests/client/integration/status.rs +++ b/pueue/tests/client/integration/status.rs @@ -29,7 +29,7 @@ async fn full() -> Result<()> { let output = run_status_without_path(shared, &[]).await?; let context = get_task_context(&daemon.settings).await?; - assert_template_matches("status__full", output.stdout, context)?; + assert_template_matches("status__full", output, context)?; Ok(()) } @@ -47,7 +47,7 @@ async fn full() -> Result<()> { // let output = run_status_without_path(shared, &["--color", "always"]).await?; // // let context = get_task_context(&daemon.settings).await?; -// assert_stdout_matches("status__colored", output.stdout, context)?; +// assert_stdout_matches("status__colored", output, context)?; // // Ok(()) //} @@ -73,7 +73,7 @@ async fn single_group() -> Result<()> { // The output should only show the first task let context = get_task_context(&daemon.settings).await?; - assert_template_matches("status__single_group", output.stdout, context)?; + assert_template_matches("status__single_group", output, context)?; Ok(()) } @@ -100,7 +100,7 @@ async fn multiple_groups() -> Result<()> { // The output should show multiple groups let context = get_task_context(&daemon.settings).await?; - assert_template_matches("status__multiple_groups", output.stdout, context)?; + assert_template_matches("status__multiple_groups", output, context)?; Ok(()) } diff --git a/pueue/tests/client/integration/wait.rs b/pueue/tests/client/integration/wait.rs index 20419f44..6f50d590 100644 --- a/pueue/tests/client/integration/wait.rs +++ b/pueue/tests/client/integration/wait.rs @@ -58,7 +58,7 @@ async fn multiple_tasks() -> Result<()> { let output = wait_handle.join().unwrap()?; let stdout = clean_wait_output(output.stdout); - assert_snapshot_matches_stdout("wait__multiple_tasks", stdout)?; + assert_snapshot_matches_output("wait__multiple_tasks", stdout)?; Ok(()) } @@ -86,7 +86,7 @@ async fn target_status() -> Result<()> { let output = wait_handle.join().unwrap()?; let stdout = clean_wait_output(output.stdout); - assert_snapshot_matches_stdout("wait__target_status", stdout)?; + assert_snapshot_matches_output("wait__target_status", stdout)?; Ok(()) } @@ -110,7 +110,7 @@ async fn single_task_target_status() -> Result<()> { let output = wait_handle.join().unwrap()?; let stdout = clean_wait_output(output.stdout); - assert_snapshot_matches_stdout("wait__single_task_target_status", stdout)?; + assert_snapshot_matches_output("wait__single_task_target_status", stdout)?; Ok(()) } @@ -130,7 +130,7 @@ async fn success_success() -> Result<()> { assert!(output.status.success(), "Got non-zero exit code on wait."); let stdout = clean_wait_output(output.stdout); - assert_snapshot_matches_stdout("wait__success_success", stdout)?; + assert_snapshot_matches_output("wait__success_success", stdout)?; Ok(()) } @@ -157,7 +157,7 @@ async fn success_failure() -> Result<()> { ); let stdout = clean_wait_output(output.stdout); - assert_snapshot_matches_stdout("wait__success_failure", stdout)?; + assert_snapshot_matches_output("wait__success_failure", stdout)?; Ok(()) }