Skip to content

Commit b8d7023

Browse files
authored
Merge branch 'main' into fix/default-untracked-env-patterns
2 parents aa566cb + 4a5c9bc commit b8d7023

84 files changed

Lines changed: 260 additions & 340 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/renovate.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
3-
"extends": ["github>Boshen/renovate"]
3+
"extends": ["github>Boshen/renovate"],
4+
"ignorePaths": ["**/fixtures/**"]
45
}

CLAUDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ just lint-windows # Cross-clippy for Windows (requires cargo-xwin)
3636
just doc # Documentation generation
3737
```
3838

39+
If `gt` (Graphite CLI) is available in PATH, use it instead of `gh` to create pull requests.
40+
3941
## Tests
4042

4143
```bash
@@ -48,6 +50,10 @@ INSTA_UPDATE=always cargo test # Update snapshots
4850

4951
Integration tests (e2e, plan, fspy) require `pnpm install` in `packages/tools` first. You don't need `pnpm install` in test fixture directories.
5052

53+
### Test Reliability
54+
55+
The test suite has no known pre-existing failures or flaky tests. If a test fails during your changes, treat it as a real regression caused by your work. Fix the root cause properly — do not skip, ignore, or work around failing tests.
56+
5157
### Test Fixtures
5258

5359
- **Plan**: `crates/vite_task_plan/tests/plan_snapshots/fixtures/` — quicker, sufficient for testing task graph, resolved configs, program paths, cwd, and env vars

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ nursery = { level = "warn", priority = -1 }
3434
cargo = { level = "warn", priority = -1 }
3535
cargo_common_metadata = "allow"
3636
multiple_crate_versions = "allow"
37+
# The task scheduling workflow is a single-threaded async task, so Send bounds are unnecessary.
38+
future_not_send = "allow"
3739

3840
[workspace.dependencies]
3941
allocator-api2 = { version = "0.2.21", default-features = false, features = ["alloc", "std"] }

crates/fspy/src/arena.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![expect(
2-
clippy::future_not_send,
3-
reason = "ouroboros generates async builder methods that cannot satisfy Send bounds"
4-
)]
5-
61
use allocator_api2::vec::Vec;
72
use bumpalo::Bump;
83

crates/fspy/src/ipc.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![expect(
2-
clippy::future_not_send,
3-
reason = "ouroboros generates async builder methods that cannot satisfy Send bounds"
4-
)]
5-
61
use std::io;
72

83
use bincode::borrow_decode_from_slice;

crates/vite_task/src/session/cache/display.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ pub fn format_cache_status_inline(cache_status: &CacheStatus) -> Option<Str> {
145145
match cache_status {
146146
CacheStatus::Hit { .. } => {
147147
// Show "cache hit" indicator when replaying from cache
148-
Some(Str::from(" cache hit, replaying"))
148+
Some(Str::from(" cache hit, replaying"))
149149
}
150150
CacheStatus::Miss(CacheMiss::NotFound) => {
151151
// No inline message for "not found" case - just show command
@@ -176,10 +176,10 @@ pub fn format_cache_status_inline(cache_status: &CacheStatus) -> Option<Str> {
176176
FingerprintMismatch::InputConfig => "input configuration changed",
177177
FingerprintMismatch::InputChanged { kind, path } => {
178178
let desc = format_input_change_str(*kind, path.as_str());
179-
return Some(vite_str::format!(" cache miss: {desc}, executing"));
179+
return Some(vite_str::format!(" cache miss: {desc}, executing"));
180180
}
181181
};
182-
Some(vite_str::format!(" cache miss: {reason}, executing"))
182+
Some(vite_str::format!(" cache miss: {reason}, executing"))
183183
}
184184
CacheStatus::Disabled(_) => Some(Str::from("⊘ cache disabled")),
185185
}

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,6 @@ fn detect_globbed_input_change(
323323

324324
// Basic database operations
325325
impl ExecutionCache {
326-
#[expect(
327-
clippy::future_not_send,
328-
reason = "tokio MutexGuard is !Send but this future only runs on a single-threaded runtime"
329-
)]
330326
#[expect(
331327
clippy::significant_drop_tightening,
332328
reason = "lock guard cannot be dropped earlier because prepared statement borrows connection"
@@ -370,10 +366,6 @@ impl ExecutionCache {
370366
self.get_key_by_value("task_fingerprints", execution_cache_key).await
371367
}
372368

373-
#[expect(
374-
clippy::future_not_send,
375-
reason = "tokio MutexGuard is !Send but this future only runs on a single-threaded runtime"
376-
)]
377369
#[expect(
378370
clippy::significant_drop_tightening,
379371
reason = "lock guard must be held while executing the prepared statement"

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

Lines changed: 54 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ pub mod fingerprint;
22
pub mod glob_inputs;
33
pub mod spawn;
44

