diff --git a/crates/vite_task/src/session/event.rs b/crates/vite_task/src/session/event.rs index 4a6c2ba6..85d07584 100644 --- a/crates/vite_task/src/session/event.rs +++ b/crates/vite_task/src/session/event.rs @@ -19,6 +19,24 @@ pub enum CacheDisabledReason { CycleDetected, } +#[derive(Debug)] +pub enum CacheNotUpdatedReason { + /// Cache was hit - task was replayed from cache, no update needed + CacheHit, + /// Caching was disabled for this task + CacheDisabled, + /// Execution exited with non-zero status + NonZeroExitStatus, +} + +#[derive(Debug)] +pub enum CacheUpdateStatus { + /// Cache was successfully updated with new fingerprint and outputs + Updated, + /// Cache was not updated (with reason) + NotUpdated(CacheNotUpdatedReason), +} + #[derive(Debug)] pub enum CacheStatus { Disabled(CacheDisabledReason), @@ -50,5 +68,5 @@ pub enum ExecutionEventKind { Start { display: Option, cache_status: CacheStatus }, Output { kind: OutputKind, content: BString }, Error { message: String }, - Finish { status: Option }, + Finish { status: Option, cache_update_status: CacheUpdateStatus }, } diff --git a/crates/vite_task/src/session/execute/mod.rs b/crates/vite_task/src/session/execute/mod.rs index d002333f..b62c01e4 100644 --- a/crates/vite_task/src/session/execute/mod.rs +++ b/crates/vite_task/src/session/execute/mod.rs @@ -18,8 +18,8 @@ use self::{ use super::{ cache::{CommandCacheValue, ExecutionCache}, event::{ - CacheDisabledReason, CacheStatus, ExecutionEvent, ExecutionEventKind, ExecutionId, - ExecutionItemDisplay, OutputKind, + CacheDisabledReason, CacheNotUpdatedReason, CacheStatus, CacheUpdateStatus, ExecutionEvent, + ExecutionEventKind, ExecutionId, ExecutionItemDisplay, OutputKind, }, reporter::{ExitStatus, Reporter}, }; @@ -153,10 +153,15 @@ impl ExecutionContext<'_> { }, }); - // Emit Finish WITHOUT cache_status (already in Start event) + // Emit Finish with CacheDisabled status (in-process executions don't cache) self.event_handler.handle_event(ExecutionEvent { execution_id, - kind: ExecutionEventKind::Finish { status: Some(0) }, + kind: ExecutionEventKind::Finish { + status: Some(0), + cache_update_status: CacheUpdateStatus::NotUpdated( + CacheNotUpdatedReason::CacheDisabled, + ), + }, }); } LeafExecutionKind::Spawn(spawn_execution) => { @@ -228,10 +233,15 @@ impl ExecutionContext<'_> { }, }); } - // Emit Finish without cache_status (status already in Start event) + // Emit Finish with CacheHit status (no cache update needed) self.event_handler.handle_event(ExecutionEvent { execution_id, - kind: ExecutionEventKind::Finish { status: Some(0) }, + kind: ExecutionEventKind::Finish { + status: Some(0), + cache_update_status: CacheUpdateStatus::NotUpdated( + CacheNotUpdatedReason::CacheHit, + ), + }, }); return Ok(()); } @@ -276,51 +286,62 @@ impl ExecutionContext<'_> { } }; - // 5. Update cache if successful - // Only update cache if: (a) tracking was enabled, and (b) execution succeeded - if let Some((track_result, cache_metadata)) = track_result_with_cache_metadata - && result.exit_status.success() + // 5. Update cache if successful and determine cache update status + let cache_update_status = if let Some((track_result, cache_metadata)) = + track_result_with_cache_metadata { - let fingerprint_ignores = - cache_metadata.spawn_fingerprint.fingerprint_ignores().map(|v| v.as_slice()); - match PostRunFingerprint::create( - &track_result.path_reads, - &*self.cache_base_path, - fingerprint_ignores, - ) { - Ok(post_run_fingerprint) => { - let cache_value = CommandCacheValue { - post_run_fingerprint, - std_outputs: track_result.std_outputs.clone().into(), - duration: result.duration, - }; - if let Err(err) = self.cache.update(cache_metadata, cache_value).await { + if result.exit_status.success() { + // Execution succeeded, attempt cache update + let fingerprint_ignores = + cache_metadata.spawn_fingerprint.fingerprint_ignores().map(|v| v.as_slice()); + match PostRunFingerprint::create( + &track_result.path_reads, + &*self.cache_base_path, + fingerprint_ignores, + ) { + Ok(post_run_fingerprint) => { + let cache_value = CommandCacheValue { + post_run_fingerprint, + std_outputs: track_result.std_outputs.clone().into(), + duration: result.duration, + }; + if let Err(err) = self.cache.update(cache_metadata, cache_value).await { + self.event_handler.handle_event(ExecutionEvent { + execution_id, + kind: ExecutionEventKind::Error { + message: format!("Failed to update cache: {err}"), + }, + }); + return Err(ExecutionAborted); + } + CacheUpdateStatus::Updated + } + Err(err) => { self.event_handler.handle_event(ExecutionEvent { execution_id, kind: ExecutionEventKind::Error { - message: format!("Failed to update cache: {err}"), + message: format!("Failed to create post-run fingerprint: {err}"), }, }); return Err(ExecutionAborted); } } - Err(err) => { - self.event_handler.handle_event(ExecutionEvent { - execution_id, - kind: ExecutionEventKind::Error { - message: format!("Failed to create post-run fingerprint: {err}"), - }, - }); - return Err(ExecutionAborted); - } + } else { + // Execution failed with non-zero exit status, don't update cache + CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::NonZeroExitStatus) } - } + } else { + // Caching was disabled for this task + CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled) + }; - // 6. Emit finish WITHOUT cache_status - // Cache status was already emitted in Start event + // 6. Emit finish with cache_update_status self.event_handler.handle_event(ExecutionEvent { execution_id, - kind: ExecutionEventKind::Finish { status: result.exit_status.code() }, + kind: ExecutionEventKind::Finish { + status: result.exit_status.code(), + cache_update_status, + }, }); Ok(()) diff --git a/crates/vite_task/src/session/reporter.rs b/crates/vite_task/src/session/reporter.rs index d2ebe3b0..1f9f84ac 100644 --- a/crates/vite_task/src/session/reporter.rs +++ b/crates/vite_task/src/session/reporter.rs @@ -489,7 +489,7 @@ impl Reporter for LabeledReporter { ExecutionEventKind::Error { message } => { self.handle_error(event.execution_id, message); } - ExecutionEventKind::Finish { status } => { + ExecutionEventKind::Finish { status, cache_update_status: _ } => { self.handle_finish(event.execution_id, status); } } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/bad.js b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/bad.js new file mode 100644 index 00000000..eab74692 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/bad.js @@ -0,0 +1 @@ +debugger; diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json new file mode 100644 index 00000000..cd8fc56e --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/package.json @@ -0,0 +1,3 @@ +{ + "name": "builtin-non-zero-exit-test" +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml new file mode 100644 index 00000000..28b3fb46 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots.toml @@ -0,0 +1,6 @@ +[[e2e]] +name = "builtin command with non-zero exit does not show cache not updated" +steps = [ + "vite lint -D no-debugger", + "vite lint -D no-debugger", +] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap new file mode 100644 index 00000000..605f153a --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit/snapshots/builtin command with non-zero exit does not show cache not updated.snap @@ -0,0 +1,29 @@ +--- +source: crates/vite_task_bin/tests/e2e_snapshots/main.rs +expression: e2e_outputs +input_file: crates/vite_task_bin/tests/e2e_snapshots/fixtures/builtin-non-zero-exit +--- +[1]> vite lint -D no-debugger + + x eslint(no-debugger): `debugger` statement is not allowed + ,-[bad.js:1:1] + 1 | debugger; + : ^^^^^^^^^ + `---- + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with 90 rules using threads. + + +[1]> vite lint -D no-debugger + + x eslint(no-debugger): `debugger` statement is not allowed + ,-[bad.js:1:1] + 1 | debugger; + : ^^^^^^^^^ + `---- + help: Remove the debugger statement + +Found 0 warnings and 1 error. +Finished in on 1 file with 90 rules using threads.