Skip to content

Commit c4118ad

Browse files
branchseerclaude
andcommitted
test(e2e): add argv step spawn mode
Replace shell string steps with structured argv format in all e2e test fixtures. Steps now use `argv` arrays instead of shell command strings, with optional `comment` and `envs` fields: ```toml # Simple: steps = [["vt", "run", "build"]] # With metadata: steps = [{ argv = ["vt", "run", "test"], comment = "cache miss", envs = [["MY_ENV", "1"]] }] # Stdin piping via vtt helper (no shell): steps = [["vtt", "pipe-stdin", "from-stdin", "--", "vt", "run", "read-stdin"]] ``` Processes are spawned directly without a shell wrapper, avoiding shell interference with signal handling and exit codes. Programs are resolved from `CARGO_BIN_EXE_<name>` env vars. Shell redirects are replaced with `vtt write-file` and `vtt pipe-stdin` helpers. Also documents Conventional Commits format for PR titles in CLAUDE.md. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2295d41 commit c4118ad

File tree

59 files changed

+1071
-341
lines changed

Some content is hidden

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

59 files changed

+1071
-341
lines changed

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ just doc # Documentation generation
3838

3939
If `gt` (Graphite CLI) is available in PATH, use it instead of `gh` to create pull requests.
4040

41+
PR titles must use [Conventional Commits](https://www.conventionalcommits.org) format: `type(scope): summary` (scope is optional), e.g. `feat(cache): add LRU eviction`, `fix: handle symlink loops`, `test(e2e): add ctrl-c propagation test`.
42+
4143
## Tests
4244

4345
```bash

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_task_bin/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ pty_terminal = { workspace = true }
3636
pty_terminal_test = { workspace = true }
3737
regex = { workspace = true }
3838
serde = { workspace = true, features = ["derive", "rc"] }
39+
shell-escape = { workspace = true }
3940
tempfile = { workspace = true }
4041
toml = { workspace = true }
42+
vec1 = { workspace = true, features = ["serde"] }
4143
vite_path = { workspace = true, features = ["absolute-redaction"] }
4244
vite_workspace = { workspace = true }
4345

crates/vite_task_bin/src/vtt/cp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pub fn run(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
2+
if args.len() != 2 {
3+
return Err("Usage: vtt cp <src> <dst>".into());
4+
}
5+
std::fs::copy(&args[0], &args[1])?;
6+
Ok(())
7+
}

crates/vite_task_bin/src/vtt/main.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,25 @@
88

99
mod barrier;
1010
mod check_tty;
11+
mod cp;
12+
mod mkdir;
13+
mod pipe_stdin;
1114
mod print;
1215
mod print_cwd;
1316
mod print_env;
1417
mod print_file;
1518
mod read_stdin;
1619
mod replace_file_content;
20+
mod rm;
1721
mod touch_file;
22+
mod write_file;
1823

1924
fn main() {
2025
let args: Vec<String> = std::env::args().collect();
2126
if args.len() < 2 {
2227
eprintln!("Usage: vtt <subcommand> [args...]");
2328
eprintln!(
24-
"Subcommands: barrier, check-tty, print, print-cwd, print-env, print-file, read-stdin, replace-file-content, touch-file"
29+
"Subcommands: barrier, check-tty, cp, mkdir, pipe-stdin, print, print-cwd, print-env, print-file, read-stdin, replace-file-content, rm, touch-file, write-file"
2530
);
2631
std::process::exit(1);
2732
}
@@ -32,6 +37,8 @@ fn main() {
3237
check_tty::run();
3338
Ok(())
3439
}
40+
"cp" => cp::run(&args[2..]),
41+
"mkdir" => mkdir::run(&args[2..]),
3542
"print" => {
3643
print::run(&args[2..]);
3744
Ok(())
@@ -41,7 +48,10 @@ fn main() {
4148
"print-file" => print_file::run(&args[2..]),
4249
"read-stdin" => read_stdin::run(),
4350
"replace-file-content" => replace_file_content::run(&args[2..]),
51+
"pipe-stdin" => pipe_stdin::run(&args[2..]),
52+
"rm" => rm::run(&args[2..]),
4453
"touch-file" => touch_file::run(&args[2..]),
54+
"write-file" => write_file::run(&args[2..]),
4555
other => {
4656
eprintln!("Unknown subcommand: {other}");
4757
std::process::exit(1);
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
pub fn run(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
2+
let mut parents = false;
3+
let mut paths = Vec::new();
4+
for arg in args {
5+
match arg.as_str() {
6+
"-p" => parents = true,
7+
_ => paths.push(arg.as_str()),
8+
}
9+
}
10+
if paths.is_empty() {
11+
return Err("Usage: vtt mkdir [-p] <path>...".into());
12+
}
13+
for path in paths {
14+
if parents {
15+
std::fs::create_dir_all(path)?;
16+
} else {
17+
std::fs::create_dir(path)?;
18+
}
19+
}
20+
Ok(())
21+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/// pipe-stdin `<data>` -- `<command>` [`<args>`...]
2+
///
3+
/// Spawns `<command>` with `<data>` piped to its stdin, then exits with
4+
/// the child's exit code. If `<data>` is empty, an empty stdin is provided.
5+
pub fn run(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
6+
let sep = args
7+
.iter()
8+
.position(|a| a == "--")
9+
.ok_or("Usage: vtt pipe-stdin <data> -- <command> [args...]")?;
10+
let data = &args[..sep].join(" ");
11+
let cmd_args = &args[sep + 1..];
12+
if cmd_args.is_empty() {
13+
return Err("Usage: vtt pipe-stdin <data> -- <command> [args...]".into());
14+
}
15+
16+
let mut child = std::process::Command::new(&cmd_args[0])
17+
.args(&cmd_args[1..])
18+
.stdin(std::process::Stdio::piped())
19+
.spawn()?;
20+
21+
{
22+
use std::io::Write;
23+
let mut stdin = child.stdin.take().unwrap();
24+
if !data.is_empty() {
25+
stdin.write_all(data.as_bytes())?;
26+
}
27+
stdin.write_all(b"\n")?;
28+
// stdin is closed when dropped, signaling EOF
29+
}
30+
31+
let status = child.wait()?;
32+
std::process::exit(status.code().unwrap_or(1));
33+
}

crates/vite_task_bin/src/vtt/rm.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
pub fn run(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
2+
let mut recursive = false;
3+
let mut paths = Vec::new();
4+
for arg in args {
5+
match arg.as_str() {
6+
"-r" | "-rf" | "-f" => recursive = true,
7+
_ => paths.push(arg.as_str()),
8+
}
9+
}
10+
if paths.is_empty() {
11+
return Err("Usage: vtt rm [-rf] <path>...".into());
12+
}
13+
for path in paths {
14+
let p = std::path::Path::new(path);
15+
if p.is_dir() && recursive {
16+
std::fs::remove_dir_all(p)?;
17+
} else {
18+
std::fs::remove_file(p)?;
19+
}
20+
}
21+
Ok(())
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pub fn run(args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
2+
if args.len() < 2 {
3+
return Err("Usage: vtt write-file <filename> <content>".into());
4+
}
5+
std::fs::write(&args[0], &args[1])?;
6+
Ok(())
7+
}

crates/vite_task_bin/tests/e2e_snapshots/fixtures/associate-existing-cache/snapshots.toml

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,26 @@
33
[[e2e]]
44
name = "associate existing cache"
55
steps = [
6-
"vt run script1 # cache miss",
7-
"vt run script2 # cache hit, same command as script1",
8-
"vtt replace-file-content package.json '\"script2\": \"vtt print hello\"' '\"script2\": \"vtt print world\"' # change script2",
9-
"vt run script2 # cache miss",
6+
{ argv = [
7+
"vt",
8+
"run",
9+
"script1",
10+
], comment = "cache miss" },
11+
{ argv = [
12+
"vt",
13+
"run",
14+
"script2",
15+
], comment = "cache hit, same command as script1" },
16+
{ argv = [
17+
"vtt",
18+
"replace-file-content",
19+
"package.json",
20+
"\"script2\": \"vtt print hello\"",
21+
"\"script2\": \"vtt print world\"",
22+
], comment = "change script2" },
23+
{ argv = [
24+
"vt",
25+
"run",
26+
"script2",
27+
], comment = "cache miss" },
1028
]

0 commit comments

Comments
 (0)