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
229 changes: 223 additions & 6 deletions .github/workflows/test-standalone-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- 'packages/cli/install.sh'
- 'packages/cli/install.ps1'
- 'packages/tools/src/install-global-cli.ts'
- 'packages/tools/src/local-npm-registry.ts'
- 'crates/vp_installer/**'
- 'crates/vp_trampoline/**'
- 'crates/vp_global_cli/**'
Expand Down Expand Up @@ -284,7 +285,12 @@ jobs:
export VP_VPDIRS_AWARE=1
unset VP_HOME VP_BIN_DIR VP_DATA_DIR VP_CACHE_DIR
unset XDG_DATA_HOME XDG_CACHE_HOME XDG_CONFIG_HOME XDG_STATE_HOME
VP_LOCAL_TGZ="$FAKE_TGZ" VP_VERSION=local-test bash packages/cli/install.sh
OUTPUT=$(mktemp)
VP_LOCAL_TGZ="$FAKE_TGZ" VP_VERSION=local-test bash packages/cli/install.sh | tee "$OUTPUT"

grep -F "Install locations:" "$OUTPUT"
grep -F "Data directory: ~/.local/share/vite-plus" "$OUTPUT"
grep -F "Bin directory: ~/.local/share/vite-plus/bin" "$OUTPUT"

test ! -d "$FRESH/.vite-plus"
test -e "$FRESH/.local/share/vite-plus/current"
Expand Down Expand Up @@ -456,6 +462,18 @@ jobs:
Write-Error "Expected the pre-split fallback notice in installer output"
exit 1
}
if (-not (Select-String -Path install-output.txt -Pattern "Install locations:" -SimpleMatch -Quiet)) {
Write-Error "Expected install locations in installer output"
exit 1
}
if (-not (Select-String -Path install-output.txt -Pattern "Data directory: ~\.vite-plus" -SimpleMatch -Quiet)) {
Write-Error "Expected the single-root data directory in installer output"
exit 1
}
if (-not (Select-String -Path install-output.txt -Pattern "Bin directory: ~\.vite-plus\bin" -SimpleMatch -Quiet)) {
Write-Error "Expected the single-root bin directory in installer output"
exit 1
}

- name: Verify monolithic layout
shell: pwsh
Expand Down Expand Up @@ -509,8 +527,10 @@ jobs:
echo "$output"
# Verify installation succeeds (not a fatal error)
echo "$output" | grep -q "successfully installed"
# Verify fallback message shows binary location
echo "$output" | grep -q "vp was installed to:"
# Verify the success message shows the single-root locations
echo "$output" | grep -q "Install locations:"
echo "$output" | grep -q "Data directory: ~/.vite-plus"
echo "$output" | grep -q "Bin directory: ~/.vite-plus/bin"
# Verify fallback message shows manual instructions
echo "$output" | grep -q "Or run vp directly:"
# Verify the permission warning was shown
Expand Down Expand Up @@ -1225,6 +1245,87 @@ jobs:
shell: bash
run: cargo build --release -p vp_global_cli -p vp_installer -p vp_trampoline

- name: vp-setup.exe rejects invalid directory overrides
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $false
$installer = Join-Path $env:DEV_DRIVE "target/release/vp-setup.exe"
$root = Join-Path $env:RUNNER_TEMP "vp-setup-invalid-dirs"
$defaultRoot = Join-Path $env:LOCALAPPDATA "vite-plus"
$defaultRootExisted = Test-Path $defaultRoot
$dirVars = @("VP_HOME", "VP_BIN_DIR", "VP_DATA_DIR", "VP_CACHE_DIR")
$togetherError = "Set all three variables together: VP_BIN_DIR, VP_DATA_DIR, and VP_CACHE_DIR"
$cases = @(
@{ Name = "bin"; Values = @{ VP_BIN_DIR = Join-Path $root "bin" }; Error = $togetherError }
@{ Name = "data"; Values = @{ VP_DATA_DIR = Join-Path $root "data" }; Error = $togetherError }
@{ Name = "cache"; Values = @{ VP_CACHE_DIR = Join-Path $root "cache" }; Error = $togetherError }
@{ Name = "bin-data"; Values = @{ VP_BIN_DIR = Join-Path $root "bin"; VP_DATA_DIR = Join-Path $root "data" }; Error = $togetherError }
@{ Name = "bin-cache"; Values = @{ VP_BIN_DIR = Join-Path $root "bin"; VP_CACHE_DIR = Join-Path $root "cache" }; Error = $togetherError }
@{ Name = "data-cache"; Values = @{ VP_DATA_DIR = Join-Path $root "data"; VP_CACHE_DIR = Join-Path $root "cache" }; Error = $togetherError }
@{ Name = "relative-home"; Values = @{ VP_HOME = "relative-home" }; Error = "Set VP_HOME to an absolute path" }
@{ Name = "relative-split"; Values = @{ VP_BIN_DIR = "relative-bin"; VP_DATA_DIR = "relative-data"; VP_CACHE_DIR = "relative-cache" }; Error = "Set VP_BIN_DIR to an absolute path" }
)

