Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Tasks are defined in `vite-task.json`:
"dependsOn": ["build", "package#task"],
"cache": true,
"env": ["NODE_ENV"],
"passThroughEnvs": ["CI"],
"untrackedEnv": ["CI"],
"input": ["src/**", "!dist/**", { "auto": true }]
}
}
Expand All @@ -146,7 +146,7 @@ Tasks are defined in `vite-task.json`:
- `dependsOn`: explicit task dependencies (`taskName` or `package#task`)
- `cache` (task): enable/disable caching for this task (default: `true`)
- `env`: env var names to fingerprint and pass to the task
- `passThroughEnvs`: env var names to pass without fingerprinting
- `untrackedEnv`: env var names to pass without fingerprinting
- `input`: files for cache fingerprinting (globs, `{ "auto": true }`, negation patterns)

## Task Dependencies
Expand Down
12 changes: 6 additions & 6 deletions crates/vite_task/docs/task-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub struct SpawnFingerprint {
pub cwd: RelativePathBuf,
pub command_fingerprint: CommandFingerprint,
pub fingerprinted_envs: BTreeMap<Str, Str>,
pub pass_through_envs: BTreeSet<Str>,
pub untracked_env: BTreeSet<Str>,
pub fingerprint_ignores: Option<Vec<Str>>,
}

Expand All @@ -124,25 +124,25 @@ This ensures cache invalidation when:

