Skip to content

Commit 355dac4

Browse files
authored
Keep shdeps sudo prompts visible (#34)
Summary Keep `shdeps update` sudo prompts visible while progress is active. Terminal progress now moves to a fresh line instead of clearing live rows before sudo, and JSONL progress writes the pre-prompt newline directly to `/dev/tty` so FIFO consumers like `dot update` cannot race and erase or hide the sudo prompt. Testing - `cargo fmt --all -- --check` - `cargo test --locked jsonl_progress_emits_prompt_pause_event` - `cargo test --locked update_pauses_progress_before_sudo_package_commands` - `cargo test --locked` - `checkrun format --path src/cli.rs --path src/update.rs --path src/update_pkg.rs` - `checkrun lint --path src/cli.rs --path src/update.rs --path src/update_pkg.rs` - E2E in tmux with patched shdeps via `SHDEPS_RUST_CLI=/home/chris/git/shdeps/target/debug/shdeps dot update`: cold sudo prompt stayed visible on its own line and completed with `DOT_COLD_TTY_RC=0`; warm run completed with `DOT_WARM_TTY_RC=0` and no prompt display issue.
1 parent 3d525bd commit 355dac4

1 file changed

Lines changed: 64 additions & 10 deletions

File tree

src/cli.rs

Lines changed: 64 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
77
use std::collections::BTreeMap;
88
use std::env;
9+
use std::fs::OpenOptions;
910
use std::io::IsTerminal;
1011
use std::io::Write;
1112
use std::path::{Path, PathBuf};
@@ -614,7 +615,7 @@ where
614615
}
615616

616617
let summary = if progress_jsonl {
617-
let mut progress = JsonlProgress { out: stdout };
618+
let mut progress = JsonlProgress::with_prompt_tty(stdout);
618619
update::run_with_progress(&entries, &manifest, &context, update_options, &mut progress)?
619620
} else {
620621
let summary = update::run(&entries, &manifest, &context, update_options)?;
@@ -636,14 +637,14 @@ where
636637
let orphans = manifest.orphans(&entries);
637638
if !orphans.is_empty() && !options.quiet {
638639
if progress_jsonl {
639-
let mut progress = JsonlProgress { out: stdout };
640+
let mut progress = JsonlProgress::new(stdout);
640641
write_prune_orphans_jsonl(&orphans, &mut progress)?;
641642
} else {
642643
write_prune_orphans(&orphans, true, stderr)?;
643644
}
644645
}
645646
if progress_jsonl {
646-
let mut progress = JsonlProgress { out: stdout };
647+
let mut progress = JsonlProgress::new(stdout);
647648
write_update_summary_jsonl(&summary, &entries, active_count, &mut progress)?;
648649
}
649650

@@ -839,18 +840,58 @@ where
839840
W: Write,
840841
{
841842
out: &'a mut W,
843+
prompt_out: Option<&'a mut dyn Write>,
844+
prompt_tty: bool,
842845
}
843846

844847
impl<W> JsonlProgress<'_, W>
845848
where
846849
W: Write,
847850
{
851+
fn new(out: &mut W) -> JsonlProgress<'_, W> {
852+
JsonlProgress {
853+
out,
854+
prompt_out: None,
855+
prompt_tty: false,
856+
}
857+
}
858+
859+
fn with_prompt_tty(out: &mut W) -> JsonlProgress<'_, W> {
860+
JsonlProgress {
861+
out,
862+
prompt_out: None,
863+
prompt_tty: true,
864+
}
865+
}
866+
867+
#[cfg(test)]
868+
fn with_prompt_out<'a>(out: &'a mut W, prompt_out: &'a mut dyn Write) -> JsonlProgress<'a, W> {
869+
JsonlProgress {
870+
out,
871+
prompt_out: Some(prompt_out),
872+
prompt_tty: false,
873+
}
874+
}
875+
848876
fn event(&mut self, value: serde_json::Value) -> Result<()> {
849877
serde_json::to_writer(&mut self.out, &value)?;
850878
writeln!(self.out)?;
851879
self.out.flush()?;
852880
Ok(())
853881
}
882+
883+
fn fresh_prompt_line(&mut self) -> Result<()> {
884+
if let Some(out) = self.prompt_out.as_deref_mut() {
885+
writeln!(out)?;
886+
out.flush()?;
887+
} else if self.prompt_tty {
888+
if let Ok(mut tty) = OpenOptions::new().write(true).open("/dev/tty") {
889+
writeln!(tty)?;
890+
tty.flush()?;
891+
}
892+
}
893+
Ok(())
894+
}
854895
}
855896

856897
impl<W> update::Progress for JsonlProgress<'_, W>
@@ -885,7 +926,8 @@ where
885926
"event": "prompt",
886927
"status": "running",
887928
"detail": detail,
888-
}))
929+
}))?;
930+
self.fresh_prompt_line()
889931
}
890932
}
891933

@@ -1209,7 +1251,13 @@ where
12091251
}
12101252

12111253
fn pause_for_prompt(&mut self, _detail: &str) -> Result<()> {
1212-
self.clear_unfinished()
1254+
if self.active && !self.finished {
1255+
writeln!(self.out)?;
1256+
self.out.flush()?;
1257+
self.active = false;
1258+
self.rendered_rows = 0;
1259+
}
1260+
Ok(())
12131261
}
12141262
}
12151263

@@ -1521,7 +1569,7 @@ where
15211569
runner: &Process,
15221570
client: &Curl,
15231571
};
1524-
let mut progress = JsonlProgress { out: stdout };
1572+
let mut progress = JsonlProgress::new(stdout);
15251573
github_method::resolve_entries_with_progress(
15261574
entries,
15271575
&context,
@@ -2915,9 +2963,10 @@ mod tests {
29152963
#[test]
29162964
fn jsonl_progress_emits_prompt_pause_event() {
29172965
let mut stdout = Vec::new();
2966+
let mut prompt_out = Vec::new();
29182967

29192968
{
2920-
let mut progress = super::JsonlProgress { out: &mut stdout };
2969+
let mut progress = super::JsonlProgress::with_prompt_out(&mut stdout, &mut prompt_out);
29212970
crate::update::Progress::pause_for_prompt(
29222971
&mut progress,
29232972
"waiting for sudo authentication",
@@ -2931,10 +2980,11 @@ mod tests {
29312980
assert_eq!(event["event"], "prompt");
29322981
assert_eq!(event["status"], "running");
29332982
assert_eq!(event["detail"], "waiting for sudo authentication");
2983+
assert_eq!(String::from_utf8(prompt_out).unwrap(), "\n");
29342984
}
29352985

29362986
#[test]
2937-
fn terminal_progress_clears_live_rows_before_prompt() {
2987+
fn terminal_progress_moves_to_fresh_line_before_prompt() {
29382988
let mut stdout = Vec::new();
29392989

29402990
{
@@ -2963,8 +3013,12 @@ mod tests {
29633013

29643014
let stdout = String::from_utf8(stdout).unwrap();
29653015
assert!(
2966-
stdout.ends_with("\r\x1b[K"),
2967-
"progress output should leave a clean current line for sudo: {stdout:?}"
3016+
stdout.ends_with('\n'),
3017+
"progress output should put sudo on a fresh line without clearing rows: {stdout:?}"
3018+
);
3019+
assert!(
3020+
!stdout.ends_with("\r\x1b[K"),
3021+
"prompt pause should not erase live progress rows: {stdout:?}"
29683022
);
29693023
}
29703024

0 commit comments

Comments
 (0)