foreach ($case in $cases) {
foreach ($name in $dirVars) {
[Environment]::SetEnvironmentVariable($name, $null)
}
foreach ($entry in $case.Values.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value)
}

$output = (& $installer --yes --quiet --no-node-manager --no-modify-path 2>&1) | Out-String
$exitCode = $LASTEXITCODE
Write-Host $output
if ($exitCode -ne 1) {
throw "$($case.Name) exited with $exitCode, expected 1"
}
if (-not $output.Contains($case.Error)) {
throw "$($case.Name) did not report '$($case.Error)'"
}
foreach ($value in $case.Values.Values) {
if ([System.IO.Path]::IsPathRooted($value) -and (Test-Path $value)) {
throw "$($case.Name) created requested root $value"
}
}
if (-not $defaultRootExisted -and (Test-Path $defaultRoot)) {
throw "$($case.Name) created default root $defaultRoot"
}
}

# Each installer process must fail in this test. Reset the native exit
# code after the assertions. This lets the PowerShell step succeed.
$global:LASTEXITCODE = 0

- name: vp-setup.exe rejects releases before 0.3.0
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $false
$installer = Join-Path $env:DEV_DRIVE "target/release/vp-setup.exe"
$installRoot = Join-Path $env:RUNNER_TEMP "vp-setup-unsupported-version"
Remove-Item -Recurse -Force $installRoot -ErrorAction SilentlyContinue
$env:VP_HOME = $installRoot
Remove-Item Env:VP_BIN_DIR, Env:VP_DATA_DIR, Env:VP_CACHE_DIR -ErrorAction SilentlyContinue

$output = (& $installer --yes --quiet --version 0.2.9 --no-node-manager --no-modify-path 2>&1) | Out-String
$exitCode = $LASTEXITCODE
Write-Host $output
if ($exitCode -ne 1) {
throw "vp-setup.exe exited with $exitCode, expected 1"
}
if (-not $output.Contains("Install vite-plus 0.3.0 or later")) {
throw "vp-setup.exe did not report its minimum supported version"
}
if ($output -match "(?i)preview") {
throw "vp-setup.exe mentioned preview builds"
}
if (Test-Path $installRoot) {
throw "vp-setup.exe created an installation root for version 0.2.9: $installRoot"
}
$global:LASTEXITCODE = 0

- name: Incomplete directory overrides are rejected in PowerShell
shell: pwsh
run: |
Expand Down Expand Up @@ -1340,7 +1441,17 @@ jobs:
New-Item -ItemType Directory -Force -Path $root | Out-Null
New-Item -ItemType File -Force -Path $env:VP_LOCAL_TGZ | Out-Null

& ./packages/cli/install.ps1
$output = (& ./packages/cli/install.ps1 *>&1) | Out-String
Write-Host $output
if (-not $output.Contains("Install locations:")) {
throw "install.ps1 did not print install locations"
}
if (-not $output.Contains("Data directory: $($env:VP_DATA_DIR)")) {
throw "install.ps1 did not print the split data directory"
}
if (-not $output.Contains("Bin directory: $($env:VP_BIN_DIR)")) {
throw "install.ps1 did not print the split bin directory"
}

function Get-DirMap([string]$VpBinary) {
$env:VP_DUMP_DIRS = "1"
Expand Down Expand Up @@ -1502,9 +1613,115 @@ jobs:
}
& $vp --version

- name: Install via vp-setup.exe (silent)
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
node-version: 24

