Skip to content

Commit 7ba7e0a

Browse files
wan9chiclaude
andcommitted
fix(runner): address PR review on env tracking + client API
- Rename the `@voidzero-dev/vite-task-client` exports `fetchEnv`/`fetchEnvs` to `getEnv`/`getEnvs`: the calls are synchronous, so the `fetch*` naming was misleading (per review). - `getEnv` now always consults the runner, even when `process.env[name]` is already set, so the dependency is still recorded for cache invalidation when the value was injected by the shell/prefix env. - `collect_tracked_env_globs` no longer drops names already covered by the user's declared `env`. Lookup-time validation re-expands the glob over the whole parent env, so a filtered match-set always diffed as having `added` entries and missed the cache deterministically for tasks that both declare `env` and call `getEnvs` on an overlapping pattern. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 53d7bd1 commit 7ba7e0a

15 files changed

Lines changed: 64 additions & 54 deletions

File tree

crates/vite_task/src/session/execute/mod.rs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -826,10 +826,8 @@ pub async fn execute_spawn(
826826
.as_ref()
827827
.map(|r| collect_tracked_envs(r, metadata))
828828
.unwrap_or_default();
829-
let tracked_env_globs = reports
830-
.as_ref()
831-
.map(|r| collect_tracked_env_globs(r, metadata))
832-
.unwrap_or_default();
829+
let tracked_env_globs =
830+
reports.as_ref().map(collect_tracked_env_globs).unwrap_or_default();
833831

834832
// Paths already in globbed_inputs are skipped: the overlap check
835833
// above guarantees no input modification, so the prerun hash is
@@ -955,33 +953,32 @@ fn collect_tracked_envs(reports: &Reports, metadata: &CacheMetadata) -> BTreeMap
955953
}
956954

