Skip to content

Commit 62d6231

Browse files
authored
fix(plan): add shell arguments for shell fallback execution (#100)
1 parent 0492491 commit 62d6231

10 files changed

Lines changed: 174 additions & 7 deletions

File tree

crates/vite_task_bin/tests/e2e_snapshots/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn run_case(tmpdir: &AbsolutePath, fixture_path: &Path, filter: Option<&str>) {
8989
return;
9090
}
9191
}
92-
92+
println!("{}", fixture_name);
9393
// Configure insta to write snapshots to fixture directory
9494
let mut settings = insta::Settings::clone_current();
9595
settings.set_snapshot_path(fixture_path.join("snapshots"));

crates/vite_task_plan/src/plan.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ async fn plan_task_as_execution_node(
239239
}
240240
});
241241

242+
static SHELL_ARGS: &[&str] =
243+
if cfg!(target_os = "windows") { &["/d", "/s", "/c"] } else { &["-c"] };
244+
242245
let mut script = Str::from(command_str);
243246
for arg in context.extra_args().iter() {
244247
script.push(' ');
@@ -266,7 +269,7 @@ async fn plan_task_as_execution_node(
266269
&task_node.resolved_config.resolved_options,
267270
context.envs(),
268271
Arc::clone(&*SHELL_PROGRAM_PATH),
269-
Arc::new([script]),
272+
Arc::from_iter(SHELL_ARGS.iter().map(|s| Str::from(*s)).chain(std::iter::once(script))),
270273
)
271274
.with_plan_context(&context)?;
272275
items.push(ExecutionItem {

crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-sharing/snapshots/task graph.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
source: crates/vite_task_bin/tests/test_snapshots/main.rs
2+
source: crates/vite_task_plan/tests/plan_snapshots/main.rs
33
expression: task_graph_json
4-
input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/cache-sharing
4+
input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/cache-sharing
55
---
66
[
77
{

crates/vite_task_plan/tests/plan_snapshots/fixtures/comprehensive-task-graph/snapshots/task graph.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
source: crates/vite_task_bin/tests/test_snapshots/main.rs
2+
source: crates/vite_task_plan/tests/plan_snapshots/main.rs
33
expression: task_graph_json
4-
input_file: crates/vite_task_bin/tests/test_snapshots/fixtures/comprehensive-task-graph
4+
input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/comprehensive-task-graph
55
---
66
[
77
{
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"scripts": {
3+
"pipe-test": "echo hello | node -e \"process.stdin.pipe(process.stdout)\""
4+
}
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[[plan]]
2+
name = "shell fallback for pipe command"
3+
args = ["run", "pipe-test"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
source: crates/vite_task_plan/tests/plan_snapshots/main.rs
3+
expression: "&plan_json"
4+
input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback
5+
---
6+
{
7+
"root_node": {
8+
"Expanded": [
9+
{
10+
"key": [
11+
"<workspace>/",
12+
"pipe-test"
13+
],
14+
"node": {
15+
"task_display": {
16+
"package_name": "",
17+
"task_name": "pipe-test",
18+
"package_path": "<workspace>/"
19+
},
20+
"items": [
21+
{
22+
"execution_item_display": {
23+
"task_display": {
24+
"package_name": "",
25+
"task_name": "pipe-test",
26+
"package_path": "<workspace>/"
27+
},
28+
"command": "echo hello | node -e \"process.stdin.pipe(process.stdout)\"",
29+
"and_item_index": null,
30+
"cwd": "<workspace>/"
31+
},
32+
"kind": {
33+
"Leaf": {
34+
"Spawn": {
35+
"cache_metadata": {
36+
"spawn_fingerprint": {
37+
"cwd": "",
38+
"program_fingerprint": {
39+
"OutsideWorkspace": {
40+
"program_name": "<os_shell_name>"
41+
}
42+
},
43+
"args": [
44+
"<os_shell_args>",
45+
"echo hello | node -e \"process.stdin.pipe(process.stdout)\""
46+
],
47+
"env_fingerprints": {
48+
"fingerprinted_envs": {},
49+
"pass_through_env_config": [
50+
"<default pass-through envs>"
51+
]
52+
},
53+
"fingerprint_ignores": null
54+
},
55+
"execution_cache_key": {
56+
"kind": {
57+
"UserTask": {
58+
"task_name": "pipe-test",
59+
"and_item_index": 0,
60+
"extra_args": []
61+
}
62+
},
63+
"origin_path": ""
64+
}
65+
},
66+
"spawn_command": {
67+
"program_path": "<os_shell_path>",
68+
"args": [
69+
"<os_shell_args>",
70+
"echo hello | node -e \"process.stdin.pipe(process.stdout)\""
71+
],
72+
"all_envs": {
73+
"NO_COLOR": "1",
74+
"PATH": "<workspace>/node_modules/.bin:<tools>/node_modules/.bin"
75+
},
76+
"cwd": "<workspace>/"
77+
}
78+
}
79+
}
80+
}
81+
}
82+
]
83+
},
84+
"neighbors": []
85+
}
86+
]
87+
}
88+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
source: crates/vite_task_plan/tests/plan_snapshots/main.rs
3+
assertion_line: 106
4+
expression: task_graph_json
5+
input_file: crates/vite_task_plan/tests/plan_snapshots/fixtures/shell-fallback
6+
---
7+
[
8+
{
9+
"key": [
10+
"<workspace>/",
11+
"pipe-test"
12+
],
13+
"node": {
14+
"task_display": {
15+
"package_name": "",
16+
"task_name": "pipe-test",
17+
"package_path": "<workspace>/"
18+
},
19+
"resolved_config": {
20+
"command": "echo hello | node -e \"process.stdin.pipe(process.stdout)\"",
21+
"resolved_options": {
22+
"cwd": "<workspace>/",
23+
"cache_config": {
24+
"env_config": {
25+
"fingerprinted_envs": [],
26+
"pass_through_envs": [
27+
"<default pass-through envs>"
28+
]
29+
}
30+
}
31+
}
32+
}
33+
},
34+
"neighbors": []
35+
}
36+
]

crates/vite_task_plan/tests/plan_snapshots/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn run_case(runtime: &Runtime, tmpdir: &AbsolutePath, fixture_path: &Path, filte
3737
return;
3838
}
3939
}
40-
40+
println!("{}", fixture_name);
4141
// Configure insta to write snapshots to fixture directory
4242
let mut settings = insta::Settings::clone_current();
4343
settings.set_snapshot_path(fixture_path.join("snapshots"));

crates/vite_task_plan/tests/plan_snapshots/redact.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub fn redact_snapshot(value: &impl Serialize, workspace_root: &str) -> serde_js
9494
});
9595

9696
// Normalize Windows program names and paths by stripping common extensions for cross-platform consistency
97+
// This must happen BEFORE shell redaction so that "cmd.exe" becomes "cmd" before comparison
9798
visit_json(&mut json_value, &mut |v| {
9899
let serde_json::Value::Object(map) = v else {
99100
return;
@@ -108,6 +109,37 @@ pub fn redact_snapshot(value: &impl Serialize, workspace_root: &str) -> serde_js
108109
}
109110
});
110111

112+
// Redact shell program and arguments for cross-platform consistency
113+
// Note: os_shell_path still includes .exe because we compare against program_path before extension stripping
114+
let os_shell_path = if cfg!(windows) { "C:\\Windows\\System32\\cmd" } else { "/bin/sh" };
115+
let os_shell_name = if cfg!(windows) { "cmd" } else { "sh" };
116+
let os_shell_args: &[&str] = if cfg!(windows) { &["/d", "/s", "/c"] } else { &["-c"] };
117+
visit_json(&mut json_value, &mut |v| {
118+
if let serde_json::Value::String(s) = v {
119+
// Use case-insensitive comparison on Windows since path casing can vary
120+
let matches_shell_path = if cfg!(windows) {
121+
s.eq_ignore_ascii_case(os_shell_path)
122+
} else {
123+
s == os_shell_path
124+
};
125+
if matches_shell_path {
126+
*s = "<os_shell_path>".to_string();
127+
} else if s == os_shell_name {
128+
*s = "<os_shell_name>".to_string();
129+
}
130+
} else if let serde_json::Value::Array(array) = v {
131+
// Check if the beginning of the array matches the shell args
132+
for (n, arg) in os_shell_args.iter().enumerate() {
133+
if !matches!(array.get(n), Some(serde_json::Value::String(s)) if s == *arg) {
134+
return;
135+
}
136+
}
137+
// Redact the shell args
138+
array.drain(0..os_shell_args.len());
139+
array.insert(0, serde_json::Value::String("<os_shell_args>".to_string()));
140+
}
141+
});
142+
111143
visit_json(&mut json_value, &mut |v| {
112144
let serde_json::Value::Array(array) = v else {
113145
return;

0 commit comments

Comments
 (0)