Skip to content

Commit 165ca76

Browse files
authored
fix(plan): forward LOCALAPPDATA so Node's compile cache stays outside the workspace (#389)
`DEFAULT_UNTRACKED_ENV` already forwards most Windows env vars to cached tasks, but `LOCALAPPDATA` was missing. Node's compile cache uses it to decide where to put cache files on Windows. Without it, the cache lands inside the workspace, the runner sees the same files both written and read in one run, and refuses to cache anything with `not cached because it modified its input`. Add `LOCALAPPDATA` to the list, and mirror it in the e2e harness so the accompanying Node compile-cache fixture goes from failing on Windows to passing on every platform.
1 parent 7644bae commit 165ca76

10 files changed

Lines changed: 109 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 'a';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 'b';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 'c';
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "node-compile-cache-outside-workspace-fixture",
3+
"private": true,
4+
"type": "module"
5+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Tiny Node script that turns on the v22 compile cache and imports a
2+
// few sibling modules so the cache directory actually gets some files
3+
// written to it. On a normally-configured machine the cache lives in
4+
// the OS temp directory (outside the workspace), so the runner doesn't
5+
// see those files when it decides whether the run can be cached.
6+
//
7+
// If the spawned process doesn't have LOCALAPPDATA (or TMP/TEMP/
8+
// USERPROFILE) set on Windows, Node ends up putting the cache inside
9+
// the workspace, the same files are both written and read in this one
10+
// run, and the runner refuses to cache it. That's the bug this fixture
11+
// catches.
12+
import { enableCompileCache } from 'node:module';
13+
14+
enableCompileCache();
15+
16+
await import('./a.mjs');
17+
await import('./b.mjs');
18+
await import('./c.mjs');
19+
20+
console.log('done');
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[[e2e]]
2+
name = "node_compile_cache_does_not_poison_workspace"
3+
comment = """
4+
Runs a small Node script that turns on Node's compile cache. The cache should land in the OS temp directory (outside the workspace), so two `vt run --cache build` calls should be a miss then a hit. On Windows, if the spawned task env doesn't have `LOCALAPPDATA`, Node puts the cache inside the workspace instead, the runner sees the same files both written and read, and refuses to cache the run — so the second call becomes another miss with a "not cached because it modified its input" message.
5+
"""
6+
ignore = true
7+
steps = [
8+
{ argv = [
9+
"vt",
10+
"run",
11+
"--cache",
12+
"build",
13+
], comment = "first run: cache miss" },
14+
{ argv = [
15+
"vt",
16+
"run",
17+
"--cache",
18+
"build",
19+
], comment = "second run: cache hit" },
20+
]
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# node_compile_cache_does_not_poison_workspace
2+
3+
Runs a small Node script that turns on Node's compile cache. The cache should land in the OS temp directory (outside the workspace), so two `vt run --cache build` calls should be a miss then a hit. On Windows, if the spawned task env doesn't have `LOCALAPPDATA`, Node puts the cache inside the workspace instead, the runner sees the same files both written and read, and refuses to cache the run — so the second call becomes another miss with a "not cached because it modified its input" message.
4+
5+
## `vt run --cache build`
6+
7+
first run: cache miss
8+
9+
```
10+
$ node run.mjs
11+
done
12+
```
13+
14+
## `vt run --cache build`
15+
16+
second run: cache hit
17+
18+
```
19+
$ node run.mjs ◉ cache hit, replaying
20+
done
21+
22+
---
23+
vt run: cache hit.
24+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"tasks": {
3+
"build": {
4+
"command": "node run.mjs",
5+
"cache": true
6+
}
7+
}
8+
}

crates/vite_task_bin/tests/e2e_snapshots/main.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,30 @@ fn run_case(
392392
// On Windows, ensure common executable extensions are included in PATHEXT for command resolution in subprocesses.
393393
if cfg!(windows) {
394394
cmd.env("PATHEXT", ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC");
395+
// Forward the Windows env vars Node needs to find a real
396+
// temp directory. Without these, Node's compile cache and
397+
// similar helpers fall back to a workspace-relative path
398+
// and break cached runs. Values come from cargo-test's
399+
// own env when present.
400+
for name in [
401+
"TMP",
402+
"TEMP",
403+
"APPDATA",
404+
"LOCALAPPDATA",
405+
"PROGRAMDATA",
406+
"USERPROFILE",
407+
"HOMEDRIVE",
408+
"HOMEPATH",
409+
"WINDIR",
410+
"SYSTEMROOT",
411+
"SYSTEMDRIVE",
412+
"ProgramFiles",
413+
"ProgramFiles(x86)",
414+
] {
415+
if let Some(value) = env::var_os(name) {
416+
cmd.env(name, value);
417+
}
418+
}
395419
}
396420
for (k, v) in step.envs() {
397421
let resolved = resolve_env_placeholder(v.as_str());

crates/vite_task_graph/src/config/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,11 @@ pub const DEFAULT_UNTRACKED_ENV: &[&str] = &[
445445
"RUNNER_*",
446446
// Windows specific
447447
"APPDATA",
448+
// Node's compile cache uses LOCALAPPDATA to pick its cache directory
449+
// on Windows. Without it the cache lands inside the workspace and
450+
// breaks every cached task that opts into the compile cache. See
451+
// the `node_compile_cache_outside_workspace` e2e fixture.
452+
"LOCALAPPDATA",
448453
"PROGRAMDATA",
449454
"SYSTEMROOT",
450455
"SYSTEMDRIVE",

0 commit comments

Comments
 (0)