From c54d633c620c4981f3afa3799c5bcee3dc880cf9 Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Fri, 17 Jul 2026 01:34:29 +0900 Subject: [PATCH 1/9] ci(env): test generated env.nu with Nushell --- .github/scripts/test-nushell-env.sh | 50 +++++++++++++++++++++++++++++ .github/workflows/ci.yml | 11 +++++++ 2 files changed, 61 insertions(+) create mode 100755 .github/scripts/test-nushell-env.sh diff --git a/.github/scripts/test-nushell-env.sh b/.github/scripts/test-nushell-env.sh new file mode 100755 index 0000000000..dfd41fbdb3 --- /dev/null +++ b/.github/scripts/test-nushell-env.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +set -euo pipefail + +: "${VP_BIN_UNDER_TEST:?Set VP_BIN_UNDER_TEST to the vp binary to test}" + +nu_bin="$(command -v "${NU_BIN:-nu}")" +test_root="$(mktemp -d)" +trap 'rm -rf -- "$test_root"' EXIT + +test_home="$test_root/vp home-\"quoted\"" + +VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null + +( + cd "$test_home" + env -u VP_HOME \ + EXPECTED_VP_HOME="$test_home" \ + PATH="/usr/bin:/bin" \ + "$nu_bin" --commands ' + source env.nu + + let expected_home = ($env.EXPECTED_VP_HOME | path expand --no-symlink) + if $env.VP_HOME != $expected_home { + error make { + msg: $"VP_HOME mismatch: expected ($expected_home), got ($env.VP_HOME)" + } + } + + let expected_bin = ($expected_home | path join "bin") + let actual_bin = ($env.PATH | first) + if $actual_bin != $expected_bin { + error make { + msg: $"PATH mismatch: expected first entry ($expected_bin), got ($actual_bin)" + } + } + + let vp_output = (vp --version) + if $env.LAST_EXIT_CODE != 0 { + error make { + msg: "vp --version failed through the Nushell wrapper" + } + } + if ($vp_output | is-empty) { + error make { + msg: "vp --version returned no output" + } + } + ' +) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 13b3141593..68f9ea7e05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -943,6 +943,17 @@ jobs: - name: Install Global CLI vp run: pnpm bootstrap-cli:ci + # https://github.com/marketplace/actions/setup-nu + - name: Install Nushell + if: runner.os == 'Linux' + uses: hustcer/setup-nu@ccd5bb5426b05a32009c2ba967946231f3919c97 # v3.25 + with: + version: '*' + + - name: Test generated Nushell environment + if: runner.os == 'Linux' + run: VP_BIN_UNDER_TEST="$HOME/.vite-plus/bin/vp" .github/scripts/test-nushell-env.sh + # Provision the managed runtime once into the real home so cases can # seed from it (seed-runtime) instead of each downloading ~50MB. # Best-effort: without a seed, cases that need the runtime download it From 2b64749a99bf9c8580146a3a8395aa72a4eb73e1 Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Fri, 17 Jul 2026 01:47:17 +0900 Subject: [PATCH 2/9] fix(tests): update test home path to handle spaces correctly --- .github/scripts/test-nushell-env.sh | 2 +- Cargo.lock | 77 +++++++++++++++-------------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/.github/scripts/test-nushell-env.sh b/.github/scripts/test-nushell-env.sh index dfd41fbdb3..dd46dd38c3 100755 --- a/.github/scripts/test-nushell-env.sh +++ b/.github/scripts/test-nushell-env.sh @@ -8,7 +8,7 @@ nu_bin="$(command -v "${NU_BIN:-nu}")" test_root="$(mktemp -d)" trap 'rm -rf -- "$test_root"' EXIT -test_home="$test_root/vp home-\"quoted\"" +test_home="$test_root/vp home with spaces" VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null diff --git a/Cargo.lock b/Cargo.lock index d48351fd8c..ad414e6f5c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5864,7 +5864,7 @@ dependencies = [ [[package]] name = "rolldown" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "append-only-vec", @@ -6050,7 +6050,7 @@ dependencies = [ [[package]] name = "rolldown_common" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6069,6 +6069,7 @@ dependencies = [ "oxc_str", "rolldown_ecmascript", "rolldown_error", + "rolldown_fs", "rolldown_sourcemap", "rolldown_std_utils", "rolldown_utils", @@ -6085,7 +6086,7 @@ dependencies = [ [[package]] name = "rolldown_dev" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6106,7 +6107,7 @@ dependencies = [ [[package]] name = "rolldown_dev_common" -version = "1.1.5" +version = "1.2.0" dependencies = [ "derive_more", "rolldown_common", @@ -6118,7 +6119,7 @@ dependencies = [ [[package]] name = "rolldown_devtools" -version = "1.1.5" +version = "1.2.0" dependencies = [ "blake3", "rolldown_devtools_action", @@ -6131,7 +6132,7 @@ dependencies = [ [[package]] name = "rolldown_devtools_action" -version = "1.1.5" +version = "1.2.0" dependencies = [ "serde", "ts-rs", @@ -6139,7 +6140,7 @@ dependencies = [ [[package]] name = "rolldown_ecmascript" -version = "1.1.5" +version = "1.2.0" dependencies = [ "arcstr", "oxc", @@ -6151,7 +6152,7 @@ dependencies = [ [[package]] name = "rolldown_ecmascript_utils" -version = "1.1.5" +version = "1.2.0" dependencies = [ "memchr", "oxc", @@ -6161,7 +6162,7 @@ dependencies = [ [[package]] name = "rolldown_error" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6178,7 +6179,7 @@ dependencies = [ [[package]] name = "rolldown_fs" -version = "1.1.5" +version = "1.2.0" dependencies = [ "oxc_resolver", "vfs", @@ -6186,7 +6187,7 @@ dependencies = [ [[package]] name = "rolldown_fs_watcher" -version = "1.1.5" +version = "1.2.0" dependencies = [ "rolldown-notify", "rolldown-notify-debouncer-full", @@ -6195,7 +6196,7 @@ dependencies = [ [[package]] name = "rolldown_plugin" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6224,36 +6225,36 @@ dependencies = [ [[package]] name = "rolldown_plugin_asset_module" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "memchr", "rolldown_common", "rolldown_plugin", + "rolldown_std_utils", "rolldown_utils", "rustc-hash", "string_wizard", - "sugar_path", "tokio", ] [[package]] name = "rolldown_plugin_bundle_analyzer" -version = "1.1.5" +version = "1.2.0" dependencies = [ "arcstr", "rolldown_common", "rolldown_plugin", + "rolldown_std_utils", "rolldown_utils", "rustc-hash", "serde", "serde_json", - "sugar_path", ] [[package]] name = "rolldown_plugin_chunk_import_map" -version = "1.1.5" +version = "1.2.0" dependencies = [ "arcstr", "rolldown_common", @@ -6266,23 +6267,23 @@ dependencies = [ [[package]] name = "rolldown_plugin_copy_module" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", "memchr", "rolldown_common", "rolldown_plugin", + "rolldown_std_utils", "rolldown_utils", "rustc-hash", "string_wizard", - "sugar_path", "tokio", ] [[package]] name = "rolldown_plugin_data_url" -version = "1.1.5" +version = "1.2.0" dependencies = [ "arcstr", "base64-simd", @@ -6295,7 +6296,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_esm_external_require" -version = "1.1.5" +version = "1.2.0" dependencies = [ "nodejs-built-in-modules", "rolldown_common", @@ -6306,7 +6307,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_hmr" -version = "1.1.5" +version = "1.2.0" dependencies = [ "rolldown_common", "rolldown_plugin", @@ -6314,7 +6315,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_isolated_declaration" -version = "1.1.5" +version = "1.2.0" dependencies = [ "arcstr", "oxc", @@ -6328,7 +6329,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_lazy_compilation" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6341,7 +6342,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_oxc_runtime" -version = "1.1.5" +version = "1.2.0" dependencies = [ "arcstr", "phf 0.13.1", @@ -6352,7 +6353,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_replace" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "oxc", @@ -6368,7 +6369,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_utils" -version = "1.1.5" +version = "1.2.0" dependencies = [ "memchr", "rolldown_plugin", @@ -6409,6 +6410,7 @@ dependencies = [ "oxc", "rolldown_common", "rolldown_plugin", + "rolldown_std_utils", "rolldown_utils", "string_wizard", "sugar_path", @@ -6427,6 +6429,7 @@ dependencies = [ "rolldown_ecmascript_utils", "rolldown_plugin", "rolldown_plugin_utils", + "rolldown_std_utils", "rolldown_utils", "string_wizard", "sugar_path", @@ -6505,6 +6508,7 @@ dependencies = [ "rayon", "rolldown_common", "rolldown_plugin", + "rolldown_std_utils", "sugar_path", "supports-color 3.0.2", "terminal_size", @@ -6512,7 +6516,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_vite_resolve" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6566,7 +6570,7 @@ dependencies = [ [[package]] name = "rolldown_resolver" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6581,7 +6585,7 @@ dependencies = [ [[package]] name = "rolldown_sourcemap" -version = "1.1.5" +version = "1.2.0" dependencies = [ "criterion2", "memchr", @@ -6591,9 +6595,10 @@ dependencies = [ [[package]] name = "rolldown_std_utils" -version = "1.1.5" +version = "1.2.0" dependencies = [ "regex", + "sugar_path", ] [[package]] @@ -6636,7 +6641,7 @@ dependencies = [ [[package]] name = "rolldown_tracing" -version = "1.1.5" +version = "1.2.0" dependencies = [ "tracing", "tracing-chrome", @@ -6645,7 +6650,7 @@ dependencies = [ [[package]] name = "rolldown_utils" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6683,7 +6688,7 @@ dependencies = [ [[package]] name = "rolldown_watcher" -version = "1.1.5" +version = "1.2.0" dependencies = [ "anyhow", "arcstr", @@ -6703,7 +6708,7 @@ dependencies = [ [[package]] name = "rolldown_workspace" -version = "1.1.5" +version = "1.2.0" [[package]] name = "ropey" @@ -7507,7 +7512,7 @@ dependencies = [ [[package]] name = "string_wizard" -version = "1.1.5" +version = "1.2.0" dependencies = [ "insta", "memchr", From 7ef85f4ad1fa8af0e15305ad2f45b370fc76252a Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Fri, 17 Jul 2026 01:53:57 +0900 Subject: [PATCH 3/9] fix(dependencies): downgrade rolldown packages from 1.2.0 to 1.1.5 --- Cargo.lock | 77 +++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad414e6f5c..d48351fd8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5864,7 +5864,7 @@ dependencies = [ [[package]] name = "rolldown" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "append-only-vec", @@ -6050,7 +6050,7 @@ dependencies = [ [[package]] name = "rolldown_common" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6069,7 +6069,6 @@ dependencies = [ "oxc_str", "rolldown_ecmascript", "rolldown_error", - "rolldown_fs", "rolldown_sourcemap", "rolldown_std_utils", "rolldown_utils", @@ -6086,7 +6085,7 @@ dependencies = [ [[package]] name = "rolldown_dev" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6107,7 +6106,7 @@ dependencies = [ [[package]] name = "rolldown_dev_common" -version = "1.2.0" +version = "1.1.5" dependencies = [ "derive_more", "rolldown_common", @@ -6119,7 +6118,7 @@ dependencies = [ [[package]] name = "rolldown_devtools" -version = "1.2.0" +version = "1.1.5" dependencies = [ "blake3", "rolldown_devtools_action", @@ -6132,7 +6131,7 @@ dependencies = [ [[package]] name = "rolldown_devtools_action" -version = "1.2.0" +version = "1.1.5" dependencies = [ "serde", "ts-rs", @@ -6140,7 +6139,7 @@ dependencies = [ [[package]] name = "rolldown_ecmascript" -version = "1.2.0" +version = "1.1.5" dependencies = [ "arcstr", "oxc", @@ -6152,7 +6151,7 @@ dependencies = [ [[package]] name = "rolldown_ecmascript_utils" -version = "1.2.0" +version = "1.1.5" dependencies = [ "memchr", "oxc", @@ -6162,7 +6161,7 @@ dependencies = [ [[package]] name = "rolldown_error" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6179,7 +6178,7 @@ dependencies = [ [[package]] name = "rolldown_fs" -version = "1.2.0" +version = "1.1.5" dependencies = [ "oxc_resolver", "vfs", @@ -6187,7 +6186,7 @@ dependencies = [ [[package]] name = "rolldown_fs_watcher" -version = "1.2.0" +version = "1.1.5" dependencies = [ "rolldown-notify", "rolldown-notify-debouncer-full", @@ -6196,7 +6195,7 @@ dependencies = [ [[package]] name = "rolldown_plugin" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6225,36 +6224,36 @@ dependencies = [ [[package]] name = "rolldown_plugin_asset_module" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "memchr", "rolldown_common", "rolldown_plugin", - "rolldown_std_utils", "rolldown_utils", "rustc-hash", "string_wizard", + "sugar_path", "tokio", ] [[package]] name = "rolldown_plugin_bundle_analyzer" -version = "1.2.0" +version = "1.1.5" dependencies = [ "arcstr", "rolldown_common", "rolldown_plugin", - "rolldown_std_utils", "rolldown_utils", "rustc-hash", "serde", "serde_json", + "sugar_path", ] [[package]] name = "rolldown_plugin_chunk_import_map" -version = "1.2.0" +version = "1.1.5" dependencies = [ "arcstr", "rolldown_common", @@ -6267,23 +6266,23 @@ dependencies = [ [[package]] name = "rolldown_plugin_copy_module" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", "memchr", "rolldown_common", "rolldown_plugin", - "rolldown_std_utils", "rolldown_utils", "rustc-hash", "string_wizard", + "sugar_path", "tokio", ] [[package]] name = "rolldown_plugin_data_url" -version = "1.2.0" +version = "1.1.5" dependencies = [ "arcstr", "base64-simd", @@ -6296,7 +6295,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_esm_external_require" -version = "1.2.0" +version = "1.1.5" dependencies = [ "nodejs-built-in-modules", "rolldown_common", @@ -6307,7 +6306,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_hmr" -version = "1.2.0" +version = "1.1.5" dependencies = [ "rolldown_common", "rolldown_plugin", @@ -6315,7 +6314,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_isolated_declaration" -version = "1.2.0" +version = "1.1.5" dependencies = [ "arcstr", "oxc", @@ -6329,7 +6328,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_lazy_compilation" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6342,7 +6341,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_oxc_runtime" -version = "1.2.0" +version = "1.1.5" dependencies = [ "arcstr", "phf 0.13.1", @@ -6353,7 +6352,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_replace" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "oxc", @@ -6369,7 +6368,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_utils" -version = "1.2.0" +version = "1.1.5" dependencies = [ "memchr", "rolldown_plugin", @@ -6410,7 +6409,6 @@ dependencies = [ "oxc", "rolldown_common", "rolldown_plugin", - "rolldown_std_utils", "rolldown_utils", "string_wizard", "sugar_path", @@ -6429,7 +6427,6 @@ dependencies = [ "rolldown_ecmascript_utils", "rolldown_plugin", "rolldown_plugin_utils", - "rolldown_std_utils", "rolldown_utils", "string_wizard", "sugar_path", @@ -6508,7 +6505,6 @@ dependencies = [ "rayon", "rolldown_common", "rolldown_plugin", - "rolldown_std_utils", "sugar_path", "supports-color 3.0.2", "terminal_size", @@ -6516,7 +6512,7 @@ dependencies = [ [[package]] name = "rolldown_plugin_vite_resolve" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6570,7 +6566,7 @@ dependencies = [ [[package]] name = "rolldown_resolver" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6585,7 +6581,7 @@ dependencies = [ [[package]] name = "rolldown_sourcemap" -version = "1.2.0" +version = "1.1.5" dependencies = [ "criterion2", "memchr", @@ -6595,10 +6591,9 @@ dependencies = [ [[package]] name = "rolldown_std_utils" -version = "1.2.0" +version = "1.1.5" dependencies = [ "regex", - "sugar_path", ] [[package]] @@ -6641,7 +6636,7 @@ dependencies = [ [[package]] name = "rolldown_tracing" -version = "1.2.0" +version = "1.1.5" dependencies = [ "tracing", "tracing-chrome", @@ -6650,7 +6645,7 @@ dependencies = [ [[package]] name = "rolldown_utils" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6688,7 +6683,7 @@ dependencies = [ [[package]] name = "rolldown_watcher" -version = "1.2.0" +version = "1.1.5" dependencies = [ "anyhow", "arcstr", @@ -6708,7 +6703,7 @@ dependencies = [ [[package]] name = "rolldown_workspace" -version = "1.2.0" +version = "1.1.5" [[package]] name = "ropey" @@ -7512,7 +7507,7 @@ dependencies = [ [[package]] name = "string_wizard" -version = "1.2.0" +version = "1.1.5" dependencies = [ "insta", "memchr", From ea223b3ad54a7f999521bfa93057155dfca9ff3b Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:15:58 +0900 Subject: [PATCH 4/9] ci:trigger From 1a06878509be5f3b8d8d0fe1995c7b7ed86eff40 Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Thu, 6 Aug 2026 03:40:13 +0900 Subject: [PATCH 5/9] fix: update test script to handle paths with spaces and validate VP_NODE_VERSION --- .github/scripts/test-nushell-env.sh | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/scripts/test-nushell-env.sh b/.github/scripts/test-nushell-env.sh index dd46dd38c3..93e07ba895 100755 --- a/.github/scripts/test-nushell-env.sh +++ b/.github/scripts/test-nushell-env.sh @@ -8,7 +8,7 @@ nu_bin="$(command -v "${NU_BIN:-nu}")" test_root="$(mktemp -d)" trap 'rm -rf -- "$test_root"' EXIT -test_home="$test_root/vp home with spaces" +test_home="$test_root/vp \"home\\with spaces\"" VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null @@ -16,7 +16,7 @@ VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null cd "$test_home" env -u VP_HOME \ EXPECTED_VP_HOME="$test_home" \ - PATH="/usr/bin:/bin" \ + PATH="$test_home/bin:$test_home/bin:/usr/bin:/bin" \ "$nu_bin" --commands ' source env.nu @@ -34,6 +34,12 @@ VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null msg: $"PATH mismatch: expected first entry ($expected_bin), got ($actual_bin)" } } + let bin_count = ($env.PATH | where { $in == $expected_bin } | length) + if $bin_count != 1 { + error make { + msg: $"PATH contains the Vite+ bin directory ($bin_count) times" + } + } let vp_output = (vp --version) if $env.LAST_EXIT_CODE != 0 { @@ -46,5 +52,24 @@ VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null msg: "vp --version returned no output" } } + + vp env use 20.18.0 --no-install + if ("VP_NODE_VERSION" not-in $env) { + error make { + msg: "vp env use did not set VP_NODE_VERSION" + } + } + if $env.VP_NODE_VERSION != "20.18.0" { + error make { + msg: $"VP_NODE_VERSION mismatch: expected 20.18.0, got ($env.VP_NODE_VERSION)" + } + } + + vp env use --unset + if ("VP_NODE_VERSION" in $env) { + error make { + msg: "vp env use --unset did not remove VP_NODE_VERSION" + } + } ' ) From 1a7c150f83c12191586daf47cce88be85ea013c3 Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Thu, 6 Aug 2026 10:28:36 +0900 Subject: [PATCH 6/9] feat: add path rendering and escaping for Nushell compatibility --- .../vite_global_cli/src/commands/env/setup.rs | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/crates/vite_global_cli/src/commands/env/setup.rs b/crates/vite_global_cli/src/commands/env/setup.rs index ba6a215fa0..7944333874 100644 --- a/crates/vite_global_cli/src/commands/env/setup.rs +++ b/crates/vite_global_cli/src/commands/env/setup.rs @@ -739,6 +739,11 @@ Register-ArgumentCompleter -Native -CommandName vpr -ScriptBlock $__vpr_comp const VP_USE_CMD_CONTENT: &str = "@echo off\r\nset VP_ENV_USE_EVAL_ENABLE=1\r\nset VP_HOME=%~dp0..\r\nfor /f \"delims=\" %%i in ('%~dp0..\\current\\bin\\vp.exe env use %*') do %%i\r\nset VP_ENV_USE_EVAL_ENABLE=\r\n"; fn render_home_relative_path(path: &std::path::Path, home_dir: Option<&std::path::Path>) -> String { + fn render_path(path: &std::path::Path) -> String { + let rendered = path.display().to_string(); + if cfg!(windows) { rendered.replace('\\', "/") } else { rendered } + } + // Use $HOME-relative path if install dir is under HOME (like rustup's ~/.cargo/env). // This makes the env file portable across sessions where HOME may differ. home_dir @@ -748,10 +753,10 @@ fn render_home_relative_path(path: &std::path::Path, home_dir: Option<&std::path "$HOME".to_string() } else { // Normalize to forward slashes for $HOME/... paths (POSIX-style) - format!("$HOME/{}", s.display().to_string().replace('\\', "/")) + format!("$HOME/{}", render_path(s)) } }) - .unwrap_or_else(|| path.display().to_string().replace('\\', "/")) + .unwrap_or_else(|| render_path(path)) } fn render_nu_path_ref(path_ref: &str) -> String { @@ -762,6 +767,10 @@ fn render_nu_path_ref(path_ref: &str) -> String { } } +fn escape_nu_double_quoted_string(value: &str) -> String { + value.replace('\\', "\\\\").replace('"', "\\\"") +} + /// Render the env-file content for `shell` against `vite_plus_home`. fn render_env_content(shell: EnvShell, vite_plus_home: &vite_path::AbsolutePath) -> String { let bin_path = vite_plus_home.join("bin"); @@ -780,8 +789,10 @@ fn render_env_content(shell: EnvShell, vite_plus_home: &vite_path::AbsolutePath) EnvShell::Nu => { // Nushell requires `~` instead of `$HOME` in string literals — `$HOME` is not // expanded at parse time, so PATH entries would contain a literal "$HOME/...". - let home_path_ref_nu = render_nu_path_ref(&home_path_ref); - let bin_path_ref_nu = render_nu_path_ref(&bin_path_ref); + let home_path_ref_nu = + escape_nu_double_quoted_string(&render_nu_path_ref(&home_path_ref)); + let bin_path_ref_nu = + escape_nu_double_quoted_string(&render_nu_path_ref(&bin_path_ref)); ENV_TEMPLATE_NU .replace("__VP_HOME__", &home_path_ref_nu) .replace("__VP_BIN__", &bin_path_ref_nu) @@ -946,6 +957,35 @@ mod tests { assert!(env_ps1_path.as_path().exists(), "env.ps1 file should be created"); } + #[test] + fn test_escape_nu_double_quoted_string() { + assert_eq!( + escape_nu_double_quoted_string(r#"vp "home\with spaces""#), + r#"vp \"home\\with spaces\""# + ); + } + + #[cfg(unix)] + #[test] + fn test_render_env_content_escapes_nu_paths() { + let _guard = home_guard("/nonexistent-home-dir"); + let home = AbsolutePathBuf::new(std::path::PathBuf::from(r#"/tmp/vp "home\with spaces""#)) + .unwrap(); + + let content = render_env_content(EnvShell::Nu, &home); + + assert!( + content.contains( + r#"$env.VP_HOME = ("/tmp/vp \"home\\with spaces\"" | path expand --no-symlink)"# + ), + "env.nu should escape VP_HOME for a Nushell string literal, got: {content}" + ); + assert!( + content.contains(r#"prepend "/tmp/vp \"home\\with spaces\"/bin")"#), + "env.nu should escape the bin path for a Nushell string literal, got: {content}" + ); + } + #[tokio::test] async fn test_create_env_files_nu_contains_path_guard() { let temp_dir = TempDir::new().unwrap(); From befcdd4487fb922cb4b99a876e05af25905373bb Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Fri, 7 Aug 2026 02:55:10 +0900 Subject: [PATCH 7/9] docs: document Nushell path escaping --- crates/vp_global_cli/src/commands/env/setup.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/vp_global_cli/src/commands/env/setup.rs b/crates/vp_global_cli/src/commands/env/setup.rs index 06eec8ffbf..b99249b124 100644 --- a/crates/vp_global_cli/src/commands/env/setup.rs +++ b/crates/vp_global_cli/src/commands/env/setup.rs @@ -731,6 +731,8 @@ const VP_USE_CMD_CONTENT: &str = "@echo off\r\nset VP_ENV_USE_EVAL_ENABLE=1\r\ns fn render_home_relative_path(path: &std::path::Path, home_dir: Option<&std::path::Path>) -> String { fn render_path(path: &std::path::Path) -> String { let rendered = path.display().to_string(); + // Windows: `C:\Users\xxx\.vite-plus` → `C:/Users/xxx/.vite-plus` + // Unix: `/tmp/vp\home` → `/tmp/vp\home` (the backslash is preserved) if cfg!(windows) { rendered.replace('\\', "/") } else { rendered } } @@ -757,7 +759,12 @@ fn render_nu_path_ref(path_ref: &str) -> String { } } +/// Escapes a value so it can be safely embedded in a Nushell double-quoted string. +/// +/// Example: `vp "home\with spaces"` → `vp \"home\\with spaces\"` +/// https://www.nushell.sh/book/working_with_strings.html#double-quoted-strings fn escape_nu_double_quoted_string(value: &str) -> String { + // `vp "home\with spaces"` → `vp \"home\\with spaces\"` value.replace('\\', "\\\\").replace('"', "\\\"") } From 25000f0c80f66e9389ad5c10f94121e6a40b36a0 Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Fri, 7 Aug 2026 03:19:34 +0900 Subject: [PATCH 8/9] test: move Nushell env coverage to snapshots --- .github/scripts/test-nushell-env.sh | 75 ------------------- .github/workflows/ci.yml | 6 +- .../tests/cli_snapshots/README.md | 11 ++- .../fixtures/command_env_nushell/assert.nu | 56 ++++++++++++++ .../command_env_nushell/snapshots.toml | 9 +++ .../snapshots/command_env_nushell.md | 17 +++++ .../tests/cli_snapshots/flavor.rs | 26 ++++++- .../tests/cli_snapshots/main.rs | 8 ++ 8 files changed, 122 insertions(+), 86 deletions(-) delete mode 100755 .github/scripts/test-nushell-env.sh create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/assert.nu create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots/command_env_nushell.md diff --git a/.github/scripts/test-nushell-env.sh b/.github/scripts/test-nushell-env.sh deleted file mode 100755 index 93e07ba895..0000000000 --- a/.github/scripts/test-nushell-env.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -: "${VP_BIN_UNDER_TEST:?Set VP_BIN_UNDER_TEST to the vp binary to test}" - -nu_bin="$(command -v "${NU_BIN:-nu}")" -test_root="$(mktemp -d)" -trap 'rm -rf -- "$test_root"' EXIT - -test_home="$test_root/vp \"home\\with spaces\"" - -VP_HOME="$test_home" "$VP_BIN_UNDER_TEST" env setup --refresh >/dev/null - -( - cd "$test_home" - env -u VP_HOME \ - EXPECTED_VP_HOME="$test_home" \ - PATH="$test_home/bin:$test_home/bin:/usr/bin:/bin" \ - "$nu_bin" --commands ' - source env.nu - - let expected_home = ($env.EXPECTED_VP_HOME | path expand --no-symlink) - if $env.VP_HOME != $expected_home { - error make { - msg: $"VP_HOME mismatch: expected ($expected_home), got ($env.VP_HOME)" - } - } - - let expected_bin = ($expected_home | path join "bin") - let actual_bin = ($env.PATH | first) - if $actual_bin != $expected_bin { - error make { - msg: $"PATH mismatch: expected first entry ($expected_bin), got ($actual_bin)" - } - } - let bin_count = ($env.PATH | where { $in == $expected_bin } | length) - if $bin_count != 1 { - error make { - msg: $"PATH contains the Vite+ bin directory ($bin_count) times" - } - } - - let vp_output = (vp --version) - if $env.LAST_EXIT_CODE != 0 { - error make { - msg: "vp --version failed through the Nushell wrapper" - } - } - if ($vp_output | is-empty) { - error make { - msg: "vp --version returned no output" - } - } - - vp env use 20.18.0 --no-install - if ("VP_NODE_VERSION" not-in $env) { - error make { - msg: "vp env use did not set VP_NODE_VERSION" - } - } - if $env.VP_NODE_VERSION != "20.18.0" { - error make { - msg: $"VP_NODE_VERSION mismatch: expected 20.18.0, got ($env.VP_NODE_VERSION)" - } - } - - vp env use --unset - if ("VP_NODE_VERSION" in $env) { - error make { - msg: "vp env use --unset did not remove VP_NODE_VERSION" - } - } - ' -) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 490f336da9..64367b2468 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -947,15 +947,10 @@ jobs: # https://github.com/marketplace/actions/setup-nu - name: Install Nushell - if: runner.os == 'Linux' uses: hustcer/setup-nu@ccd5bb5426b05a32009c2ba967946231f3919c97 # v3.25 with: version: '*' - - name: Test generated Nushell environment - if: runner.os == 'Linux' - run: VP_BIN_UNDER_TEST="$HOME/.vite-plus/bin/vp" .github/scripts/test-nushell-env.sh - # Provision the managed runtime once into the real home so cases can # seed from it (seed-runtime) instead of each downloading ~50MB. # Best-effort: without a seed, cases that need the runtime download it @@ -974,6 +969,7 @@ jobs: run: | VP_SNAP_GLOBAL_VP="$HOME/.vite-plus/bin/vp" \ VP_SNAP_JS_RUNTIME_DIR="$HOME/.vite-plus/js_runtime" \ + VP_SNAP_NU_BIN="$(command -v nu)" \ cargo test -p vp_cli_snapshots env: RUST_BACKTRACE: '1' diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/README.md b/crates/vp_cli_snapshots/tests/cli_snapshots/README.md index f117bd86ac..9085a84da6 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/README.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/README.md @@ -63,6 +63,7 @@ Environment overrides, mainly for CI: | `VP_SNAP_GLOBAL_VP` | Path to a prebuilt global `vp` binary (skips the target-dir lookup) | | `VP_SNAP_LOCAL_CLI_BIN_DIR` | Local CLI bin dir (default `/packages/cli/bin`) | | `VP_SNAP_JS_RUNTIME_DIR` | Provisioned managed runtime to seed case homes with | +| `VP_SNAP_NU_BIN` | Nushell binary for cases that execute generated `env.nu` files | | `VP_SNAP_SKIP_FLAVORS` | Comma-separated flavors to skip registering (e.g. `local`) | ## Case reference @@ -116,9 +117,10 @@ A step is a bare argv array or a table: interactions = [ ... ] } ``` -`argv[0]` may be `vpt` or any executable exposed by the case's Vite+ -installation, including default shims such as `vp`, `node`, and `corepack` -and globally installed package binaries. There is no shell: no `&&`, no +`argv[0]` may be `vpt`, a runner-provisioned tool such as `nu`, or any +executable exposed by the case's Vite+ installation, including default shims +such as `vp`, `node`, and `corepack` and globally installed package binaries. +There is no shell: no `&&`, no redirects, no globs. File setup and assertions go through `vpt` so behavior is identical on every platform: @@ -182,7 +184,8 @@ case-owned tool dirs, then a system tail for child processes and direct `git` st `TERM=xterm-256color`, `VP_CLI_TEST=1`, `VP_EMIT_MILESTONES=1`, a fresh `HOME`, `VP_HOME`, and npm prefix. The runner still rejects direct step tools that resolve outside the case-owned dirs, except for `git`; `vpt` is the only -runner helper on PATH. `CI` and `NO_COLOR` are deliberately NOT set: with a PTY +required runner helper on PATH, while optional tools such as `nu` are linked +there when available. `CI` and `NO_COLOR` are deliberately NOT set: with a PTY attached, the CLI behaves interactively by default, which is the point. `seed-runtime = true` (default) symlinks a provisioned managed Node runtime into the case `VP_HOME` so commands do not download ~50MB per case. diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/assert.nu b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/assert.nu new file mode 100644 index 0000000000..d834942056 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/assert.nu @@ -0,0 +1,56 @@ +source env.nu + +let expected_home = ($env.EXPECTED_VP_HOME | path expand --no-symlink) +if $env.VP_HOME != $expected_home { + error make { + msg: $"VP_HOME mismatch: expected ($expected_home), got ($env.VP_HOME)" + } +} + +let expected_bin = ($expected_home | path join "bin") +let actual_bin = ($env.PATH | first) +if $actual_bin != $expected_bin { + error make { + msg: $"PATH mismatch: expected first entry ($expected_bin), got ($actual_bin)" + } +} + +let bin_count = ($env.PATH | where { $in == $expected_bin } | length) +if $bin_count != 1 { + error make { + msg: $"PATH contains the Vite+ bin directory ($bin_count) times" + } +} + +let vp_output = (vp --version) +if $env.LAST_EXIT_CODE != 0 { + error make { + msg: "vp --version failed through the Nushell wrapper" + } +} +if ($vp_output | is-empty) { + error make { + msg: "vp --version returned no output" + } +} + +vp env use 20.18.0 --no-install +if ("VP_NODE_VERSION" not-in $env) { + error make { + msg: "vp env use did not set VP_NODE_VERSION" + } +} +if $env.VP_NODE_VERSION != "20.18.0" { + error make { + msg: $"VP_NODE_VERSION mismatch: expected 20.18.0, got ($env.VP_NODE_VERSION)" + } +} + +vp env use --unset +if ("VP_NODE_VERSION" in $env) { + error make { + msg: "vp env use --unset did not remove VP_NODE_VERSION" + } +} + +print "Nushell environment checks passed" diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml new file mode 100644 index 0000000000..e0299a57fa --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml @@ -0,0 +1,9 @@ +[[case]] +name = "command_env_nushell" +vp = "global" +skip-platforms = ["windows"] +steps = [ + { argv = ["vp", "env", "setup", "--refresh"], envs = [["VP_HOME", '${workspace}/vp "home\with spaces"']], snapshot = false }, + { argv = ["vpt", "cp", "assert.nu", 'vp "home\with spaces"/assert.nu'], snapshot = false }, + { argv = ["nu", "assert.nu"], cwd = 'vp "home\with spaces"', comment = "loads the generated env.nu and verifies the Nushell wrapper", envs = [["EXPECTED_VP_HOME", "${workspace}"], ["PATH", "${workspace}/bin:${workspace}/bin:${PATH}"]] }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots/command_env_nushell.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots/command_env_nushell.md new file mode 100644 index 0000000000..2b8d9bcebe --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots/command_env_nushell.md @@ -0,0 +1,17 @@ +# command_env_nushell + +## `VP_HOME=${workspace}/vp "home\with spaces" vp env setup --refresh` + + +## `vpt cp assert.nu 'vp "home\with spaces"/assert.nu'` + + +## `cd 'vp "home\with spaces"' && EXPECTED_VP_HOME=${workspace} PATH=${workspace}/bin:${workspace}/bin:${PATH} nu assert.nu` + +loads the generated env.nu and verifies the Nushell wrapper + +``` +Using Node.js (resolved from 20.18.0) +Reverted to file-based Node.js version resolution +Nushell environment checks passed +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs index 2cfbf66c4d..cc4fca6a5c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs @@ -5,7 +5,8 @@ //! checkout package's JS bin directory from inside that same case home. //! //! Each flavor gets one runner bin directory per run (created under the run -//! temp root) for runner-owned helpers. Only `vpt` lives there. +//! temp root) for runner-owned helpers. `vpt` always lives there; optional +//! external tools such as Nushell are linked there when available. use std::path::{Path, PathBuf}; @@ -29,6 +30,9 @@ impl Flavor { pub struct FlavorRuntime { pub runner_bin_dir: PathBuf, pub vpt: PathBuf, + /// Runner-owned Nushell binary used by fixtures that execute generated + /// `env.nu` files. CI supplies it through `VP_SNAP_NU_BIN`. + pub nu: Option, /// Source global `vp` binary to install into each case's `VP_HOME/current`. pub global_vp: PathBuf, /// Source package installed into each case's `VP_HOME/current/node_modules`. @@ -189,6 +193,21 @@ fn vpt_path() -> Result { }) } +/// Resolves an optional Nushell binary for fixtures that exercise generated +/// `env.nu` files. The explicit override keeps CI deterministic; a developer's +/// PATH is the local fallback. +fn nushell_path() -> Result, String> { + if let Some(nu) = std::env::var_os("VP_SNAP_NU_BIN") { + let nu = PathBuf::from(nu); + if nu.is_file() { + return Ok(Some(nu)); + } + return Err(format!("VP_SNAP_NU_BIN is set but {} does not exist", nu.display())); + } + + Ok(which::which("nu").ok()) +} + /// Home-layout names, shared with `CaseHome` in main.rs so the product's /// `~/.vite-plus/js_runtime` layout is spelled once. pub const VP_HOME_DIR: &str = ".vite-plus"; @@ -286,10 +305,13 @@ pub fn provision(flavor: Flavor, run_root: &Path) -> Result local_cli_package_dir()?, Flavor::Global => repo_root().join("packages/cli"), }; - Ok(FlavorRuntime { runner_bin_dir, vpt, global_vp, cli_package_dir }) + Ok(FlavorRuntime { runner_bin_dir, vpt, nu, global_vp, cli_package_dir }) } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs index 78ee1c0ca1..00486b07ce 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs @@ -437,6 +437,7 @@ struct CaseInstall { path_env: OsString, tool_dirs: Vec, vpt: PathBuf, + nu: Option, } impl CaseInstall { @@ -451,6 +452,12 @@ impl CaseInstall { if program == "vpt" { return Ok(self.vpt.clone()); } + if program == "nu" { + return self.nu.clone().ok_or_else(|| { + "`nu` is required by this snapshot case; install Nushell or set VP_SNAP_NU_BIN" + .to_owned() + }); + } // An explicit `./`-prefixed program runs a file the case itself // produced inside the staged workspace (a packed executable); the @@ -546,6 +553,7 @@ impl CaseHome { path_env: compose_path_env(&path_dirs), tool_dirs, vpt: runtime.vpt.clone(), + nu: runtime.nu.clone(), }) } From d4ae878f976d7f66529e51cb75a41c6eb14bc8aa Mon Sep 17 00:00:00 2001 From: naokihaba <59875779+naokihaba@users.noreply.github.com> Date: Fri, 7 Aug 2026 03:32:44 +0900 Subject: [PATCH 9/9] fix: handle optional Nushell snapshot dependency --- .../tests/cli_snapshots/README.md | 1 + .../command_env_nushell/snapshots.toml | 1 + .../tests/cli_snapshots/flavor.rs | 6 +++-- .../tests/cli_snapshots/main.rs | 25 ++++++++++++++++++- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/README.md b/crates/vp_cli_snapshots/tests/cli_snapshots/README.md index 9085a84da6..0005c5c2e6 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/README.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/README.md @@ -75,6 +75,7 @@ vp = "local" # "local" | "global" | ["local", "global"] comment = "What this proves." # rendered into the snapshot cwd = "packages/app" # optional, relative to the fixture root skip-platforms = ["windows"] # or { os = "linux", libc = "musl" } +requires = ["nu"] # ignore when an optional runner tool is absent ignore = false # true: only runs with `-- --ignored` seed-runtime = true # false: start from an empty VP_HOME link-node-modules = false # true: expose the run-root node_modules as diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml index e0299a57fa..93e3c071aa 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_nushell/snapshots.toml @@ -2,6 +2,7 @@ name = "command_env_nushell" vp = "global" skip-platforms = ["windows"] +requires = ["nu"] steps = [ { argv = ["vp", "env", "setup", "--refresh"], envs = [["VP_HOME", '${workspace}/vp "home\with spaces"']], snapshot = false }, { argv = ["vpt", "cp", "assert.nu", 'vp "home\with spaces"/assert.nu'], snapshot = false }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs index cc4fca6a5c..9f5f8455f4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs @@ -196,11 +196,13 @@ fn vpt_path() -> Result { /// Resolves an optional Nushell binary for fixtures that exercise generated /// `env.nu` files. The explicit override keeps CI deterministic; a developer's /// PATH is the local fallback. -fn nushell_path() -> Result, String> { +pub fn nushell_path() -> Result, String> { if let Some(nu) = std::env::var_os("VP_SNAP_NU_BIN") { let nu = PathBuf::from(nu); if nu.is_file() { - return Ok(Some(nu)); + return std::fs::canonicalize(&nu).map(Some).map_err(|e| { + format!("failed to canonicalize VP_SNAP_NU_BIN {}: {e}", nu.display()) + }); } return Err(format!("VP_SNAP_NU_BIN is set but {} does not exist", nu.display())); } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs index 00486b07ce..c0991f0014 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs @@ -316,6 +316,22 @@ impl PlatformFilter { } } +#[derive(Clone, Copy, serde::Deserialize, Debug)] +#[serde(rename_all = "lowercase")] +enum RequiredTool { + Nu, +} + +impl RequiredTool { + /// A configuration error counts as available here so the trial runs and + /// reports that error instead of silently hiding a bad override. + fn is_missing(self) -> bool { + match self { + Self::Nu => matches!(flavor::nushell_path(), Ok(None)), + } + } +} + #[derive(serde::Deserialize, Debug)] #[serde(deny_unknown_fields)] struct Case { @@ -332,6 +348,10 @@ struct Case { /// Exclude-list of platforms this case does not run on. #[serde(default, rename = "skip-platforms")] skip_platforms: Vec, + /// Optional runner-owned tools needed by this case. The trial is ignored + /// when a tool is unavailable, while invalid explicit overrides still fail. + #[serde(default)] + requires: Vec, /// Marks the trial `#[ignore]` (runnable with `cargo test -- --ignored`). #[serde(default)] ignore: bool, @@ -1602,6 +1622,7 @@ fn main() { if case.skip_platforms.iter().any(PlatformFilter::matches_current) { continue; } + let required_tool_missing = case.requires.iter().any(|tool| tool.is_missing()); let multi = case.vp.is_multi(); let case = Arc::new(case); for flavor in case.vp.flavors() { @@ -1623,7 +1644,9 @@ fn main() { let fixture_name = Arc::clone(&fixture_name); let tmp_dir_path = Arc::clone(&tmp_dir_path); let case = Arc::clone(&case); - let ignored = case.ignore || (case.local_registry && !local_build_present); + let ignored = case.ignore + || required_tool_missing + || (case.local_registry && !local_build_present); let isolated = case_needs_isolation(&case); let timings = Arc::clone(&timings); let timing_name = trial_name.clone();