Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,9 @@ jobs:
- name: Install Global CLI vp
run: pnpm bootstrap-cli:ci

- name: Install Fish
uses: fish-actions/install-fish@4bb37a71daebb76c6fb7d83771cff31b8a78c28e # v1.2.0

# https://github.com/marketplace/actions/setup-nu
- name: Install Nushell
uses: hustcer/setup-nu@ccd5bb5426b05a32009c2ba967946231f3919c97 # v3.25
Expand All @@ -980,6 +983,7 @@ jobs:
run: |
VP_SNAP_GLOBAL_VP="$HOME/.vite-plus/bin/vp" \
VP_SNAP_JS_RUNTIME_DIR="$HOME/.vite-plus/js_runtime" \
VP_SNAP_FISH_BIN="$(command -v fish)" \
VP_SNAP_NU_BIN="$(command -v nu)" \
cargo test -p vp_cli_snapshots
env:
Expand Down
12 changes: 7 additions & 5 deletions crates/vp_cli_snapshots/tests/cli_snapshots/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<repo>/packages/cli/bin`) |
| `VP_SNAP_JS_RUNTIME_DIR` | Provisioned managed runtime to seed case homes with |
| `VP_SNAP_FISH_BIN` | Fish binary for cases that execute generated `env.fish` files |
| `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`) |

Expand All @@ -75,7 +76,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
requires = ["fish"] # "fish" | "nu"; ignore when the 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
Expand Down Expand Up @@ -118,7 +119,7 @@ A step is a bare argv array or a table:
interactions = [ ... ] }
```

`argv[0]` may be `vpt`, a runner-provisioned tool such as `nu`, or any
`argv[0]` may be `vpt`, a runner-provisioned tool such as `fish` or `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
Expand Down Expand Up @@ -185,9 +186,10 @@ 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
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.
required runner helper on PATH, while optional tools such as `fish` and `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.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
source env.fish
or begin
echo "failed to source env.fish"
exit 1
end

if test "$VP_HOME" != "$EXPECTED_VP_HOME"
echo "VP_HOME mismatch: expected $EXPECTED_VP_HOME, got $VP_HOME"
exit 1
end

set -l expected_bin "$EXPECTED_VP_HOME/bin"
if test "$PATH[1]" != "$expected_bin"
echo "PATH mismatch: expected first entry $expected_bin, got $PATH[1]"
exit 1
end

set -l bin_count 0
for entry in $PATH
if test "$entry" = "$expected_bin"
set bin_count (math $bin_count + 1)
end
end
if test $bin_count -ne 1
echo "PATH contains the Vite+ bin directory $bin_count times"
exit 1
end

functions -q vp
or begin
echo "env.fish did not define the vp wrapper"
exit 1
end

set -l vp_output (vp --version)
or begin
echo "vp --version failed through the Fish wrapper"
exit 1
end
if test (count $vp_output) -eq 0
echo "vp --version returned no output"
exit 1
end

echo "Fish environment setup checks passed"
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
source env.fish
or begin
echo "failed to source env.fish"
exit 1
end

set -gx VP_NODE_VERSION 18.20.0
vp env use --help >/dev/null
or begin
echo "vp env use --help failed through the Fish wrapper"
exit 1
end
if test "$VP_NODE_VERSION" != "18.20.0"
echo "vp env use --help changed VP_NODE_VERSION"
exit 1
end

vp env use 20.18.0 --no-install
or begin
echo "vp env use failed through the Fish wrapper"
exit 1
end
if not set -q VP_NODE_VERSION
echo "vp env use did not set VP_NODE_VERSION"
exit 1
end
if test "$VP_NODE_VERSION" != "20.18.0"
echo "VP_NODE_VERSION mismatch: expected 20.18.0, got $VP_NODE_VERSION"
exit 1
end

vp env use --unset
or begin
echo "vp env use --unset failed through the Fish wrapper"
exit 1
end
if set -q VP_NODE_VERSION
echo "vp env use --unset did not remove VP_NODE_VERSION"
exit 1
end

vp env use --no-install
or begin
echo "vp env use without a version failed through the Fish wrapper"
exit 1
end
if not set -q VP_NODE_VERSION
echo "vp env use without a version did not set VP_NODE_VERSION"
exit 1
end
if test "$VP_NODE_VERSION" != "22.18.0"
echo "file-based VP_NODE_VERSION mismatch: expected 22.18.0, got $VP_NODE_VERSION"
exit 1
end

if vp env use --invalid-option >/dev/null 2>&1
echo "vp env use did not preserve a failing command status"
exit 1
end
if test "$VP_NODE_VERSION" != "22.18.0"
echo "failing vp env use changed VP_NODE_VERSION: $VP_NODE_VERSION"
exit 1
end