- Working directory changes (package location changes)
- Command or arguments change
- Declared environment variables differ (pass-through envs don't affect cache)
- Declared environment variables differ (untracked envs don't affect cache)
- Program location changes (inside/outside workspace)

### 3. Environment Variable Impact on Cache

The `fingerprinted_envs` field is crucial for cache correctness:

- Only includes env vars explicitly declared in the task's `env` array
- Does NOT include pass-through envs (PATH, CI, etc.)
- Does NOT include untracked envs (PATH, CI, etc.)
- These env vars become part of the cache key

When a task runs:

1. All env vars (including pass-through) are available to the process
1. All env vars (including untracked) are available to the process
2. Only declared env vars affect the cache key
3. If a declared env var changes value, cache will miss
4. If a pass-through env changes, cache will still hit
4. If an untracked env changes, cache will still hit

The `pass_through_envs` field stores env names (not values) — if the set of pass-through env names changes, the cache invalidates, but value changes don't.
The `untracked_env` field stores env names (not values) — if the set of untracked env names changes, the cache invalidates, but value changes don't.

### 4. Execution Cache Key

Expand Down
2 changes: 1 addition & 1 deletion crates/vite_task/docs/wildcard-env-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This approach becomes cumbersome when dealing with multiple environment variable
## Non-Goals

1. Full regex support (only glob-style wildcards)
2. Wildcard patterns in `passThroughEnvs` (same as `env`)
2. Wildcard patterns in `untrackedEnv` (same as `env`)
3. Complex glob patterns like `{VITE,NODE}_*` (supported by wax crate)

## Proposed Solution
Expand Down
38 changes: 19 additions & 19 deletions crates/vite_task/src/session/cache/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ pub enum SpawnFingerprintChange {
/// Environment variable value changed
EnvValueChanged { key: Str, old_value: Str, new_value: Str },

// Pass-through env config changes
/// Pass-through env pattern added
PassThroughEnvAdded { name: Str },
/// Pass-through env pattern removed
PassThroughEnvRemoved { name: Str },
// Untracked env config changes
/// Untracked env pattern added
UntrackedEnvAdded { name: Str },
/// Untracked env pattern removed
UntrackedEnvRemoved { name: Str },

// Command changes
/// Program changed
Expand All @@ -55,11 +55,11 @@ pub fn format_spawn_change(change: &SpawnFingerprintChange) -> Str {
SpawnFingerprintChange::EnvValueChanged { key, old_value, new_value } => {
vite_str::format!("env {key} value changed from '{old_value}' to '{new_value}'")
}
SpawnFingerprintChange::PassThroughEnvAdded { name } => {
vite_str::format!("pass-through env '{name}' added")
SpawnFingerprintChange::UntrackedEnvAdded { name } => {
vite_str::format!("untracked env '{name}' added")
}
SpawnFingerprintChange::PassThroughEnvRemoved { name } => {
vite_str::format!("pass-through env '{name}' removed")
SpawnFingerprintChange::UntrackedEnvRemoved { name } => {
vite_str::format!("untracked env '{name}' removed")
}
SpawnFingerprintChange::ProgramChanged => Str::from("program changed"),
SpawnFingerprintChange::ArgsChanged => Str::from("args changed"),
Expand Down Expand Up @@ -104,14 +104,14 @@ pub fn detect_spawn_fingerprint_changes(
}
}

// Check pass-through env config changes
let old_pass_through: FxHashSet<_> = old_env.pass_through_env_config.iter().collect();
let new_pass_through: FxHashSet<_> = new_env.pass_through_env_config.iter().collect();
for name in old_pass_through.difference(&new_pass_through) {
changes.push(SpawnFingerprintChange::PassThroughEnvRemoved { name: (*name).clone() });
// Check untracked env config changes
let old_untracked: FxHashSet<_> = old_env.untracked_env_config.iter().collect();
let new_untracked: FxHashSet<_> = new_env.untracked_env_config.iter().collect();
for name in old_untracked.difference(&new_untracked) {
changes.push(SpawnFingerprintChange::UntrackedEnvRemoved { name: (*name).clone() });
}
for name in new_pass_through.difference(&old_pass_through) {
changes.push(SpawnFingerprintChange::PassThroughEnvAdded { name: (*name).clone() });
for name in new_untracked.difference(&old_untracked) {
changes.push(SpawnFingerprintChange::UntrackedEnvAdded { name: (*name).clone() });
}

// Check program changes
Expand Down Expand Up @@ -164,9 +164,9 @@ pub fn format_cache_status_inline(cache_status: &CacheStatus) -> Option<Str> {
| SpawnFingerprintChange::EnvValueChanged { .. },
) => "envs changed",
Some(
SpawnFingerprintChange::PassThroughEnvAdded { .. }
| SpawnFingerprintChange::PassThroughEnvRemoved { .. },
) => "pass-through env config changed",
SpawnFingerprintChange::UntrackedEnvAdded { .. }
| SpawnFingerprintChange::UntrackedEnvRemoved { .. },
) => "untracked env config changed",
Some(SpawnFingerprintChange::ProgramChanged) => "program changed",
Some(SpawnFingerprintChange::ArgsChanged) => "args changed",
Some(SpawnFingerprintChange::CwdChanged) => "working directory changed",
Expand Down
4 changes: 2 additions & 2 deletions crates/vite_task_bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn synthesize_node_modules_bin_task(
args: args.into(),
cache_config: UserCacheConfig::with_config(EnabledCacheConfig {
env: None,
pass_through_envs: None,
untracked_env: None,
input: None,
}),
envs: Arc::clone(envs),
Expand Down Expand Up @@ -128,7 +128,7 @@ impl vite_task::CommandHandler for CommandHandler {
cache_config: UserCacheConfig::with_config({
EnabledCacheConfig {
env: None,
pass_through_envs: Some(vec![name]),
untracked_env: Some(vec![name]),
input: None,
}
}),
Expand Down
2 changes: 1 addition & 1 deletion crates/vite_task_bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ async fn run() -> anyhow::Result<ExitStatus> {
cache_config: UserCacheConfig::with_config({
EnabledCacheConfig {
env: Some(Box::from([Str::from("FOO")])),
pass_through_envs: None,
untracked_env: None,
input: None,
}
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ steps = [
]

[[e2e]]
name = "pass-through env added"
name = "untracked env added"
steps = [
"vp run test # cache miss",
"json-edit vite-task.json \"_.tasks.test.passThroughEnvs = ['MY_PASSTHROUGH']\" # add pass-through env",
"vp run test # cache miss: pass-through env added",
"json-edit vite-task.json \"_.tasks.test.untrackedEnv = ['MY_UNTRACKED']\" # add untracked env",
"vp run test # cache miss: untracked env added",
]

[[e2e]]
name = "pass-through env removed"
name = "untracked env removed"
steps = [
"json-edit vite-task.json \"_.tasks.test.passThroughEnvs = ['MY_PASSTHROUGH']\" # setup",
"json-edit vite-task.json \"_.tasks.test.untrackedEnv = ['MY_UNTRACKED']\" # setup",
"vp run test # cache miss",
"json-edit vite-task.json \"delete _.tasks.test.passThroughEnvs\" # remove pass-through env",
"vp run test # cache miss: pass-through env removed",
"json-edit vite-task.json \"delete _.tasks.test.untrackedEnv\" # remove untracked env",
"vp run test # cache miss: untracked env removed",
]

[[e2e]]
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
expression: e2e_outputs
---
> vp run test # cache miss
$ print-file test.txt
initial content
> json-edit vite-task.json "_.tasks.test.untrackedEnv = ['MY_UNTRACKED']" # add untracked env

> vp run test # cache miss: untracked env added
$ print-file test.txt ✗ cache miss: untracked env config changed, executing
initial content
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
expression: e2e_outputs
---
> json-edit vite-task.json "_.tasks.test.untrackedEnv = ['MY_UNTRACKED']" # setup

> vp run test # cache miss
$ print-file test.txt
initial content
> json-edit vite-task.json "delete _.tasks.test.untrackedEnv" # remove untracked env

> vp run test # cache miss: untracked env removed
$ print-file test.txt ✗ cache miss: untracked env config changed, executing
initial content
2 changes: 1 addition & 1 deletion crates/vite_task_graph/run-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type Task = {
/**
* Environment variable names to be passed to the task without fingerprinting.
*/
passThroughEnvs?: Array<string>;
untrackedEnv?: Array<string>;
/**
* Files to include in the cache fingerprint.
*
Expand Down
15 changes: 6 additions & 9 deletions crates/vite_task_graph/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,9 @@ impl ResolvedTaskOptions {
let cache_config = match user_options.cache_config {
UserCacheConfig::Disabled { cache: MustBe!(false) } => None,
UserCacheConfig::Enabled { cache: _, enabled_cache_config } => {
let mut pass_through_envs: FxHashSet<Str> = enabled_cache_config
.pass_through_envs
.unwrap_or_default()
.into_iter()
.collect();
pass_through_envs.extend(DEFAULT_PASSTHROUGH_ENVS.iter().copied().map(Str::from));
let mut untracked_env: FxHashSet<Str> =
enabled_cache_config.untracked_env.unwrap_or_default().into_iter().collect();
untracked_env.extend(DEFAULT_UNTRACKED_ENV.iter().copied().map(Str::from));

let input_config = ResolvedInputConfig::from_user_config(
enabled_cache_config.input.as_ref(),
Expand All @@ -83,7 +80,7 @@ impl ResolvedTaskOptions {
.env
.map(|e| e.into_vec().into_iter().collect())
.unwrap_or_default(),
pass_through_envs,
untracked_env,
},
input_config,
})
Expand Down Expand Up @@ -238,7 +235,7 @@ pub struct EnvConfig {
/// environment variable names to be fingerprinted and passed to the task, with defaults populated
pub fingerprinted_envs: FxHashSet<Str>,
/// environment variable names to be passed to the task without fingerprinting, with defaults populated
pub pass_through_envs: FxHashSet<Str>,
pub untracked_env: FxHashSet<Str>,
}

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -305,7 +302,7 @@ impl ResolvedTaskConfig {
// Referenced from Turborepo's implementation:
// https://github.com/vercel/turborepo/blob/26d309f073ca3ac054109ba0c29c7e230e7caac3/crates/turborepo-lib/src/task_hash.rs#L439
#[doc(hidden)] // exported for redacting snapshots in tests
pub const DEFAULT_PASSTHROUGH_ENVS: &[&str] = &[
pub const DEFAULT_UNTRACKED_ENV: &[&str] = &[
// System and shell
"HOME",
"USER",
Expand Down
8 changes: 4 additions & 4 deletions crates/vite_task_graph/src/config/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct EnabledCacheConfig {
pub env: Option<Box<[Str]>>,

/// Environment variable names to be passed to the task without fingerprinting.
pub pass_through_envs: Option<Vec<Str>>,
pub untracked_env: Option<Vec<Str>>,

/// Files to include in the cache fingerprint.
///
Expand Down Expand Up @@ -126,7 +126,7 @@ impl Default for UserTaskOptions {
cache: None,
enabled_cache_config: EnabledCacheConfig {
env: None,
pass_through_envs: None,
untracked_env: None,
input: None,
},
},
Expand Down Expand Up @@ -411,15 +411,15 @@ mod tests {
let user_config_json = json!({
"cache": true,
"env": ["NODE_ENV"],
"passThroughEnvs": ["FOO"],
"untrackedEnv": ["FOO"],
});
assert_eq!(
serde_json::from_value::<UserCacheConfig>(user_config_json).unwrap(),
UserCacheConfig::Enabled {
cache: Some(MustBe!(true)),
enabled_cache_config: EnabledCacheConfig {
env: Some(std::iter::once("NODE_ENV".into()).collect()),
pass_through_envs: Some(std::iter::once("FOO".into()).collect()),
untracked_env: Some(std::iter::once("FOO".into()).collect()),
input: None,
}
},
Expand Down
8 changes: 4 additions & 4 deletions crates/vite_task_plan/src/cache_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ pub struct CacheMetadata {
///
/// # Environment Variable Impact on Cache
///
/// The `envs_without_pass_through` field is crucial for cache correctness:
/// The `envs_without_untracked` field is crucial for cache correctness:
/// - Only includes env vars explicitly declared in the task's `env` array
/// - Does NOT include pass-through envs (PATH, CI, etc.)
/// - Does NOT include untracked envs (PATH, CI, etc.)
/// - These env vars become part of the cache key
///
/// When a task runs:
/// 1. All envs (including pass-through) are available to the process
/// 1. All envs (including untracked) are available to the process
/// 2. Only declared envs affect the cache key
/// 3. If a declared env changes value, cache will miss
/// 4. If a pass-through env changes, cache will still hit
/// 4. If an untracked env changes, cache will still hit
///
/// For built-in tasks (lint, build, etc):
/// - The resolver provides envs which become part of the fingerprint
Expand Down
Loading
Loading