diff --git a/Cargo.lock b/Cargo.lock index 62a747bd3..818eed386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4688,6 +4688,7 @@ dependencies = [ "uffs-broker-protocol", "uffs-winsvc", "windows 0.62.2", + "winresource", ] [[package]] diff --git a/crates/uffs-update/Cargo.toml b/crates/uffs-update/Cargo.toml index 77a0d47d8..986d396ea 100644 --- a/crates/uffs-update/Cargo.toml +++ b/crates/uffs-update/Cargo.toml @@ -59,5 +59,10 @@ windows.workspace = true [target.'cfg(unix)'.dependencies] libc = "0.2" +# Embed `app.manifest` (asInvoker) into uffs-update.exe on Windows MSVC so the +# "update" name doesn't trip Installer Detection → forced UAC (os error 740). +[build-dependencies] +winresource.workspace = true + [lints] workspace = true diff --git a/crates/uffs-update/app.manifest b/crates/uffs-update/app.manifest new file mode 100644 index 000000000..af7b450f1 --- /dev/null +++ b/crates/uffs-update/app.manifest @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + true + + + + + + + + + + diff --git a/crates/uffs-update/build.rs b/crates/uffs-update/build.rs new file mode 100644 index 000000000..50a72b229 --- /dev/null +++ b/crates/uffs-update/build.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) 2025-2026 SKY, LLC. + +// Build scripts run on the build host, not the shipping binary; the workspace +// deny-expect lint exists for runtime code. `expect` with a readable message is +// the idiomatic shape for build-script error reporting. +#![allow( + clippy::expect_used, + reason = "build scripts may panic on build-host failure; workspace deny-expect exists for runtime code" +)] + +//! Build script: embed `app.manifest` into `uffs-update.exe` on Windows MSVC. +//! +//! The manifest declares `asInvoker`, which is **required** to stop Windows' +//! Installer Detection heuristic from force-elevating this binary purely +//! because its name contains "update". Without it, `uffs.exe` (non-elevated) +//! cannot spawn `uffs-update.exe` — it fails with `ERROR_ELEVATION_REQUIRED` +//! (os error 740) — breaking every `uffs --update` operation on Windows. The +//! helper only rewrites files in the user's install dir; it never needs admin. +//! +//! Inert on non-Windows / non-MSVC targets (the helper ships windows-msvc). + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=app.manifest"); + println!("cargo:rerun-if-changed=../../assets/brand/icons/uffs.ico"); + + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + + if target_os == "windows" && target_env == "msvc" { + let mut res = winresource::WindowsResource::new(); + res.set_icon("../../assets/brand/icons/uffs.ico") + .set("ProductName", "UltraFastFileSearch") + .set("FileDescription", "UFFS self-update helper") + .set("CompanyName", "SKY, LLC.") + .set("LegalCopyright", "(c) 2025-2026 SKY, LLC. MPL-2.0.") + .set("OriginalFilename", "uffs-update.exe") + .set_manifest_file("app.manifest"); + + // Panic on failure is fine in a build script (host-only; the + // workspace deny-unwrap lint targets runtime code). + res.compile() + .expect("winresource: failed to embed uffs-update manifest"); + } +}