From 14200b925e71e520382ed2d215e8bee36ccf5754 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Mon, 6 Apr 2026 08:46:48 +0000 Subject: [PATCH] fix(cli): fix PowerShell install errors on Windows (#1284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Add `Unblock-File` after downloading binaries to strip Zone.Identifier (Mark of the Web), preventing Windows SmartScreen/Defender "Access Denied" errors - Replace `*> $installLog` with captured output + `Out-File` to fix PowerShell 7.6 `StandardOutputEncoding` error - Add PowerShell 7.6 CI test job for regression coverage ## Details Three related issues all stem from Windows security blocking downloaded executables: 1. **#901 — `vp.exe` blocked by Windows Security**: Defender flags the downloaded binary due to MOTW Zone.Identifier ADS. Users had to manually add exclusions. 2. **#1019 — `StandardOutputEncoding` error (PowerShell 7.6)**: The `*>` redirection operator on native commands causes a .NET `InvalidOperationException` when running interactively (real console attached). Also reported "Access Denied" when running `vp.exe`. 3. **#1226 — `Failed to download Node.js runtime: Access is denied. (os error 5)`**: `vp.exe` starts but its file operations (locking, renaming) are restricted by Defender due to MOTW, causing the Node.js runtime download to fail. **Fix**: `Unblock-File` strips the MOTW from all `.exe` files in `$BinDir` immediately after extraction, before `vp.exe install --silent` runs. Closes #901 Closes #1019 Closes #1226 --- .github/workflows/test-standalone-install.yml | 80 +++++++++++++++++++ packages/cli/install.ps1 | 10 ++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-standalone-install.yml b/.github/workflows/test-standalone-install.yml index 9e3d29c1cd..b4b371a4a1 100644 --- a/.github/workflows/test-standalone-install.yml +++ b/.github/workflows/test-standalone-install.yml @@ -474,6 +474,86 @@ jobs: vp upgrade --rollback vp --version + test-install-ps1-v76: + name: Test install.ps1 (Windows x64, PowerShell 7.6) + runs-on: windows-latest + permissions: + contents: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install PowerShell 7.6 + shell: pwsh + run: | + dotnet tool install --global PowerShell --version 7.6.0 + $pwsh76 = "$env:USERPROFILE\.dotnet\tools\pwsh.exe" + echo "PWSH76=$pwsh76" >> $env:GITHUB_ENV + $ver = & $pwsh76 -NoProfile -Command '$PSVersionTable.PSVersion.ToString()' + Write-Host "PowerShell version: $ver" + if (-not $ver.StartsWith("7.6")) { + Write-Error "Expected PowerShell 7.6.x but got $ver" + exit 1 + } + + - name: Run install.ps1 via iex under PowerShell 7.6 + shell: pwsh + run: | + & $env:PWSH76 -NoProfile -Command "Get-Content ./packages/cli/install.ps1 -Raw | Invoke-Expression" + + - name: Set PATH + shell: bash + run: | + echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH + + - name: Verify installation + shell: pwsh + working-directory: ${{ runner.temp }} + run: | + Write-Host "PATH: $env:Path" + vp --version + vp --help + vp create vite --no-interactive --no-agent -- hello --no-interactive -t vanilla + cd hello + vp run build + vp --version + + - name: Verify bin setup + shell: pwsh + run: | + $binPath = "$env:USERPROFILE\.vite-plus\bin" + Get-ChildItem -Force $binPath + if (-not (Test-Path $binPath)) { + Write-Error "Bin directory not found: $binPath" + exit 1 + } + + $expectedShims = @("node.exe", "npm.exe", "npx.exe") + foreach ($shim in $expectedShims) { + $shimFile = Join-Path $binPath $shim + if (-not (Test-Path $shimFile)) { + Write-Error "Shim not found: $shimFile" + exit 1 + } + Write-Host "Found shim: $shimFile" + } + where.exe node + where.exe npm + where.exe npx + where.exe vp + + $env:Path = "$env:USERPROFILE\.vite-plus\bin;$env:Path" + vp env doctor + vp env run --node 24 -- node -p "process.versions" + + - name: Verify upgrade + shell: pwsh + run: | + vp upgrade --check + vp upgrade 0.1.14-alpha.1 + vp --version + vp upgrade --rollback + vp --version + test-install-ps1: name: Test install.ps1 (Windows x64) runs-on: windows-latest diff --git a/packages/cli/install.ps1 b/packages/cli/install.ps1 index 4755b4e0cb..973d7f1794 100644 --- a/packages/cli/install.ps1 +++ b/packages/cli/install.ps1 @@ -350,6 +350,10 @@ function Main { } } + # Remove Zone.Identifier (Mark of the Web) from downloaded binaries so + # Windows SmartScreen / Defender won't block execution. + Get-ChildItem -Path $BinDir -Filter "*.exe" | Unblock-File + # Generate wrapper package.json that declares vite-plus as a dependency. # pnpm will install vite-plus and all transitive deps via `vp install`. # The packageManager field pins pnpm to a known-good version. @@ -376,8 +380,10 @@ function Main { try { # Use cmd /c so CI=true is scoped to the child process only, # avoiding leaking it into the user's shell session. - cmd /c "set CI=true && `"$BinDir\vp.exe`" install --silent" *> $installLog - if ($LASTEXITCODE -ne 0) { + $output = cmd /c "set CI=true && `"$BinDir\vp.exe`" install --silent" 2>&1 + $installExitCode = $LASTEXITCODE + $output | Out-File $installLog + if ($installExitCode -ne 0) { Write-Host "error: Failed to install dependencies. See log for details: $installLog" -ForegroundColor Red exit 1 }