Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

- **Fixed** In a script like `cd ui && vpr hello`, where `ui` is a separate project outside the workspace, the nested `vp run` now runs `ui`'s `hello` instead of the workspace root's ([vite-plus#3129](https://github.com/voidzero-dev/vite-plus/issues/3129), [#867](https://github.com/voidzero-dev/vite-task/pull/867)).
- **Fixed** A mistyped or misplaced setting in a task now gets an error that names it. For example, `cache: { outputs: [...] }` now fails with an `unknown field` error that names `outputs` and lists the accepted settings, instead of `data did not match any variant of untagged enum UserTaskDefinition` ([vite-plus#2635](https://github.com/voidzero-dev/vite-plus/issues/2635), [#815](https://github.com/voidzero-dev/vite-task/pull/815)).
- **Fixed** In cached tasks on Linux and macOS, programs that pass `-1` as a directory descriptor to calls such as `openat` no longer abort. For example, `ps -p <pid>` from procps-ng 4.0.6 or later aborted when the process had already exited ([#862](https://github.com/voidzero-dev/vite-task/pull/862)).
- **Fixed** `vp run` no longer panics before running a script that contains non-ASCII characters, such as `echo こんにちは`, and no longer cuts off such commands where it displays them ([#861](https://github.com/voidzero-dev/vite-task/pull/861)).
Expand Down
2 changes: 1 addition & 1 deletion crates/vt_plan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ vt_powershell = { workspace = true }
vt_shell = { workspace = true }
vt_str = { workspace = true }
vt_graph = { workspace = true }
vt_workspace = { workspace = true }
which = { workspace = true }

[dev-dependencies]
Expand All @@ -46,7 +47,6 @@ tokio = { workspace = true, features = ["rt", "macros"] }
toml = { workspace = true }
vt = { workspace = true }
vt_bin = { workspace = true }
vt_workspace = { workspace = true }

[[test]]
name = "plan_snapshots"
Expand Down
26 changes: 25 additions & 1 deletion crates/vt_plan/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use vt_graph::{
use vt_path::{AbsolutePath, AbsolutePathBuf, RelativePathBuf, relative::InvalidPathDataError};
use vt_shell::try_parse_as_and_list;
use vt_str::Str;
use vt_workspace::find_workspace_root;

use crate::{
ExecutionItem, ExecutionItemDisplay, ExecutionItemKind, LeafExecutionKind, PlanContext,
Expand All @@ -45,6 +46,13 @@ use crate::{
resolve_cache_with_override,
};

/// Whether `cwd` belongs to a workspace other than the one rooted at `workspace_path`.
///
/// Directories without any discoverable workspace root are treated as part of `workspace_path`.
fn is_in_other_workspace(cwd: &AbsolutePath, workspace_path: &AbsolutePath) -> bool {
find_workspace_root(cwd).is_ok_and(|(root, _)| &*root.path != workspace_path)
}

/// Locate the executable path for a given program name in the provided envs and cwd.
fn which(
program: &Arc<OsStr>,
Expand Down Expand Up @@ -229,7 +237,10 @@ async fn plan_task_as_execution_node(
envs,
cwd: Arc::clone(&cwd),
};
let plan_request =
// The command as written, before the callback rewrites it (e.g. `vpr` -> `vt run`).
let original_program = script_command.program.clone();
let original_args = Arc::clone(&script_command.args);
let mut plan_request =
context.callbacks().get_plan_request(&mut script_command).await.map_err(
|error| Error::ParsePlanRequest {
program: script_command.program.clone(),
Expand All @@ -239,6 +250,19 @@ async fn plan_task_as_execution_node(
},
)?;

// After a `cd` into a project that is not part of this workspace (it has its own
// workspace root), this workspace's task graph cannot resolve the nested run:
// it would fall back to the enclosing package. Run the command as written
// instead, so it discovers its own workspace like it does in a shell.
if matches!(plan_request, Some(PlanRequest::Query(_)))
&& *cwd != *task_node.resolved_config.resolved_options.cwd
&& is_in_other_workspace(&cwd, context.workspace_path())
{
plan_request = None;
script_command.program = original_program;
script_command.args = original_args;
}

let execution_item_kind: ExecutionItemKind = match plan_request {
// Expand task query like `vp run -r build`
Some(PlanRequest::Query(query_plan_request)) => {
Expand Down
1 change: 1 addition & 0 deletions crates/vt_plan/tests/plan_snapshots/fake-bin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ never actually executed during plan tests — only its resolved path appears in

- `vtt` — Unix stub (executable shell script)
- `vtt.cmd` — Windows stub (looked up via `PATHEXT`)
- `vpr`, `vpr.cmd` — the same stubs for `vpr`, which is spawned as written when a script `cd`s into a project outside the workspace
1 change: 1 addition & 0 deletions crates/vt_plan/tests/plan_snapshots/fake-bin/vpr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#\!/bin/sh
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "root",
"private": true,
"scripts": {
"hello": "echo ROOT",
"plain": "cd ui && vpr hello"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Tests that after `cd` into a nested project outside this workspace (no workspace file lists it),
# a nested `vpr` runs as written instead of planning the enclosing package's task.

[fields]
key = true
execution_item_display = { command = true, cwd = true }
spawn_command = { program_path = true, args = true, cwd = true }

[[plan]]
name = "cd_into_other_project_then_vpr"
args = ["run", "plain"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// run plain
{
"graph": [
{
"key": [
"<workspace>/",
"plain"
],
"node": {
"items": [
{
"execution_item_display": {
"command": "vpr hello",
"cwd": "<workspace>/ui"
},
"kind": {
"Leaf": {
"Spawn": {
"spawn_command": {
"program_path": "<tools>/vpr",
"args": [
"hello"
],
"cwd": "<workspace>/ui"
}
}
}
}
}
]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# task graph

```mermaid
flowchart TD
task_0["<workspace>/#hello"]
task_1["<workspace>/#plain"]
```

## `<workspace>/#hello`

```json
{
"task_display": {
"package_name": "root",
"task_name": "hello",
"package_path": "<workspace>/"
},
"resolved_config": {
"commands": [
"echo ROOT"
],
"resolved_options": {
"cwd": "<workspace>/",
"cache_config": {
"remote_cache_allowed": true,
"env_config": {
"fingerprinted_envs": [],
"untracked_env": [
"<default untracked envs>"
]
},
"input_config": {
"includes_auto": true,
"positive_globs": [],
"negative_globs": []
},
"output_config": {
"includes_auto": true,
"positive_globs": [],
"negative_globs": []
}
}
}
},
"source": "PackageJsonScript"
}
```

## `<workspace>/#plain`

```json
{
"task_display": {
"package_name": "root",
"task_name": "plain",
"package_path": "<workspace>/"
},
"resolved_config": {
"commands": [
"cd ui && vpr hello"
],
"resolved_options": {
"cwd": "<workspace>/",
"cache_config": {
"remote_cache_allowed": true,
"env_config": {
"fingerprinted_envs": [],
"untracked_env": [
"<default untracked envs>"
]
},
"input_config": {
"includes_auto": true,
"positive_globs": [],
"negative_globs": []
},
"output_config": {
"includes_auto": true,
"positive_globs": [],
"negative_globs": []
}
}
}
},
"source": "PackageJsonScript"
}
```

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"ui","private":true,"scripts":{"hello":"echo UI"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cache": true
}