From 4d6ea6afcde945484be07e0db033da1add949597 Mon Sep 17 00:00:00 2001 From: jpicklyk <5747775+jpicklyk@users.noreply.github.com> Date: Tue, 22 Sep 2026 09:57:31 -0400 Subject: [PATCH 1/2] =?UTF-8?q?chore(release):=20v0.13.3=20=E2=80=94=20upd?= =?UTF-8?q?ater=20elevation=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Carries the fix for in-app updates of an all-users install run by a standard user (#40), which through 0.13.2 silently created a second per-user install instead of updating the one in Program Files. This is also the build that can finally exercise the elevated path: install it per-machine, then take the next update. Co-Authored-By: Claude Opus 5 --- package-lock.json | 4 ++-- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5bcdac..eeb3ad4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "log-tapper", - "version": "0.13.2", + "version": "0.13.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "log-tapper", - "version": "0.13.2", + "version": "0.13.3", "dependencies": { "@codemirror/commands": "^6.10.2", "@codemirror/lang-markdown": "6.5.2", diff --git a/package.json b/package.json index 525381b..3477517 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "log-tapper", - "version": "0.13.2", + "version": "0.13.3", "private": true, "type": "module", "scripts": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 450649e..d7415a5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2387,7 +2387,7 @@ dependencies = [ [[package]] name = "log-tapper" -version = "0.13.2" +version = "0.13.3" dependencies = [ "aho-corasick", "axum", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index a1376cf..100c8b7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "log-tapper" -version = "0.13.2" +version = "0.13.3" description = "Android log file analyzer with AI-assisted analysis" edition = "2021" # Bumped from 1.77.2 for ts-rs 11.1.0 (its own rust-version is 1.78.0). diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1abb71c..9f4c4c7 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "LogTapper", - "version": "0.13.2", + "version": "0.13.3", "identifier": "io.github.jpicklyk.logtapper", "build": { "frontendDist": "../dist-solid", From 90fdadd240d7da7e6012f0b1da1562d502e40f96 Mon Sep 17 00:00:00 2001 From: jpicklyk <5747775+jpicklyk@users.noreply.github.com> Date: Tue, 22 Sep 2026 10:37:52 -0400 Subject: [PATCH 2/2] fix(updater): tell the elevated installer which scope to install into MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Elevation alone was not enough. Tauri's NSIS template sets MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY/..._VALUENAME "CurrentUser", so MultiUser picks its scope from that value and reads HKCU before HKLM. On a machine this bug has already damaged — an all-users install plus the per-user copy an earlier update created — the per-user key wins and even an elevated installer updates the wrong one. Measured on this machine 2026-09-22 with a local e2e (test keypair, localhost endpoint, two signed NSIS builds): elevation alone moved HKCU 0.13.2 -> 0.13.4 and left HKLM at 0.13.3. With /allusers, the same setup moved HKLM 0.13.3 -> 0.13.4 and left the per-user decoy at 0.13.3. MULTIUSER_INSTALLMODE_COMMANDLINE is defined, so the switch overrides the registry default. It precedes /ARGS because everything after that is handed to the relaunched app. Co-Authored-By: Claude Opus 5 --- src-tauri/src/commands/app_update.rs | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src-tauri/src/commands/app_update.rs b/src-tauri/src/commands/app_update.rs index 1960be7..e548709 100644 --- a/src-tauri/src/commands/app_update.rs +++ b/src-tauri/src/commands/app_update.rs @@ -437,9 +437,26 @@ mod elevated { /// `plugins.updater.windows.installMode = "passive"` (tauri.conf.json): /// `/P`, `/UPDATE`, `/R` (relaunch), then `/ARGS` followed by this /// process's own arguments so a file opened from the command line survives - /// the restart. + /// the restart — plus `/allusers`, which the plugin never sends. + /// + /// That switch is the difference between elevating and actually updating + /// the right install. Tauri's template sets + /// `MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY`/`…_VALUENAME "CurrentUser"`, + /// so MultiUser picks its default scope by reading that value — and reads + /// HKCU before HKLM. On a machine this bug has already damaged (an + /// all-users install plus the per-user copy an earlier update created) the + /// per-user key wins, so even an elevated installer updates the per-user + /// copy and leaves `Program Files` behind. Measured exactly that way on + /// 2026-09-22: elevation alone moved HKCU 0.13.2 → 0.13.4 while HKLM sat + /// at 0.13.3. `MULTIUSER_INSTALLMODE_COMMANDLINE` is defined, so the + /// switch overrides the registry default; it must precede `/ARGS`, since + /// everything after that belongs to the relaunched app. + /// + /// Sending it unconditionally is safe because this whole module only runs + /// when [`is_machine_install`] already said the running exe *is* the + /// all-users one. pub(super) fn installer_parameters(current_args: &[OsString]) -> OsString { - let mut out = OsString::from("/P /UPDATE /R /ARGS"); + let mut out = OsString::from("/P /allusers /UPDATE /R /ARGS"); for arg in current_args { out.push(" "); out.push(escape_nsis_current_exe_arg(arg)); @@ -527,8 +544,17 @@ mod elevated { } #[test] - fn parameters_match_the_plugins_passive_mode_set() { - assert_eq!(installer_parameters(&[]), OsString::from("/P /UPDATE /R /ARGS")); + fn parameters_are_the_plugins_passive_mode_set_plus_an_explicit_scope() { + assert_eq!(installer_parameters(&[]), OsString::from("/P /allusers /UPDATE /R /ARGS")); + } + + #[test] + fn the_scope_switch_precedes_args() { + // Everything after /ARGS is handed to the relaunched app, so a + // scope switch on the wrong side of it silently does nothing. + let rendered = installer_parameters(&[OsString::from("x")]).to_string_lossy().to_string(); + let (allusers, args) = (rendered.find("/allusers").unwrap(), rendered.find("/ARGS").unwrap()); + assert!(allusers < args, "{rendered}"); } #[test] @@ -553,7 +579,7 @@ mod elevated { let args = [OsString::from(r"D:\logs\my file.log"), OsString::from("--flag/x"), OsString::from("plain")]; assert_eq!( installer_parameters(&args), - OsString::from(r#"/P /UPDATE /R /ARGS "D:\logs\my file.log" "--flag/x" plain"#) + OsString::from(r#"/P /allusers /UPDATE /R /ARGS "D:\logs\my file.log" "--flag/x" plain"#) ); } }