- name: Start local preview registry for vp-setup.exe
shell: bash
run: |
test_version="0.0.0-commit.${GITHUB_SHA}"
registry_root="$RUNNER_TEMP/vp-setup-local-registry"
packages_dir="$registry_root/packages"
main_dir="$registry_root/vite-plus"
platform_dir="$registry_root/vite-plus-cli-win32-x64-msvc"
rm -rf "$registry_root"
mkdir -p "$packages_dir" "$main_dir" "$platform_dir"

node -e '
const fs = require("node:fs");
const path = require("node:path");
const [dir, version] = process.argv.slice(1);
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ name: "vite-plus", version }));
' "$main_dir" "$test_version"
node -e '
const fs = require("node:fs");
const path = require("node:path");
const [dir, version] = process.argv.slice(1);
const manifest = {
name: "@voidzero-dev/vite-plus-cli-win32-x64-msvc",
version,
files: ["vp.exe", "vp-shim.exe"],
};
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify(manifest));
' "$platform_dir" "$test_version"
cp "$DEV_DRIVE/target/release/vp.exe" "$DEV_DRIVE/target/release/vp-shim.exe" "$platform_dir/"
npm pack "$main_dir" --pack-destination "$packages_dir"
npm pack "$platform_dir" --pack-destination "$packages_dir"

registry_log="$registry_root/registry.out"
node "$GITHUB_WORKSPACE/packages/tools/src/local-npm-registry.ts" --serve --packages-dir "$packages_dir" > "$registry_log" 2>&1 &
server_pid=$!
until grep -q '"registry"' "$registry_log" 2>/dev/null; do
if ! kill -0 "$server_pid" 2>/dev/null; then
cat "$registry_log"
exit 1
fi
sleep 0.2
done
handshake=$(grep -m1 '"registry"' "$registry_log")
registry=$(echo "$handshake" | node -e 'process.stdin.on("data", data => process.stdout.write(JSON.parse(data).registry))')
echo "VP_SETUP_TEST_REGISTRY=$registry" >> "$GITHUB_ENV"
echo "VP_SETUP_TEST_VERSION=$test_version" >> "$GITHUB_ENV"

- name: Create a dangling current junction
shell: pwsh
run: ${{ format('{0}/target/release/vp-setup.exe', env.DEV_DRIVE) }}
run: |
$ErrorActionPreference = "Stop"
$current = Join-Path $env:VP_HOME "current"
$removedTarget = Join-Path $env:RUNNER_TEMP "vp-setup-removed-target"
Remove-Item -Recurse -Force $env:VP_HOME, $removedTarget -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $env:VP_HOME, $removedTarget | Out-Null

& cmd.exe /d /c "mklink /J `"$current`" `"$removedTarget`""
if ($LASTEXITCODE -ne 0) {
throw "cmd.exe could not create the test junction"
}
Remove-Item -Recurse -Force $removedTarget
& fsutil.exe reparsepoint query $current | Out-Null
if ($LASTEXITCODE -ne 0) {
throw "the dangling current entry is not a reparse point"
}

