Address review: Linux data-dir caches, bash invocation, pre-webview clear

Three review findings, all verified:

1. wry keys the WebKitGTK base-cache dir to the app DATA dir (same as
base-data), so on Linux the stale frontend cache also lives under
~/.local/share/ai.unsloth.studio. Clear the cache-typed subdirs there
(WebKitCache, CacheStorage, serviceworkers) while keeping localstorage,
indexeddb, and cookies. Tests extended to 23 assertions.

2. run_all.sh invoked the new test with sh, but the extracted setup.sh
function uses bash arrays; dash aborts with a syntax error before any
assertion. Invoke with bash.

3. The in-app desktop update runs start_backend_update before
downloadAndInstall/relaunch, so setup.ps1/setup.sh clear caches while
the live WebView still holds them and silently fail. Clear the caches
from the Rust side in main() before the Builder runs: the config window
(and the WebView lock) exists by the time setup hooks fire, so this is
the one point where the profile is guaranteed unlocked. Same cache-only
path lists per OS; cargo check passes.
This commit is contained in:
shimmyshimmer 2026-07-23 02:35:45 -07:00
commit cbb1508513
4 changed files with 101 additions and 15 deletions

View file

@ -492,7 +492,18 @@ _clear_webview_caches() {
)
;;
Linux)
_wvc_paths=("${XDG_CACHE_HOME:-$HOME/.cache}/$_wvc_bid")
# wry keys the WebKitGTK base-cache dir to the app DATA dir (same
# as base-data), so cache subdirs also live under
# ~/.local/share/<bid>. Clear only the cache-typed subdirs there;
# sibling localstorage/, indexeddb/, and cookies stay.
_wvc_data="${XDG_DATA_HOME:-$HOME/.local/share}/$_wvc_bid"
_wvc_paths=(
"${XDG_CACHE_HOME:-$HOME/.cache}/$_wvc_bid"
"$_wvc_data/WebKitCache"
"$_wvc_data/CacheStorage"
"$_wvc_data/serviceworkers"
"$_wvc_data/ServiceWorkers"
)
;;
*) return 0 ;;
esac

View file

