diff --git a/studio/setup.sh b/studio/setup.sh index 749c585a26..13d654cdd6 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -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/. 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 diff --git a/studio/src-tauri/src/main.rs b/studio/src-tauri/src/main.rs index 405b390177..e422124f0c 100644 --- a/studio/src-tauri/src/main.rs +++ b/studio/src-tauri/src/main.rs @@ -161,6 +161,64 @@ fn setup_tray(app: &tauri::App) -> Result<(), Box> { 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 = 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 { diff --git a/tests/run_all.sh b/tests/run_all.sh index 1b5609c475..147aaf1645 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -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" diff --git a/tests/sh/test_setup_webview_cache_clear.sh b/tests/sh/test_setup_webview_cache_clear.sh index de2ab157d2..19a3ee0064 100755 --- a/tests/sh/test_setup_webview_cache_clear.sh +++ b/tests/sh/test_setup_webview_cache_clear.sh @@ -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")