Skip to content

Commit a766ca1

Browse files
committed
fix(plan): add shell arguments for shell fallback execution
1 parent 006de3f commit a766ca1

6 files changed

Lines changed: 178 additions & 1 deletion

File tree

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,47 @@ pub fn redact_snapshot(value: &impl Serialize, workspace_root: &str) -> serde_js
108108
}
109109
});
110110

111+
// Normalize shell program paths and arguments for cross-platform consistency
112+
visit_json(&mut json_value, &mut |v| {
113+
let serde_json::Value::Object(map) = v else {
114+
return;
115+
};
116+
117+
// Check if program_path is a shell
118+
let is_shell =
119+
if let Some(serde_json::Value::String(program_path)) = map.get("program_path") {
120+
let lower = program_path.to_lowercase();
121+
if cfg!(windows) {
122+
lower.ends_with("cmd") || lower.ends_with("cmd.exe")
123+
} else {
124+
program_path == "/bin/sh"
125+
}
126+
} else {
127+
false
128+
};
129+
130+
if !is_shell {
131+
return;
132+
}
133+
134+
// Redact shell program path
135+
if let Some(serde_json::Value::String(program_path)) = map.get_mut("program_path") {
136+
*program_path = "<shell>".to_string();
137+
}
138+
139+
// Redact shell arguments
140+
if let Some(serde_json::Value::Array(args)) = map.get_mut("args") {
141+
let shell_args: &[&str] = if cfg!(windows) { &["/d", "/s", "/c"] } else { &["-c"] };
142+
for arg in args.iter_mut() {
143+
if let serde_json::Value::String(s) = arg {
144+
if shell_args.contains(&s.as_str()) {
145+
*s = "<shell-arg>".to_string();
146+
}
147+
}
148+
}
149+
}
150+
});
151+
111152
visit_json(&mut json_value, &mut |v| {
112153
let serde_json::Value::Array(array) = v else {
113154
return;

0 commit comments

Comments
 (0)