@ -161,6 +161,64 @@ fn setup_tray(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
// Clear WebView caches before the webview initializes. An in-app update runs
// setup.sh/setup.ps1 while the old WebView still holds these files (its clear
// silently fails), so without this a relaunch can serve the previous frontend
// from cache. Cache-only paths; LocalStorage, IndexedDB, cookies, and app
// data are kept. Mirrors setup.sh _clear_webview_caches / setup.ps1.
fn clear_webview_caches(bundle_id: &str) {
use std::path::PathBuf;
let mut paths: Vec<PathBuf> = Vec::new();
#[cfg(target_os = "windows")]
if let Ok(local) = std::env::var("LOCALAPPDATA") {
if !local.is_empty() {
let profile = PathBuf::from(local)
.join(bundle_id)
.join("EBWebView")
.join("Default");
for sub in ["Cache", "Code Cache", "GPUCache", "Service Worker"] {
paths.push(profile.join(sub));
}
}
}
#[cfg(target_os = "macos")]
if let Ok(home) = std::env::var("HOME") {
if !home.is_empty() {
let home = PathBuf::from(home);
paths.push(home.join("Library/Caches").join(bundle_id));
let data = home.join("Library/WebKit").join(bundle_id).join("WebsiteData");
for sub in ["CacheStorage", "ServiceWorkers", "DiskCache"] {
paths.push(data.join(sub));
}
}
}
#[cfg(target_os = "linux")]
if let Ok(home) = std::env::var("HOME") {
if !home.is_empty() {
let home = PathBuf::from(home);
let cache = std::env::var("XDG_CACHE_HOME")
.ok()
.filter(|v| !v.is_empty())
.map(PathBuf::from)
.unwrap_or_else(|| home.join(".cache"));
paths.push(cache.join(bundle_id));
// wry keys the WebKitGTK base-cache dir to the app data dir.
let data = std::env::var("XDG_DATA_HOME")
.ok()
.filter(|v| !v.is_empty())
.map(PathBuf::from)
.unwrap_or_else(|| home.join(".local/share"))
.join(bundle_id);
for sub in ["WebKitCache", "CacheStorage", "serviceworkers", "ServiceWorkers"] {
paths.push(data.join(sub));
}
}
}
for p in paths {
let _ = fs::remove_dir_all(&p);
}
}
fn main() {
// Fix PATH for GUI apps (macOS .app bundles, Linux AppImage, Windows)
// GUI apps don't inherit shell dotfile PATH — this spawns the user's
@ -171,6 +229,11 @@ fn main() {
info!("Unsloth Studio desktop app starting");
windows_job::initialize();
// Must run before the Builder: the config-defined window (and its
// WebView, which locks these files) exists by the time setup hooks run.
let context = tauri::generate_context!();
clear_webview_caches(&context.config().identifier);
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
if let Some(window) = app.get_webview_window("main") {
@ -247,7 +310,7 @@ fn main() {
api.prevent_close();
}
})
.build(tauri::generate_context!())
.build(context)
.expect("error while building tauri application")
.run(|app, event| {
if let tauri::RunEvent::Exit = event {

View file

@ -14,7 +14,8 @@ sh "$TESTS_DIR/sh/test_nvcc_meets_llama_minimum.sh"
sh "$TESTS_DIR/sh/test_resolve_cuda_archs.sh"
sh "$TESTS_DIR/sh/test_strixhalo_wsl_reroute.sh"
sh "$TESTS_DIR/sh/test_uninstall_shared_icon.sh"
sh "$TESTS_DIR/sh/test_setup_webview_cache_clear.sh"
# bash, not sh: the extracted setup.sh function uses bash arrays (dash chokes).
bash "$TESTS_DIR/sh/test_setup_webview_cache_clear.sh"
sh "$TESTS_DIR/sh/test_torch_flavor.sh"
sh "$TESTS_DIR/sh/test_redact_install_output.sh"
sh "$TESTS_DIR/sh/test_install_uv_override_space.sh"

View file

@ -53,23 +53,34 @@ assert_present "macOS: IndexedDB kept" "$H/Library/WebKit/$BID
assert_present "macOS: Application Support kept" "$H/Library/Application Support/$BID"
assert_present "macOS: unrelated app cache kept" "$H/Library/Caches/com.other.app"
# ── 2. Linux: cache dir removed, data/config kept ──
# ── 2. Linux: cache paths removed (XDG cache dir AND the cache subdirs wry
# keys to the app data dir), user-facing storage kept ──
H=$(mktemp -d -p "$_TMP_ROOT")
mkdir -p "$H/.cache/$BID" "$H/.local/share/$BID" "$H/.config/$BID" "$H/.cache/other.app"
D="$H/.local/share/$BID"
mkdir -p "$H/.cache/$BID" "$D/WebKitCache" "$D/CacheStorage" "$D/serviceworkers" \
"$D/localstorage" "$D/indexeddb" "$H/.config/$BID" "$H/.cache/other.app"
: > "$D/cookies.sqlite"
uname() { echo Linux; }
HOME="$H" XDG_CACHE_HOME="" _clear_webview_caches
assert_gone "linux: ~/.cache/$BID removed" "$H/.cache/$BID"
assert_present "linux: ~/.local/share/$BID kept" "$H/.local/share/$BID"
assert_present "linux: ~/.config/$BID kept" "$H/.config/$BID"
assert_present "linux: unrelated app cache kept" "$H/.cache/other.app"
HOME="$H" XDG_CACHE_HOME="" XDG_DATA_HOME="" _clear_webview_caches
assert_gone "linux: ~/.cache/$BID removed" "$H/.cache/$BID"
assert_gone "linux: data-dir WebKitCache removed" "$D/WebKitCache"
assert_gone "linux: data-dir CacheStorage removed" "$D/CacheStorage"
assert_gone "linux: data-dir serviceworkers removed" "$D/serviceworkers"
assert_present "linux: localstorage kept" "$D/localstorage"
assert_present "linux: indexeddb kept" "$D/indexeddb"
assert_present "linux: cookies kept" "$D/cookies.sqlite"
assert_present "linux: ~/.config/$BID kept" "$H/.config/$BID"
assert_present "linux: unrelated app cache kept" "$H/.cache/other.app"
# ── 3. Linux: XDG_CACHE_HOME override honored ──
# ── 3. Linux: XDG_CACHE_HOME / XDG_DATA_HOME overrides honored ──
H=$(mktemp -d -p "$_TMP_ROOT")
XDG=$(mktemp -d -p "$_TMP_ROOT")
mkdir -p "$XDG/$BID" "$H/.cache/$BID"
HOME="$H" XDG_CACHE_HOME="$XDG" _clear_webview_caches
assert_gone "linux: XDG_CACHE_HOME/$BID removed" "$XDG/$BID"
assert_present "linux: ~/.cache/$BID kept under override" "$H/.cache/$BID"
mkdir -p "$XDG/cache/$BID" "$XDG/data/$BID/WebKitCache" "$XDG/data/$BID/localstorage" "$H/.cache/$BID"
HOME="$H" XDG_CACHE_HOME="$XDG/cache" XDG_DATA_HOME="$XDG/data" _clear_webview_caches
assert_gone "linux: XDG_CACHE_HOME/$BID removed" "$XDG/cache/$BID"
assert_gone "linux: XDG_DATA_HOME WebKitCache removed" "$XDG/data/$BID/WebKitCache"
assert_present "linux: XDG_DATA_HOME localstorage kept" "$XDG/data/$BID/localstorage"
assert_present "linux: ~/.cache/$BID kept under override" "$H/.cache/$BID"
# ── 4. Nothing to clear is a clean no-op ──
H=$(mktemp -d -p "$_TMP_ROOT")