957955
/// Select tool-reported env-glob records to embed in the post-run
958-
/// fingerprint. Only `tracked: true` records are included, and within each
959-
/// matched set, names already covered by the user's declared `env` (via the
960-
/// spawn fingerprint) are skipped — they're redundant and could cause
961-
/// spurious cache misses if the user later changes their `env` config.
962-
fn collect_tracked_env_globs(
963-
reports: &Reports,
964-
metadata: &CacheMetadata,
965-
) -> BTreeMap<Str, BTreeMap<Str, Str>> {
966-
let fingerprinted = &metadata.spawn_fingerprint.env_fingerprints().fingerprinted_envs;
956+
/// fingerprint. Only `tracked: true` records are included, and the full
957+
/// match-set is stored as-is.
958+
///
959+
/// Unlike [`collect_tracked_envs`], names already covered by the user's
960+
/// declared `env` are *not* filtered out: lookup-time validation re-expands
961+
/// the glob over the whole parent env (see [`PostRunFingerprint::validate`]),
962+
/// so a filtered match-set would always diff as having `added` entries and
963+
/// miss the cache deterministically. Storing user-declared names here is
964+
/// harmless — a change to their value already shifts the spawn fingerprint,
965+
/// invalidating the cache key before the post-run fingerprint is consulted.
966+
fn collect_tracked_env_globs(reports: &Reports) -> BTreeMap<Str, BTreeMap<Str, Str>> {
967967
reports
968968
.env_glob_records
969969
.iter()
970970
.filter(|(_, record)| record.tracked)
971971
.map(|(pattern, record)| {
972-
let filtered: BTreeMap<Str, Str> = record
972+
let matches: BTreeMap<Str, Str> = record
973973
.matches
974974
.iter()
975975
.filter_map(|(name, value)| {
976976
let name_str = name.to_str()?;
977-
if fingerprinted.contains_key(name_str) {
978-
return None;
979-
}
980977
let value_str = value.to_str()?;
981978
Some((Str::from(name_str), Str::from(value_str)))
982979
})
983980
.collect();
984-
(Str::from(pattern.as_ref()), filtered)
981+
(Str::from(pattern.as_ref()), matches)
985982
})
986983
.collect()
987984
}

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/scripts/fetch_env.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { fetchEnv } from '@voidzero-dev/vite-task-client';
1+
import { getEnv } from '@voidzero-dev/vite-task-client';
22
import { writeFileSync, mkdirSync } from 'node:fs';
33

4-
// fetchEnv populates process.env from the runner and — with tracked: true —
4+
// getEnv populates process.env from the runner and — with tracked: true —
55
// adds the env to the post-run fingerprint, so a change between runs
66
// invalidates the cache.
7-
fetchEnv('PROBE_ENV', { tracked: true });
7+
getEnv('PROBE_ENV', { tracked: true });
88
const value = process.env.PROBE_ENV ?? '(unset)';
99

1010
mkdirSync('dist', { recursive: true });

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/scripts/fetch_envs.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { fetchEnvs } from '@voidzero-dev/vite-task-client';
1+
import { getEnvs } from '@voidzero-dev/vite-task-client';
22
import { writeFileSync, mkdirSync } from 'node:fs';
33

4-
// fetchEnvs asks the runner for every env matching the glob. The glob (plus
4+
// getEnvs asks the runner for every env matching the glob. The glob (plus
55
// its match-set) becomes part of the post-run fingerprint, so adding,
66
// removing, or changing any matching env invalidates the cache on the next
77
// run. The non-matching UNRELATED envs set by some test steps must not
88
// contribute.
9-
const matches = fetchEnvs('PROBE_*', { tracked: true });
9+
const matches = getEnvs('PROBE_*', { tracked: true });
1010

1111
mkdirSync('dist', { recursive: true });
1212
const sorted = Object.entries(matches).sort(([a], [b]) => a.localeCompare(b));

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/snapshots.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ steps = [
9191
[[e2e]]
9292
name = "fetch_envs_tracks_glob_match_set"
9393
comment = """
94-
Exercises `fetchEnvs(pattern, { tracked: true })`. The glob `PROBE_*` and
94+
Exercises `getEnvs(pattern, { tracked: true })`. The glob `PROBE_*` and
9595
its match-set snapshot enter the post-run fingerprint: later runs diff the
9696
current match-set against what was stored and miss on add / remove / change,
9797
but hit when only non-matching envs differ.
@@ -209,7 +209,7 @@ steps = [
209209
[[e2e]]
210210
name = "fetch_env_tracked_invalidates_on_change"
211211
comment = """
212-
Exercises `fetchEnv(name, { tracked: true })`. The env value becomes part
212+
Exercises `getEnv(name, { tracked: true })`. The env value becomes part
213213
of the post-run fingerprint: the same value still hits, a different value
214214
misses with `tracked env 'PROBE_ENV' changed`.
215215
"""

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/snapshots/fetch_env_tracked_invalidates_on_change.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# fetch_env_tracked_invalidates_on_change
22

3-
Exercises `fetchEnv(name, { tracked: true })`. The env value becomes part
3+
Exercises `getEnv(name, { tracked: true })`. The env value becomes part
44
of the post-run fingerprint: the same value still hits, a different value
55
misses with `tracked env 'PROBE_ENV' changed`.
66

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/snapshots/fetch_envs_tracks_glob_match_set.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# fetch_envs_tracks_glob_match_set
22

3-
Exercises `fetchEnvs(pattern, { tracked: true })`. The glob `PROBE_*` and
3+
Exercises `getEnvs(pattern, { tracked: true })`. The glob `PROBE_*` and
44
its match-set snapshot enter the post-run fingerprint: later runs diff the
55
current match-set against what was stored and miss on add / remove / change,
66
but hit when only non-matching envs differ.

crates/vite_task_bin/tests/e2e_snapshots/fixtures/vite_build_cache/snapshots.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ steps = [
4646
name = "vite_prefix_env_change_invalidates_cache"
4747
comment = """
4848
`VITE_MODE` is picked up by Vite's patched `loadEnv`, which asks the runner
49-
for every `VITE_*` env via `fetchEnvs(pattern, { tracked: true })`. Flipping
49+
for every `VITE_*` env via `getEnvs(pattern, { tracked: true })`. Flipping
5050
its value between runs must invalidate the cache AND change the build output
5151
— Vite's `define` plugin substitutes `import.meta.env.VITE_MODE` at build
5252
time, so dead-code elimination leaves only the branch matching the value.

crates/vite_task_bin/tests/e2e_snapshots/fixtures/vite_build_cache/snapshots/vite_prefix_env_change_invalidates_cache.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# vite_prefix_env_change_invalidates_cache
22

33
`VITE_MODE` is picked up by Vite's patched `loadEnv`, which asks the runner
4-
for every `VITE_*` env via `fetchEnvs(pattern, { tracked: true })`. Flipping
4+
for every `VITE_*` env via `getEnvs(pattern, { tracked: true })`. Flipping
55
its value between runs must invalidate the cache AND change the build output
66
— Vite's `define` plugin substitutes `import.meta.env.VITE_MODE` at build
77
time, so dead-code elimination leaves only the branch matching the value.

crates/vite_task_bin/tests/e2e_snapshots/fixtures/vite_build_cache/vite-task.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"build": {
44
"command": "vite build",
55
// No `"env": [...]` — vite's patched `loadEnv` asks the runner for
6-
// every `VITE_*` env via `fetchEnvs`, so the glob + match-set are
6+
// every `VITE_*` env via `getEnvs`, so the glob + match-set are
77
// fingerprinted automatically.
88
"cache": true
99
}

docs/runner-task-ipc/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Crate / package responsibilities:
3030
- `vite_task_client` — sync blocking Rust client; `Client::from_envs` no-ops when the IPC env is absent.
3131
- `vite_task_client_napi` — NAPI cdylib exposing the client's methods to Node.
3232
- `materialized_artifact` — shared machinery for embedding the cdylib into `vp` and writing it to a temp file on first use (extracted from `fspy`).
33-
- `@voidzero-dev/vite-task-client` — npm package with JSDoc-typed wrappers (`ignoreInput`, `ignoreOutput`, `disableCache`, `fetchEnv`, `fetchEnvs`). Lazy-loads the addon and silently no-ops when it can't connect, so tools don't break when run outside `vp`.
33+
- `@voidzero-dev/vite-task-client` — npm package with JSDoc-typed wrappers (`ignoreInput`, `ignoreOutput`, `disableCache`, `getEnv`, `getEnvs`). Lazy-loads the addon and silently no-ops when it can't connect, so tools don't break when run outside `vp`.
3434

3535
Notes:
3636

0 commit comments

Comments
 (0)