flowey: don't leave the vhd download prompt open forever, timeout after 30s#3134
flowey: don't leave the vhd download prompt open forever, timeout after 30s#3134chris-oo wants to merge 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Flowey node responsible for downloading OpenVMM VMM test artifacts to avoid blocking indefinitely on the local “press enter to confirm download” prompt by introducing a 30s timeout.
Changes:
- Add a 30-second timeout to the local interactive download confirmation prompt.
- Return an error if the prompt times out (instead of waiting forever).
| if !skip_prompt { | ||
| let _ = std::io::stdin().read_line(&mut String::new()); | ||
| // Only display the prompt for 30s before timing | ||
| // out. | ||
| let (tx, rx) = std::sync::mpsc::channel(); | ||
|
|
There was a problem hiding this comment.
In non-interactive environments (stdin not a TTY), the prompt logic can still proceed immediately (read_line returns EOF) and start downloading without explicit user acceptance. Consider checking stdin().is_terminal() (or equivalent) here and failing with a message that the user must pass LocalOnlySkipDownloadPrompt/run in an interactive terminal.
| let _ = rx | ||
| .recv_timeout(std::time::Duration::from_secs(30)) | ||
| .map_err(|_| anyhow!("timed out waiting for input after 30s"))?; |
There was a problem hiding this comment.
The value received from recv_timeout is an io::Result<usize> from read_line, but it’s currently ignored. If read_line returns Ok(0) (EOF) or Err(_), this should be treated as “no acceptance” and fail the step (rather than implicitly accepting and continuing).
| let _ = rx | |
| .recv_timeout(std::time::Duration::from_secs(30)) | |
| .map_err(|_| anyhow!("timed out waiting for input after 30s"))?; | |
| let read_result = rx | |
| .recv_timeout(std::time::Duration::from_secs(30)) | |
| .map_err(|_| anyhow!("timed out waiting for input after 30s"))?; | |
| match read_result { | |
| Ok(n) if n > 0 => { | |
| // User pressed enter; proceed. | |
| } | |
| Ok(0) => { | |
| // EOF before confirmation: treat as no acceptance. | |
| return Err(anyhow!( | |
| "stdin closed (EOF) before confirmation; aborting download" | |
| )); | |
| } | |
| Err(e) => { | |
| // Read error: treat as no acceptance. | |
| return Err(anyhow!( | |
| "failed to read confirmation from stdin: {e}" | |
| )); | |
| } | |
| } |
a78eaf3 to
d0f6545
Compare
d0f6545 to
7316980
Compare
flowey/flowey_lib_hvlite/src/download_openvmm_vmm_tests_artifacts.rs
Outdated
Show resolved
Hide resolved
7316980 to
f70363d
Compare
…er 30s feedback: use crossterm to read terminal input and poll just download if not interactive
f70363d to
9314ed3
Compare
| if let crossterm::event::Event::Key(key_event) = | ||
| crossterm::event::read().unwrap() | ||
| { | ||
| if key_event.code == crossterm::event::KeyCode::Enter { | ||
| // proceed with download | ||
| } else { | ||
| anyhow::bail!("user cancelled the run"); | ||
| } |
There was a problem hiding this comment.
crossterm::event::read().unwrap() can panic if the terminal backend errors (e.g., stdin closed, platform-specific event read failure). Please handle the error and convert it into an anyhow error (with context) instead of unwrapping so this step fails cleanly.
| if let crossterm::event::Event::Key(key_event) = | |
| crossterm::event::read().unwrap() | |
| { | |
| if key_event.code == crossterm::event::KeyCode::Enter { | |
| // proceed with download | |
| } else { | |
| anyhow::bail!("user cancelled the run"); | |
| } | |
| match crossterm::event::read() { | |
| Ok(crossterm::event::Event::Key(key_event)) => { | |
| if key_event.code == crossterm::event::KeyCode::Enter { | |
| // proceed with download | |
| } else { | |
| anyhow::bail!("user cancelled the run"); | |
| } | |
| } | |
| Ok(_) => { | |
| // Ignore non-key events and proceed with download | |
| } | |
| Err(e) => { | |
| anyhow::bail!("error while reading user input: {e}"); | |
| } |
| Ok(true) => { | ||
| if let crossterm::event::Event::Key(key_event) = | ||
| crossterm::event::read().unwrap() | ||
| { | ||
| if key_event.code == crossterm::event::KeyCode::Enter { | ||
| // proceed with download | ||
| } else { | ||
| anyhow::bail!("user cancelled the run"); | ||
| } | ||
| } |
There was a problem hiding this comment.
If poll() returns true but the next event is not a Key event (e.g., Resize, Mouse, Paste), the current code falls through and proceeds with the download without explicit user confirmation. Consider looping until you receive KeyCode::Enter (accept) or a cancel signal, while still enforcing the 30s timeout.
This behavior is really annoying when you start a terminal, or run it in an agent. Fail the download step after 30s if the user didn't accept, or if the user didn't ask to skip the prompt in the first place.