diff --git a/apps/bootstrap-installer/src-tauri/src/bootstrap.rs b/apps/bootstrap-installer/src-tauri/src/bootstrap.rs index 7d2c4bfaea..50dd3a6999 100644 --- a/apps/bootstrap-installer/src-tauri/src/bootstrap.rs +++ b/apps/bootstrap-installer/src-tauri/src/bootstrap.rs @@ -220,7 +220,9 @@ pub(crate) fn resolve_hermes_desktop_exe(install_root: &std::path::Path) -> Opti ("mac-arm64/Hermes.app/Contents/MacOS", "Hermes"), ] } else { - &[("linux-unpacked", "hermes")] + // electron-builder names the x64 dir `linux-unpacked` and every other + // arch `linux--unpacked` (#94703). + &[("linux-unpacked", "hermes"), ("linux-arm64-unpacked", "hermes")] }; for (subdir, exe) in candidates { let p = release_dir.join(subdir).join(exe); @@ -1136,6 +1138,21 @@ mod tests { let _ = std::fs::remove_dir_all(&root); } + // electron-builder writes ARM64 Linux builds to `linux-arm64-unpacked`; only + // x64 uses the bare `linux-unpacked` name (#94703). + #[cfg(target_os = "linux")] + #[test] + fn resolve_hermes_desktop_exe_finds_arm64_linux_build() { + let root = unique_tmp_dir("app-linux-arm64"); + let dir = root.join("apps/desktop/release/linux-arm64-unpacked"); + std::fs::create_dir_all(&dir).unwrap(); + let exe = dir.join("hermes"); + std::fs::write(&exe, b"stub").unwrap(); + + assert_eq!(resolve_hermes_desktop_exe(&root), Some(exe)); + let _ = std::fs::remove_dir_all(&root); + } + #[test] fn resolve_hermes_desktop_app_is_none_without_a_build() { let root = unique_tmp_dir("app-none"); diff --git a/apps/bootstrap-installer/src-tauri/src/update.rs b/apps/bootstrap-installer/src-tauri/src/update.rs index 0ddc81f3b1..b7cebf4f6e 100644 --- a/apps/bootstrap-installer/src-tauri/src/update.rs +++ b/apps/bootstrap-installer/src-tauri/src/update.rs @@ -706,7 +706,11 @@ fn desktop_app_payload_paths(install_root: &Path) -> Vec { release.join("mac-arm64").join("Hermes.app").join("Contents").join("Resources").join("app.asar"), ] } else { - vec![release.join("linux-unpacked").join("resources").join("app.asar")] + // x64 builds land in `linux-unpacked`, ARM64 in `linux-arm64-unpacked` (#94703). + vec![ + release.join("linux-unpacked").join("resources").join("app.asar"), + release.join("linux-arm64-unpacked").join("resources").join("app.asar"), + ] } } @@ -1270,6 +1274,18 @@ mod tests { ); } + #[cfg(target_os = "linux")] + #[test] + fn lock_probe_paths_cover_arm64_linux_build() { + let root = Path::new("/x/hermes-agent"); + let probes = install_lock_probe_paths(root); + + for dir in ["linux-unpacked", "linux-arm64-unpacked"] { + let asar = root.join("apps/desktop/release").join(dir).join("resources/app.asar"); + assert!(probes.contains(&asar), "{dir} payload must be probed (#94703)"); + } + } + #[test] fn locked_paths_ignores_missing_payloads() { let root = Path::new("/nonexistent/hermes-agent");