echo "Fish environment use checks passed"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[[case]]
name = "command_env_fish_setup"
vp = "global"
skip-platforms = ["windows"]
requires = ["fish"]
steps = [
{ argv = ["vp", "env", "setup", "--refresh"], envs = [["VP_HOME", '${workspace}/vp "$project\with spaces"']], snapshot = false },
{ argv = ["vpt", "cp", "assert_setup.fish", 'vp "$project\with spaces"/assert_setup.fish'], snapshot = false },
{ argv = ["fish", "--no-config", "assert_setup.fish"], cwd = 'vp "$project\with spaces"', comment = "loads the generated env.fish and verifies Fish path setup and command passthrough", envs = [["EXPECTED_VP_HOME", "${workspace}"], ["PATH", "${workspace}/bin:${workspace}/bin:${PATH}"]] },
]

[[case]]
name = "command_env_fish_use"
vp = "global"
skip-platforms = ["windows"]
requires = ["fish"]
steps = [
{ argv = ["vp", "env", "setup", "--refresh"], envs = [["VP_HOME", '${workspace}/vp "$project\with spaces"']], snapshot = false },
{ argv = ["vpt", "cp", "assert_use.fish", 'vp "$project\with spaces"/assert_use.fish'], snapshot = false },
{ argv = ["vpt", "write-file", 'vp "$project\with spaces"/.node-version', "22.18.0"], snapshot = false },
{ argv = ["fish", "--no-config", "assert_use.fish"], cwd = 'vp "$project\with spaces"', comment = "verifies the Fish wrapper help, explicit use, unset, file-based use, and failure branches", envs = [["PATH", "${workspace}/bin:${PATH}"]] },
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# command_env_fish_setup

## `VP_HOME=${workspace}/vp "$project\with spaces" vp env setup --refresh`


## `vpt cp assert_setup.fish 'vp "$project\with spaces"/assert_setup.fish'`


## `cd 'vp "$project\with spaces"' && EXPECTED_VP_HOME=${workspace} PATH=${workspace}/bin:${workspace}/bin:${PATH} fish --no-config assert_setup.fish`

loads the generated env.fish and verifies Fish path setup and command passthrough

```
Fish environment setup checks passed
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# command_env_fish_use

## `VP_HOME=${workspace}/vp "$project\with spaces" vp env setup --refresh`


## `vpt cp assert_use.fish 'vp "$project\with spaces"/assert_use.fish'`


## `vpt write-file 'vp "$project\with spaces"/.node-version' 22.18.0`


## `cd 'vp "$project\with spaces"' && PATH=${workspace}/bin:${PATH} fish --no-config assert_use.fish`

verifies the Fish wrapper help, explicit use, unset, file-based use, and failure branches

```
Using Node.js <version> (resolved from 20.18.0)
Reverted to file-based Node.js version resolution
Using Node.js <version> (resolved from .node-version)
error: Unexpected argument '--invalid-option'

Use `-- --invalid-option` to pass the argument as a value
Fish environment use checks passed
```
44 changes: 30 additions & 14 deletions crates/vp_cli_snapshots/tests/cli_snapshots/flavor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
//!
//! Each flavor gets one runner bin directory per run (created under the run
//! temp root) for runner-owned helpers. `vpt` always lives there; optional
//! external tools such as Nushell are linked there when available.
//! external tools such as Fish and Nushell are linked there when available.

use std::path::{Path, PathBuf};

Expand All @@ -30,6 +30,9 @@ impl Flavor {
pub struct FlavorRuntime {
pub runner_bin_dir: PathBuf,
pub vpt: PathBuf,
/// Runner-owned Fish binary used by fixtures that execute generated
/// `env.fish` files. CI supplies it through `VP_SNAP_FISH_BIN`.
pub fish: Option<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<PathBuf>,
Expand Down Expand Up @@ -193,21 +196,32 @@ fn vpt_path() -> Result<PathBuf, String> {
})
}

/// 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.
pub fn nushell_path() -> Result<Option<PathBuf>, String> {
if let Some(nu) = std::env::var_os("VP_SNAP_NU_BIN") {
let nu = PathBuf::from(nu);
if nu.is_file() {
return std::fs::canonicalize(&nu).map(Some).map_err(|e| {
format!("failed to canonicalize VP_SNAP_NU_BIN {}: {e}", nu.display())
});
/// Resolves an optional external tool. The explicit override keeps CI
/// deterministic; a developer's PATH is the local fallback.
fn optional_tool_path(env_var: &str, binary: &str) -> Result<Option<PathBuf>, String> {
if let Some(tool) = std::env::var_os(env_var) {
let tool = PathBuf::from(tool);
if tool.is_file() {
return std::fs::canonicalize(&tool)
.map(Some)
.map_err(|e| format!("failed to canonicalize {env_var} {}: {e}", tool.display()));
}
return Err(format!("VP_SNAP_NU_BIN is set but {} does not exist", nu.display()));
return Err(format!("{env_var} is set but {} does not exist", tool.display()));
}

Ok(which::which("nu").ok())
Ok(which::which(binary).ok())
}

/// Resolves an optional Fish binary for fixtures that exercise generated
/// `env.fish` files.
pub fn fish_path() -> Result<Option<PathBuf>, String> {
optional_tool_path("VP_SNAP_FISH_BIN", "fish")
}

/// Resolves an optional Nushell binary for fixtures that exercise generated
/// `env.nu` files.
pub fn nushell_path() -> Result<Option<PathBuf>, String> {
optional_tool_path("VP_SNAP_NU_BIN", "nu")
}

/// Home-layout names, shared with `CaseHome` in main.rs so the product's
Expand Down Expand Up @@ -307,6 +321,8 @@ pub fn provision(flavor: Flavor, run_root: &Path) -> Result<FlavorRuntime, Strin
.map_err(|e| format!("failed to create bin dir: {e}"))?;

let vpt = install_runner_tool(&runner_bin_dir, "vpt", &vpt_path()?)?;
let fish =
fish_path()?.map(|path| install_runner_tool(&runner_bin_dir, "fish", &path)).transpose()?;
let nu = nushell_path()?
.map(|path| install_runner_tool(&runner_bin_dir, "nu", &path))
.transpose()?;
Expand All @@ -315,5 +331,5 @@ pub fn provision(flavor: Flavor, run_root: &Path) -> Result<FlavorRuntime, Strin
Flavor::Local => local_cli_package_dir()?,
Flavor::Global => repo_root().join("packages/cli"),
};
Ok(FlavorRuntime { runner_bin_dir, vpt, nu, global_vp, cli_package_dir })
Ok(FlavorRuntime { runner_bin_dir, vpt, fish, nu, global_vp, cli_package_dir })
}
10 changes: 10 additions & 0 deletions crates/vp_cli_snapshots/tests/cli_snapshots/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ impl PlatformFilter {
#[derive(Clone, Copy, serde::Deserialize, Debug)]
#[serde(rename_all = "lowercase")]
enum RequiredTool {
Fish,
Nu,
}

Expand All @@ -327,6 +328,7 @@ impl RequiredTool {
/// reports that error instead of silently hiding a bad override.
fn is_missing(self) -> bool {
match self {
Self::Fish => matches!(flavor::fish_path(), Ok(None)),
Self::Nu => matches!(flavor::nushell_path(), Ok(None)),
}
}
Expand Down Expand Up @@ -457,6 +459,7 @@ struct CaseInstall {
path_env: OsString,
tool_dirs: Vec<PathBuf>,
vpt: PathBuf,
fish: Option<PathBuf>,
nu: Option<PathBuf>,
}

Expand All @@ -472,6 +475,12 @@ impl CaseInstall {
if program == "vpt" {
return Ok(self.vpt.clone());
}
if program == "fish" {
return self.fish.clone().ok_or_else(|| {
"`fish` is required by this snapshot case; install Fish or set VP_SNAP_FISH_BIN"
.to_owned()
});
}
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"
Expand Down Expand Up @@ -573,6 +582,7 @@ impl CaseHome {
path_env: compose_path_env(&path_dirs),
tool_dirs,
vpt: runtime.vpt.clone(),
fish: runtime.fish.clone(),
nu: runtime.nu.clone(),
})
}
Expand Down
10 changes: 7 additions & 3 deletions crates/vp_global_cli/src/commands/env/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,9 @@ fi
"#;

const ENV_TEMPLATE_FISH: &str = r#"# Vite+ environment setup (https://viteplus.dev)
__ENV_EXPORTS__set -l __vp_idx (contains -i -- "__VP_BIN__" $PATH)
and set -e PATH[$__vp_idx]
__ENV_EXPORTS__while set -l __vp_idx (contains -i -- "__VP_BIN__" $PATH)
set -e PATH[$__vp_idx]
end
set -gx PATH "__VP_BIN__" $PATH

# Shell function wrapper: intercepts `vp env use` to eval its stdout,
Expand All @@ -587,7 +588,10 @@ function vp
set -lx VP_ENV_USE_EVAL_ENABLE 1
set -lx VP_SHELL fish
set -l __vp_out (command vp $argv); or return $status
eval (string join ';' $__vp_out)
for __vp_command in $__vp_out
eval $__vp_command; or return $status
Comment thread
fengmk2 marked this conversation as resolved.
end
return 0
else
command vp $argv
end
Expand Down
Loading
Loading