Merge pull request #64576 from joelbrilliant/fix/desktop-update-stream-output

fix(update): stream update child output to the live log (PYTHONUNBUFFERED)
This commit is contained in:
nousbot-eng 2026-07-17 16:20:39 -04:00 committed by GitHub
commit 7f76fc040a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 28 additions and 1 deletions

View file

@ -733,6 +733,13 @@ fn update_child_env(install_root: &Path) -> Vec<(String, OsString)> {
"HERMES_HOME".to_string(),
hermes_home.as_os_str().to_os_string(),
)];
// `hermes update` is a Python CLI writing to a pipe here, so CPython
// block-buffers its stdout: nothing reaches run_streamed (and the live
// log UI) until 8 KB accumulate or the process exits. Long quiet steps —
// the pre-update backup can zip multi-GB archives for minutes — render as
// a frozen stage, and users cancel a healthy update. Force line-by-line
// output instead.
envs.push(("PYTHONUNBUFFERED".to_string(), OsString::from("1")));
if let Some(path) = path_with_prepended_entries(&[
hermes_home.join("node").join("bin"),
venv_bin_dir(install_root),
@ -1046,6 +1053,16 @@ mod tests {
assert!(!is_locked(Path::new("/nonexistent/does/not/exist/xyz")));
}
#[test]
fn update_child_env_forces_unbuffered_python() {
let envs = update_child_env(Path::new("/x/hermes-agent"));
assert!(
envs.iter()
.any(|(k, v)| k == "PYTHONUNBUFFERED" && v.to_str() == Some("1")),
"update children must run unbuffered so long steps stream to the live log"
);
}
#[test]
fn lock_probe_paths_include_desktop_app_payload() {
let root = Path::new("/x/hermes-agent");
@ -1056,7 +1073,12 @@ mod tests {
"venv shim remains part of the update lock probe"
);
assert!(
probes.iter().any(|p| p.ends_with(Path::new("resources/app.asar"))),
// Windows/Linux payloads live under `resources/`, the macOS bundle
// under `Contents/Resources/` — Path::ends_with is case-sensitive.
probes.iter().any(|p| {
p.ends_with(Path::new("resources/app.asar"))
|| p.ends_with(Path::new("Resources/app.asar"))
}),
"packaged app.asar must be probed so repair/re-clone waits for the old desktop to exit"
);
}

View file

@ -2715,8 +2715,13 @@ async function applyUpdatesPosixInApp(opts: any) {
// Put the Hermes-managed Node and the venv on PATH so `hermes desktop`'s
// npm build can find them on a machine with no system Node. Windows portable
// Node lives directly under %LOCALAPPDATA%\hermes\node, not node\bin.
// PYTHONUNBUFFERED: `hermes update` writes to a pipe here, so CPython
// block-buffers stdout and long quiet steps (the pre-update backup can zip
// multi-GB archives for minutes) stream nothing to the progress UI — users
// read the silence as a hang and cancel a healthy update.
const env: Record<string, string> = {
HERMES_HOME,
PYTHONUNBUFFERED: '1',
PATH: pathWithHermesManagedNode(path.join(updateRoot, 'venv', 'bin'))
}