Skip to content

Commit 1957b34

Browse files
branchseerclaude
andauthored
Port packages/tools bins to single Rust binary (vtt) (#291)
## Summary - Replace all 8 Node.js test utilities in \`packages/tools\` with a single Rust binary \`vtt\` using subcommands - Remove \`pnpm install\` in \`packages/tools\` as a test prerequisite - Remove \`cross-env\` dependency — use native \`ENV=VALUE\` shell syntax ## CI Time Comparison Compared against main run [23539482166](https://github.com/voidzero-dev/vite-task/actions/runs/23539482166) vs this PR run [23579321007](https://github.com/voidzero-dev/vite-task/actions/runs/23579321007): | Platform | Main | This PR | Improvement | |---|---|---|---| | Linux (x86_64-gnu) | 2m16s | 2m2s | **-11%** | | Linux (musl) | 4m28s | 4m15s | **-5%** | | macOS (x86_64) | 6m38s | 4m40s | **-30%** | | macOS (aarch64) | 2m30s | 1m38s | **-35%** | | Windows | 5m38s | 4m23s | **-22%** | ## Test plan - [x] All e2e and plan snapshot tests pass on all platforms - [x] Clippy and formatting pass - [x] cargo shear: no unused dependencies https://claude.ai/code/session_01MM41gBUbWHBiX8Skve91rG --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 26c1892 commit 1957b34

File tree

283 files changed

+1377
-1574
lines changed

Some content is hidden

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

283 files changed

+1377
-1574
lines changed

.github/workflows/ci.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,20 @@ jobs:
127127
# Must run after setup-node so correct native binaries are installed
128128
- run: pnpm install
129129

130-
- run: cargo test --target ${{ matrix.target }}
130+
- name: Build tests
131+
run: cargo test --no-run --target ${{ matrix.target }}
131132
if: ${{ matrix.os != 'ubuntu-latest' }}
132133

133-
- run: cargo-zigbuild test --target x86_64-unknown-linux-gnu.2.17
134+
- name: Build tests
135+
run: cargo-zigbuild test --no-run --target x86_64-unknown-linux-gnu.2.17
136+
if: ${{ matrix.os == 'ubuntu-latest' }}
137+
138+
- name: Run tests
139+
run: cargo test --target ${{ matrix.target }}
140+
if: ${{ matrix.os != 'ubuntu-latest' }}
141+
142+
- name: Run tests
143+
run: cargo-zigbuild test --target x86_64-unknown-linux-gnu.2.17
134144
if: ${{ matrix.os == 'ubuntu-latest' }}
135145

136146
test-musl:
@@ -175,7 +185,11 @@ jobs:
175185
corepack enable
176186
pnpm install
177187
178-
- run: cargo test
188+
- name: Build tests
189+
run: cargo test --no-run
190+
191+
- name: Run tests
192+
run: cargo test
179193

180194
fmt:
181195
name: Format and Check Deps

Cargo.lock

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

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ os_str_bytes = "7.1.1"
9393
ouroboros = "0.18.5"
9494
owo-colors = { version = "4.1.0", features = ["supports-colors"] }
9595
passfd = { git = "https://github.com/polachok/passfd", rev = "d55881752c16aced1a49a75f9c428d38d3767213", default-features = false }
96+
notify = "8.0.0"
9697
path-clean = "1.0.1"
97-
pathdiff = "0.2.3"
9898
petgraph = "0.8.2"
9999
phf = { version = "0.11.3", features = ["macros"] }
100100
portable-pty = "0.9.0"
@@ -167,6 +167,9 @@ ignored = [
167167
# and we don't rely on it for debugging that much.
168168
debug = false
169169

170+
[profile.test]
171+
debug = false
172+
170173
[profile.release]
171174
# Configurations explicitly listed here for clarity.
172175
# Using the best options for performance.

crates/vite_task_bin/Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ rust-version.workspace = true
1010
name = "vt"
1111
path = "src/main.rs"
1212

13+
[[bin]]
14+
name = "vtt"
15+
path = "src/vtt/main.rs"
16+
1317
[dependencies]
1418
anyhow = { workspace = true }
19+
libc = { workspace = true }
20+
notify = { workspace = true }
1521
async-trait = { workspace = true }
1622
clap = { workspace = true, features = ["derive"] }
1723
jsonc-parser = { workspace = true }
18-
rustc-hash = { workspace = true }
1924
serde_json = { workspace = true }
2025
tokio = { workspace = true, features = ["full"] }
2126
vite_path = { workspace = true }
@@ -27,7 +32,6 @@ which = { workspace = true }
2732
cow-utils = { workspace = true }
2833
cp_r = { workspace = true }
2934
insta = { workspace = true, features = ["glob", "json", "redactions", "filters", "ron"] }
30-
pathdiff = { workspace = true }
3135
pty_terminal = { workspace = true }
3236
pty_terminal_test = { workspace = true }
3337
regex = { workspace = true }

crates/vite_task_bin/src/lib.rs

Lines changed: 22 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use std::{
66
};
77

88
use clap::Parser;
9-
use rustc_hash::FxHashMap;
109
use vite_path::AbsolutePath;
1110
use vite_str::Str;
1211
use vite_task::{
@@ -47,44 +46,25 @@ pub fn find_executable(
4746
Ok(executable_path.into_os_string().into())
4847
}
4948

50-
/// Create a synthetic plan request for running a tool from `node_modules/.bin`.
49+
/// Internal argument parser for `vt`/`vp` commands that appear inside task scripts.
5150
///
52-
/// # Errors
53-
///
54-
/// Returns an error if the executable cannot be found.
55-
fn synthesize_node_modules_bin_task(
56-
executable_name: &str,
57-
args: &[Str],
58-
envs: &Arc<FxHashMap<Arc<OsStr>, Arc<OsStr>>>,
59-
cwd: &Arc<AbsolutePath>,
60-
) -> anyhow::Result<SyntheticPlanRequest> {
61-
Ok(SyntheticPlanRequest {
62-
program: find_executable(get_path_env(envs), cwd, executable_name)?,
63-
args: args.into(),
64-
cache_config: UserCacheConfig::with_config(EnabledCacheConfig {
65-
env: None,
66-
untracked_env: None,
67-
input: None,
68-
}),
69-
envs: Arc::clone(envs),
70-
})
71-
}
72-
51+
/// [`CommandHandler`] uses this to parse the command line when it intercepts a `vt` or `vp`
52+
/// invocation during script execution. It extends [`Command`] with a `tool` subcommand that
53+
/// forwards to the `vtt` test-utility binary — a subcommand that only makes sense within
54+
/// script execution and is therefore not exposed on the top-level `vt` CLI entry point.
7355
#[derive(Debug, Parser)]
7456
#[command(name = "vt", version)]
75-
pub enum Args {
76-
Lint {
57+
enum Args {
58+
/// Forward arguments to the `vtt` test-utility binary.
59+
///
60+
/// Resolves `vtt` via `node_modules/.bin` lookup (same as any other script executable),
61+
/// then synthesizes a cached invocation with the given arguments. The `--` separator,
62+
/// if present, is stripped before forwarding.
63+
Tool {
7764
#[clap(trailing_var_arg = true, allow_hyphen_values = true)]
7865
args: Vec<Str>,
7966
},
80-
Test {
81-
#[clap(trailing_var_arg = true, allow_hyphen_values = true)]
82-
args: Vec<Str>,
83-
},
84-
EnvTest {
85-
name: Str,
86-
value: Str,
87-
},
67+
/// Any other `vt` subcommand, delegated to the standard [`Command`] parser.
8868
#[command(flatten)]
8969
Task(Command),
9070
}
@@ -109,30 +89,17 @@ impl vite_task::CommandHandler for CommandHandler {
10989
std::iter::once(command.program.as_str()).chain(command.args.iter().map(Str::as_str)),
11090
)?;
11191
match args {
112-
Args::Lint { args } => Ok(HandledCommand::Synthesized(
113-
synthesize_node_modules_bin_task("oxlint", &args, &command.envs, &command.cwd)?,
114-
)),
115-
Args::Test { args } => Ok(HandledCommand::Synthesized(
116-
synthesize_node_modules_bin_task("vitest", &args, &command.envs, &command.cwd)?,
117-
)),
118-
Args::EnvTest { name, value } => {
119-
let mut envs = FxHashMap::clone(&command.envs);
120-
envs.insert(
121-
Arc::from(OsStr::new(name.as_str())),
122-
Arc::from(OsStr::new(value.as_str())),
123-
);
124-
92+
Args::Tool { args } => {
93+
let program = find_executable(get_path_env(&command.envs), &command.cwd, "vtt")?;
12594
Ok(HandledCommand::Synthesized(SyntheticPlanRequest {
126-
program: find_executable(get_path_env(&envs), &command.cwd, "print-env")?,
127-
args: [name.clone()].into(),
128-
cache_config: UserCacheConfig::with_config({
129-
EnabledCacheConfig {
130-
env: None,
131-
untracked_env: Some(vec![name]),
132-
input: None,
133-
}
95+
program,
96+
args: args.into_iter().filter(|a| a.as_str() != "--").collect(),
97+
cache_config: UserCacheConfig::with_config(EnabledCacheConfig {
98+
env: None,
99+
untracked_env: None,
100+
input: None,
134101
}),
135-
envs: Arc::new(envs),
102+
envs: Arc::clone(&command.envs),
136103
}))
137104
}
138105
Args::Task(parsed) => Ok(HandledCommand::ViteTaskCommand(parsed)),

crates/vite_task_bin/src/main.rs

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,18 @@
1-
use std::{process::ExitCode, sync::Arc};
1+
use std::process::ExitCode;
22

3-
use clap::Parser;
4-
use vite_str::Str;
5-
use vite_task::{
6-
EnabledCacheConfig, ExitStatus, Session, UserCacheConfig, get_path_env,
7-
plan_request::SyntheticPlanRequest,
8-
};
9-
use vite_task_bin::{Args, OwnedSessionConfig, find_executable};
3+
use clap::Parser as _;
4+
use vite_task::{Command, ExitStatus, Session};
5+
use vite_task_bin::OwnedSessionConfig;
106

117
#[tokio::main]
128
async fn main() -> anyhow::Result<ExitCode> {
13-
#[expect(clippy::large_futures, reason = "top-level await in main, no alternative")]
149
let exit_status = run().await?;
1510
Ok(exit_status.0.into())
1611
}
1712

1813
async fn run() -> anyhow::Result<ExitStatus> {
19-
let args = Args::parse();
14+
let args = Command::parse();
2015
let mut owned_config = OwnedSessionConfig::default();
2116
let session = Session::init(owned_config.as_config())?;
22-
match args {
23-
Args::Task(parsed) => session.main(parsed).await,
24-
args => {
25-
// If env FOO is set, run `print-env FOO` via Session::exec before proceeding.
26-
// In vite-plus, Session::exec is used for auto-install.
27-
let envs = session.envs();
28-
if envs.contains_key(std::ffi::OsStr::new("FOO")) {
29-
let program = find_executable(get_path_env(envs), session.cwd(), "print-env")?;
30-
let request = SyntheticPlanRequest {
31-
program,
32-
args: [Str::from("FOO")].into(),
33-
cache_config: UserCacheConfig::with_config({
34-
EnabledCacheConfig {
35-
env: Some(Box::from([Str::from("FOO")])),
36-
untracked_env: None,
37-
input: None,
38-
}
39-
}),
40-
envs: Arc::clone(envs),
41-
};
42-
let cache_key: Arc<[Str]> = Arc::from([Str::from("print-env-foo")]);
43-
#[expect(
44-
clippy::large_futures,
45-
reason = "execute_synthetic produces a large future"
46-
)]
47-
let status = session.execute_synthetic(request, cache_key, true).await?;
48-
if status != ExitStatus::SUCCESS {
49-
return Ok(status);
50-
}
51-
}
52-
#[expect(clippy::print_stdout, reason = "CLI binary output for non-task commands")]
53-
{
54-
println!("{args:?}");
55-
}
56-
Ok(ExitStatus::SUCCESS)
57-
}
58-
}
17+
session.main(args).await
5918
}

0 commit comments

Comments
 (0)