5-
use std::{collections::BTreeMap, process::Stdio, sync::Arc};
5+
use std::{collections::BTreeMap, io::Write as _, process::Stdio, sync::Arc};
66

77
use futures_util::FutureExt;
8-
use tokio::io::AsyncWriteExt as _;
98
use vite_path::AbsolutePath;
109
use vite_task_plan::{
1110
ExecutionGraph, ExecutionItemDisplay, ExecutionItemKind, LeafExecutionKind, SpawnCommand,
@@ -78,7 +77,6 @@ impl ExecutionContext<'_> {
7877
///
7978
/// Returns `true` if all tasks succeeded, `false` if any task failed.
8079
#[tracing::instrument(level = "debug", skip_all)]
81-
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
8280
async fn execute_expanded_graph(
8381
&mut self,
8482
graph: &ExecutionGraph,
@@ -132,7 +130,6 @@ impl ExecutionContext<'_> {
132130
///
133131
/// Returns `true` if the execution failed (non-zero exit or infrastructure error).
134132
#[tracing::instrument(level = "debug", skip_all)]
135-
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
136133
async fn execute_leaf(
137134
&mut self,
138135
display: &ExecutionItemDisplay,
@@ -146,21 +143,18 @@ impl ExecutionContext<'_> {
146143
LeafExecutionKind::InProcess(in_process_execution) => {
147144
// In-process (built-in) commands: caching is disabled, execute synchronously
148145
let mut stdio_config = leaf_reporter
149-
.start(CacheStatus::Disabled(CacheDisabledReason::InProcessExecution))
150-
.await;
146+
.start(CacheStatus::Disabled(CacheDisabledReason::InProcessExecution));
151147

152148
let execution_output = in_process_execution.execute();
153149
// Write output to the stdout writer from StdioConfig
154-
let _ = stdio_config.stdout_writer.write_all(&execution_output.stdout).await;
155-
let _ = stdio_config.stdout_writer.flush().await;
150+
let _ = stdio_config.stdout_writer.write_all(&execution_output.stdout);
151+
let _ = stdio_config.stdout_writer.flush();
156152

157-
leaf_reporter
158-
.finish(
159-
None,
160-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
161-
None,
162-
)
163-
.await;
153+
leaf_reporter.finish(
154+
None,
155+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
156+
None,
157+
);
164158
false
165159
}
166160
LeafExecutionKind::Spawn(spawn_execution) => {
@@ -196,7 +190,6 @@ impl ExecutionContext<'_> {
196190
/// Errors (cache lookup failure, spawn failure, cache update failure) are reported
197191
/// through `leaf_reporter.finish()` and do not abort the caller.
198192
#[tracing::instrument(level = "debug", skip_all)]
199-
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
200193
#[expect(
201194
clippy::too_many_lines,
202195
reason = "sequential cache check, execute, and update steps are clearer in one function"
@@ -223,13 +216,11 @@ pub async fn execute_spawn(
223216
) {
224217
Ok(inputs) => inputs,
225218
Err(err) => {
226-
leaf_reporter
227-
.finish(
228-
None,
229-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
230-
Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }),
231-
)
232-
.await;
219+
leaf_reporter.finish(
220+
None,
221+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
222+
Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }),
223+
);
233224
return SpawnOutcome::Failed;
234225
}
235226
};
@@ -250,13 +241,11 @@ pub async fn execute_spawn(
250241
Err(err) => {
251242
// Cache lookup error — report through finish.
252243
// Note: start() is NOT called because we don't have a valid cache status.
253-
leaf_reporter
254-
.finish(
255-
None,
256-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
257-
Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }),
258-
)
259-
.await;
244+
leaf_reporter.finish(
245+
None,
246+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
247+
Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }),
248+
);
260249
return SpawnOutcome::Failed;
261250
}
262251
}
@@ -266,23 +255,25 @@ pub async fn execute_spawn(
266255
};
267256

268257
// 2. Report execution start with the determined cache status.
269-
// Returns StdioConfig with the reporter's suggestion and async writers.
270-
let mut stdio_config = leaf_reporter.start(cache_status).await;
258+
// Returns StdioConfig with the reporter's suggestion and writers.
259+
let mut stdio_config = leaf_reporter.start(cache_status);
271260

