* fix: Windows compatibility across postinstall, desktop, plugins, and scripts * fix: clean script gracefully skips locked files on Windows * test(file-secrets): add vitest coverage for xdgDataHome Exports `xdgDataHome` and adds 9 parametrized vitest cases covering: - XDG_DATA_HOME override (all platforms, with whitespace trimming) - posix $HOME/.local/share fallback + default when HOME is unset - win32 LOCALAPPDATA → APPDATA → USERPROFILE\AppData\Local chain Replaces the ad-hoc scripts/_test-windows.mjs verification script, which was never wired into CI and re-implemented the logic inline instead of testing the real code. * fix(desktop): guard registry PATH update against parse failures If `reg query HKCU\Environment /v Path` exits non-zero for any reason other than 'value not present' (exit 1), or if the output doesn't match the expected `Path REG(_EXPAND)?_SZ <value>` shape, bail out instead of writing. Previously a parse failure would silently treat current PATH as empty and overwrite it with only the executor bin dir. ---------
Showing the changes to scripts (1 of 9 files). Show all
Unified · Split
| @@ -0,0 +1,60 @@ | |||
| 1 | #!/usr/bin/env bun | ||
| 2 | import fs from "node:fs"; | ||
| 3 | import path from "node:path"; | ||
| 4 | import { fileURLToPath } from "node:url"; | ||
| 5 | |||
| 6 | const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); | ||
| 7 | |||
| 8 | const rootTargets = ["node_modules", ".turbo", ".astro", "dist"]; | ||
| 9 | for (const name of rootTargets) { | ||
| 10 | const p = path.join(root, name); | ||
| 11 | if (fs.existsSync(p)) { | ||
| 12 | try { | ||
| 13 | fs.rmSync(p, { recursive: true, force: true }); | ||
| 14 | console.log("removed", p); | ||
| 15 | } catch (e: any) { | ||
| 16 | console.warn("skipped (in use?):", p, "-", e.message); | ||
| 17 | } | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | const nestedTargets = new Set(["node_modules", "dist", ".turbo", ".output", ".astro"]); | ||
| 22 | const nestedGlobs = /\.tsbuildinfo$/; | ||
| 23 | const searchRoots = ["apps", "packages"]; | ||
| 24 | const maxDepth = 5; | ||
| 25 | |||
| 26 | function clean(dir: string, depth: number) { | ||
| 27 | if (depth > maxDepth) return; | ||
| 28 | let entries: fs.Dirent[]; | ||
| 29 | try { | ||
| 30 | entries = fs.readdirSync(dir, { withFileTypes: true }); | ||
| 31 | } catch { | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | for (const entry of entries) { | ||
| 35 | const full = path.join(dir, entry.name); | ||
| 36 | if (entry.isDirectory()) { | ||
| 37 | if (nestedTargets.has(entry.name)) { | ||
| 38 | try { | ||
| 39 | fs.rmSync(full, { recursive: true, force: true }); | ||
| 40 | console.log("removed", full); | ||
| 41 | } catch (e: any) { | ||
| 42 | console.warn("skipped (in use?):", full, "-", e.message); | ||
| 43 | } | ||
| 44 | } else { | ||
| 45 | clean(full, depth + 1); | ||
| 46 | } | ||
| 47 | } else if (entry.isFile() && nestedGlobs.test(entry.name)) { | ||
| 48 | try { | ||
| 49 | fs.rmSync(full, { force: true }); | ||
| 50 | console.log("removed", full); | ||
| 51 | } catch (e: any) { | ||
| 52 | console.warn("skipped (in use?):", full, "-", e.message); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | for (const dir of searchRoots) { | ||
| 59 | clean(path.join(root, dir), 1); | ||
| 60 | } | ||