- name: Repair the dangling junction without ANSI output
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$installer = Join-Path $env:DEV_DRIVE "target/release/vp-setup.exe"
$stdout = Join-Path $env:RUNNER_TEMP "vp-setup-no-color.stdout.txt"
$stderr = Join-Path $env:RUNNER_TEMP "vp-setup-no-color.stderr.txt"
$env:NO_COLOR = "1"
try {
$process = Start-Process -FilePath $installer `
-ArgumentList @(
"--yes",
"--version", $env:VP_SETUP_TEST_VERSION,
"--registry", $env:VP_SETUP_TEST_REGISTRY,
"--no-node-manager",
"--no-modify-path"
) `
-RedirectStandardOutput $stdout -RedirectStandardError $stderr `
-Wait -PassThru -NoNewWindow
} finally {
Remove-Item Env:NO_COLOR -ErrorAction SilentlyContinue
}

Get-Content -Raw $stdout | Write-Host
Get-Content -Raw $stderr | Write-Host
if ($process.ExitCode -ne 0) {
throw "vp-setup.exe exited with $($process.ExitCode)"
}
foreach ($path in @($stdout, $stderr)) {
if ([System.IO.File]::ReadAllBytes($path) -contains [byte]0x1b) {
throw "$path contains an ANSI escape"
}
}
if (-not (Test-Path (Join-Path $env:VP_HOME "current/bin/vp.exe"))) {
throw "vp-setup.exe did not repair the dangling current junction"
}
if (-not (Test-Path (Join-Path $env:VP_HOME "bin/vp.exe"))) {
throw "vp-setup.exe did not create the global vp.exe"
}

- name: Set PATH
shell: bash
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ blake3 = "1.8.2"
chrono = { version = "0.4", features = ["serde"] }
clap = "4.5.40"
clap_complete = "4.6.0"
console = "0.16.4"
cow-utils = "0.1.3"
cp_r = "0.5.2"
criterion = { version = "0.7", features = ["html_reports"] }
Expand Down
34 changes: 1 addition & 33 deletions crates/vp_global_cli/src/commands/upgrade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub async fn execute(options: UpgradeOptions) -> Result<ExitStatus, Error> {
// binary. A monolithic install accepts each release. This includes a
// VP_HOME pin and an existing ~/.vite-plus install.
let legacy = vp_shared::VpDirs::legacy_single_root(&config.user_home);
if legacy.data != config.dirs.data && !supports_split_layout(&resolved.version) {
if legacy.data != config.dirs.data && !vp_setup::supports_split_layout(&resolved.version) {
return Err(Error::Upgrade(
format!(
"vite-plus {} does not support this split directory layout. \
Expand Down Expand Up @@ -280,35 +280,3 @@ async fn execute_rollback(

Ok(ExitStatus::default())
}

/// Return `true` if `version` supports the split layout.
///
/// Version 0.3.0 and later support it, including prereleases. Preview builds
/// (`0.0.0-commit.<sha>`) also support it because they track the current branch.
fn supports_split_layout(version: &str) -> bool {
let Ok(version) = node_semver::Version::parse(version) else {
return false;
};
if version.major == 0 && version.minor == 0 && version.patch == 0 {
return !version.pre_release.is_empty() || !version.build.is_empty();
}
version.major > 0 || version.minor >= 3
}

#[cfg(test)]
mod tests {
use super::supports_split_layout;

#[test]
fn split_layout_support_by_version() {
assert!(supports_split_layout("0.3.0"));
assert!(supports_split_layout("0.3.0-alpha.1"));
assert!(supports_split_layout("0.4.2"));
assert!(supports_split_layout("1.0.0"));
assert!(supports_split_layout("0.0.0-commit.0123abc"));
assert!(!supports_split_layout("0.2.9"));
assert!(!supports_split_layout("0.2.0"));
assert!(!supports_split_layout("0.1.14-alpha.1"));
assert!(!supports_split_layout("not-a-version"));
}
}
2 changes: 1 addition & 1 deletion crates/vp_installer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ path = "src/main.rs"

[dependencies]
clap = { workspace = true, features = ["derive"] }
console = { workspace = true }
indicatif = { workspace = true }
owo-colors = { workspace = true, features = ["supports-colors"] }
tokio = { workspace = true, features = ["full"] }
vp_pm_cli = { workspace = true }
vt_path = { workspace = true }
Expand Down
10 changes: 0 additions & 10 deletions crates/vp_installer/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,6 @@ pub struct Options {
#[arg(long = "tag", default_value = "latest")]
pub tag: String,

/// Set a custom single-root installation directory and set `VP_HOME`.
///
/// By default, Vite+ reuses an existing `~/.vite-plus` install. Otherwise,
/// it uses the platform data directory. This directory is
/// `~/.local/share/vite-plus` on Unix and `%LOCALAPPDATA%\vite-plus\data`
/// on Windows.
#[arg(long = "install-dir")]
pub install_dir: Option<String>,

/// Custom npm registry URL
#[arg(long = "registry")]
pub registry: Option<String>,
Expand All @@ -54,7 +45,6 @@ pub fn parse() -> Options {
opts.version = std::env::var("VP_VERSION").ok();
}
// `VP_HOME` / `VP_*_DIR` / `XDG_*` are owned by [`vp_shared::EnvConfig`].
// Do not promote them to `--install-dir` here.
if opts.registry.is_none() {
opts.registry = std::env::var("NPM_CONFIG_REGISTRY").ok();
}
Expand Down
Loading
Loading