272261
// 3. If cache hit, replay outputs via the StdioConfig writers and finish early.
273262
// No need to actually execute the command — just replay what was cached.
274263
if let Some(cached) = cached_value {
275264
for output in cached.std_outputs.iter() {
276-
let writer: &mut (dyn tokio::io::AsyncWrite + Unpin) = match output.kind {
265+
let writer: &mut dyn std::io::Write = match output.kind {
277266
spawn::OutputKind::StdOut => &mut stdio_config.stdout_writer,
278267
spawn::OutputKind::StdErr => &mut stdio_config.stderr_writer,
279268
};
280-
let _ = writer.write_all(&output.content).await;
281-
let _ = writer.flush().await;
269+
let _ = writer.write_all(&output.content);
270+
let _ = writer.flush();
282271
}
283-
leaf_reporter
284-
.finish(None, CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheHit), None)
285-
.await;
272+
leaf_reporter.finish(
273+
None,
274+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheHit),
275+
None,
276+
);
286277
return SpawnOutcome::CacheHit;
287278
}
288279

@@ -296,29 +287,25 @@ pub async fn execute_spawn(
296287
if use_inherited {
297288
// Inherited mode: all three stdio FDs (stdin, stdout, stderr) are inherited
298289
// from the parent process. No fspy tracking, no output capture.
299-
// Drop the StdioConfig writers before spawning to avoid holding tokio::io::Stdout
290+
// Drop the StdioConfig writers before spawning to avoid holding std::io::Stdout
300291
// while the child also writes to the same FD.
301292
drop(stdio_config);
302293

303294
match spawn_inherited(&spawn_execution.spawn_command).await {
304295
Ok(result) => {
305-
leaf_reporter
306-
.finish(
307-
Some(result.exit_status),
308-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
309-
None,
310-
)
311-
.await;
296+
leaf_reporter.finish(
297+
Some(result.exit_status),
298+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
299+
None,
300+
);
312301
return SpawnOutcome::Spawned(result.exit_status);
313302
}
314303
Err(err) => {
315-
leaf_reporter
316-
.finish(
317-
None,
318-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
319-
Some(ExecutionError::Spawn(err)),
320-
)
321-
.await;
304+
leaf_reporter.finish(
305+
None,
306+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
307+
Some(ExecutionError::Spawn(err)),
308+
);
322309
return SpawnOutcome::Failed;
323310
}
324311
}
@@ -349,13 +336,11 @@ pub async fn execute_spawn(
349336
{
350337
Ok(negs) => negs,
351338
Err(err) => {
352-
leaf_reporter
353-
.finish(
354-
None,
355-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
356-
Some(ExecutionError::PostRunFingerprint(err)),
357-
)
358-
.await;
339+
leaf_reporter.finish(
340+
None,
341+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
342+
Some(ExecutionError::PostRunFingerprint(err)),
343+
);
359344
return SpawnOutcome::Failed;
360345
}
361346
}
@@ -370,8 +355,8 @@ pub async fn execute_spawn(
370355
let result = match spawn_with_tracking(
371356
&spawn_execution.spawn_command,
372357
cache_base_path,
373-
&mut stdio_config.stdout_writer,
374-
&mut stdio_config.stderr_writer,
358+
&mut *stdio_config.stdout_writer,
359+
&mut *stdio_config.stderr_writer,
375360
std_outputs.as_mut(),
376361
path_accesses.as_mut(),
377362
&resolved_negatives,
@@ -380,13 +365,11 @@ pub async fn execute_spawn(
380365
{
381366
Ok(result) => result,
382367
Err(err) => {
383-
leaf_reporter
384-
.finish(
385-
None,
386-
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
387-
Some(ExecutionError::Spawn(err)),
388-
)
389-
.await;
368+
leaf_reporter.finish(
369+
None,
370+
CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled),
371+
Some(ExecutionError::Spawn(err)),
372+
);
390373
return SpawnOutcome::Failed;
391374
}
392375
};
@@ -459,7 +442,7 @@ pub async fn execute_spawn(
459442
// 7. Finish the leaf execution with the result and optional cache error.
460443
// Cache update/fingerprint failures are reported but do not affect the outcome —
461444
// the process ran, so we return its actual exit status.
462-
leaf_reporter.finish(Some(result.exit_status), cache_update_status, cache_error).await;
445+
leaf_reporter.finish(Some(result.exit_status), cache_update_status, cache_error);
463446

464447
SpawnOutcome::Spawned(result.exit_status)
465448
}
@@ -531,7 +514,6 @@ impl Session<'_> {
531514
/// Returns `Err(ExitStatus)` to indicate the caller should exit with the given status code.
532515
/// Returns `Ok(())` when all tasks succeeded.
533516
#[tracing::instrument(level = "debug", skip_all)]
534-
#[expect(clippy::future_not_send, reason = "uses !Send types internally")]
535517
pub(crate) async fn execute_graph(
536518
&self,
537519
execution_graph: ExecutionGraph,
@@ -564,6 +546,6 @@ impl Session<'_> {
564546

565547
// Leaf-level errors and non-zero exit statuses are tracked internally
566548
// by the reporter.
567-
reporter.finish().await
549+
reporter.finish()
568550
}
569551
}

0 commit comments

Comments
 (0)