Skip to content
Open
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
41 changes: 36 additions & 5 deletions .github/scripts/test-install-bootstrap.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ $originalCheck = $env:VP_SELF_SETUP_SUPPORT_CHECK
$originalPath = $env:Path
$originalRegistry = $env:NPM_CONFIG_REGISTRY
$originalVpShell = $env:VP_SHELL
$managerVariables = @('VP_NODE_MANAGER', 'VP_PM_MANAGER', 'VP_NPM_MANAGER', 'VP_PNPM_MANAGER', 'VP_YARN_MANAGER', 'VP_BUN_MANAGER')
$originalManagers = @{}
foreach ($name in $managerVariables) { $originalManagers[$name] = [Environment]::GetEnvironmentVariable($name) }
$fixtureSha = '0123456789012345678901234567890123456789'
New-Item -ItemType Directory -Path "$testRoot/package", "$testRoot/tmp", "$testRoot/scripts" | Out-Null
Set-Content -LiteralPath "$testRoot/package/vp.exe" -Value 'Payload fixture'
Expand All @@ -78,17 +81,23 @@ if ($args.Count -eq 0) {
$expectedVpShell = if ($scenario -eq 'supported-pr') { 'fish' } else { 'powershell' }
if ($env:VP_SHELL -ne $expectedVpShell) { exit 96 }
if ($scenario -eq 'supported-pr' -and $env:NPM_CONFIG_REGISTRY -ne 'https://registry-bridge.viteplus.dev/') { exit 97 }
if ($scenario -like 'system-only*' -and $env:VP_NODE_MANAGER -cne 'system-only') { exit 94 }
Write-Output ("`$script:InstallDir = '{0}'" -f "$testRoot/data")
Write-Output ("`$script:ShimDir = '{0}'" -f "$testRoot/installed bin")
Write-Output ("`$script:CacheDir = '{0}'" -f "$testRoot/cache")
Write-Output ("`$script:ConfigDir = '{0}'" -f "$testRoot/config")
Write-Output ("`$script:StateDir = '{0}'" -f "$testRoot/state")
exit 0
}
if ($env:VP_SELF_SETUP_SUPPORT_CHECK -ne '1') { exit 99 }
# Only a system-only setting asks for the second response line.
$expectedCheck = if ($scenario -like 'system-only*' -or $scenario -eq 'pm-system-only') { 'system-only' } else { '1' }
if ($env:VP_SELF_SETUP_SUPPORT_CHECK -cne $expectedCheck) { exit 99 }
New-Item -ItemType File -Path "$testRoot/binary-probed" | Out-Null
if ($scenario -in @('legacy', 'legacy-remote', 'legacy-failure', 'pr')) { Write-Output 'Usage: vp [COMMAND]' }
else { Write-Output 'vite-plus-self-setup-v1' }
if ($scenario -in @('legacy', 'legacy-remote', 'legacy-failure', 'pr', 'system-only-legacy')) { Write-Output 'Usage: vp [COMMAND]' }
else {
Write-Output 'vite-plus-self-setup-v1'
if ($expectedCheck -eq 'system-only' -and $scenario -ne 'system-only-v1') { Write-Output 'vite-plus-self-setup-system-only' }
}
exit 0
'@ | Set-Content -LiteralPath "$testRoot/package/binary.ps1"
@'
Expand Down Expand Up @@ -196,6 +205,11 @@ function Test-SelfSetupSupport {
param($BinarySource)
& $probe -BinarySource (Join-Path (Split-Path $BinarySource) 'binary.ps1')
}
$systemOnlyProbe = ${function:Test-SystemOnlySupport}
function Test-SystemOnlySupport {
param($BinarySource)
& $systemOnlyProbe -BinarySource (Join-Path (Split-Path $BinarySource) 'binary.ps1')
}
$handoff = ${function:Invoke-InstallHandoff}
function Invoke-InstallHandoff {
param($BinarySource)
Expand All @@ -205,6 +219,7 @@ function Invoke-InstallHandoff {
try {
foreach ($scenario in @(
'supported', 'legacy', 'legacy-remote', 'legacy-failure', 'failure', 'pr', 'supported-pr',
'system-only', 'system-only-v1', 'system-only-legacy', 'pm-system-only', 'mixed-case-system-only',
'metadata-error', 'metadata-missing-version', 'metadata-network-error', 'platform-error', 'platform-network-error',
'provenance-missing', 'provenance-malformed', 'provenance-dotted-key', 'tarball-missing',
'integrity-missing', 'integrity-malformed', 'integrity-unsupported', 'integrity-noncanonical',
Expand All @@ -220,9 +235,17 @@ try {
$script:PackageMetadata = $null
Remove-Item -LiteralPath "$testRoot/legacy", "$testRoot/binary-invoked", "$testRoot/binary-probed" -ErrorAction SilentlyContinue
$env:VP_SELF_SETUP_SUPPORT_CHECK = if ($scenario -eq 'supported') { 'original' } else { $null }
foreach ($name in $managerVariables) { [Environment]::SetEnvironmentVariable($name, $null, 'Process') }
switch ($scenario) {
{ $_ -like 'system-only*' } { $env:VP_NODE_MANAGER = 'system-only' }
'pm-system-only' { $env:VP_PNPM_MANAGER = 'system-only' }
# Only the exact lowercase value is system-only.
'mixed-case-system-only' { $env:VP_NODE_MANAGER = 'System-Only' }
}
$isSystemOnlyFailure = $scenario -in @('system-only-v1', 'system-only-legacy')
$isIntegrityFailure = $scenario -like 'integrity-*'
$isMetadataFailure = $scenario -match '^(metadata-|platform-|provenance-|tarball-missing$)'
$expectedExit = if ($isIntegrityFailure -or $isMetadataFailure) {
$expectedExit = if ($isIntegrityFailure -or $isMetadataFailure -or $isSystemOnlyFailure) {
1
} elseif ($scenario -in @('failure', 'legacy-failure')) {
42
Expand Down Expand Up @@ -260,13 +283,20 @@ try {
Write-Host "PASS: $scenario"
continue
}
if ($isSystemOnlyFailure) {
Assert (Test-Path -LiteralPath "$testRoot/binary-probed") 'System-only support was not probed'
Assert (-not (Test-Path -LiteralPath "$testRoot/binary-invoked")) 'An unsupported binary was invoked with system-only'
Assert (-not (Test-Path -LiteralPath "$testRoot/legacy")) 'System-only reached legacy setup'
Write-Host "PASS: $scenario"
continue
}
Assert ($env:VP_SHELL -eq $initialVpShell) 'Setup changed the caller shell'
if ($scenario -eq 'supported') {
Assert (($env:Path -split ';')[0] -eq "$testRoot/installed bin") 'Installed bin directory was not added to the current PATH'
} elseif ($scenario -eq 'failure') {
Assert ($env:Path -eq $originalPath) 'Failed setup changed the current PATH'
}
$usesBinary = $scenario -in @('supported', 'supported-pr', 'failure')
$usesBinary = $scenario -in @('supported', 'supported-pr', 'failure', 'system-only', 'pm-system-only', 'mixed-case-system-only')
Assert ((Test-Path -LiteralPath "$testRoot/binary-invoked") -eq $usesBinary) 'Incorrect binary invocation'
Assert ((Test-Path -LiteralPath "$testRoot/legacy") -eq (-not $usesBinary)) 'Incorrect legacy invocation'
if ($scenario -eq 'pr') {
Expand Down Expand Up @@ -325,6 +355,7 @@ try {
$env:Path = $originalPath
$env:NPM_CONFIG_REGISTRY = $originalRegistry
$env:VP_SHELL = $originalVpShell
foreach ($name in $managerVariables) { [Environment]::SetEnvironmentVariable($name, $originalManagers[$name], 'Process') }
$env:TEMP = $originalTemp
Remove-Item -LiteralPath $testRoot -Recurse -Force
$global:LASTEXITCODE = 0
Expand Down
35 changes: 31 additions & 4 deletions .github/scripts/test-install-bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,30 @@ if [ "$#" -eq 0 ]; then
if [[ "$scenario" == *pr ]]; then
test "$NPM_CONFIG_REGISTRY" = https://registry-bridge.viteplus.dev/ || exit 97
fi
if [[ "$scenario" == system-only* ]]; then
test "${VP_NODE_MANAGER:-}" = system-only || exit 95
fi
printf 'INSTALL_DIR=%q\n' "$test_root/data"
printf 'SHIM_DIR=%q\n' "$test_root/installed bin"
printf 'CACHE_DIR=%q\n' "$test_root/cache"
printf 'CONFIG_DIR=%q\n' "$test_root/config"
printf 'STATE_DIR=%q\n' "$test_root/state"
exit 0
fi
test "${VP_SELF_SETUP_SUPPORT_CHECK:-}" = 1 || exit 99
# Only a system-only setting asks for the second response line.
case "$scenario:${VP_SELF_SETUP_SUPPORT_CHECK:-}" in
system-only*:system-only|pm-system-only:system-only) ;;
*:1) ;;
*) exit 99 ;;
esac
touch "$test_root/binary-probed"
case "$scenario" in
legacy|legacy-failure|piped-legacy|piped-legacy-failure|pr) printf 'Usage: vp [COMMAND]\n' ;;
*) printf 'vite-plus-self-setup-v1\n' ;;
legacy|legacy-failure|piped-legacy|piped-legacy-failure|pr|system-only-legacy) printf 'Usage: vp [COMMAND]\n' ;;
*)
printf 'vite-plus-self-setup-v1\n'
if [ "$VP_SELF_SETUP_SUPPORT_CHECK" = system-only ] && [ "$scenario" != system-only-v1 ]; then
printf 'vite-plus-self-setup-system-only\n'
fi ;;
esac
BINARY
chmod +x "$test_root/package/vp"
Expand Down Expand Up @@ -128,6 +140,7 @@ curl() {
}

for scenario in supported legacy legacy-failure piped-legacy piped-legacy-failure failure pr supported-pr \
system-only system-only-v1 system-only-legacy pm-system-only mixed-case-system-only \
missing-hash-tools missing-hash-tools-pr \
escaped-integrity escaped-integrity-pr escaped-integrity-all escaped-integrity-lower escaped-integrity-key escaped-integrity-slash \
integrity-missing integrity-malformed integrity-unsupported integrity-noncanonical integrity-wrong-type \
Expand Down Expand Up @@ -156,6 +169,13 @@ for scenario in supported legacy legacy-failure piped-legacy piped-legacy-failur
fi
if [[ "$scenario" == *pr ]]; then PR_VERSION=2406; fi
if [ "$scenario" = supported ]; then export VP_SELF_SETUP_SUPPORT_CHECK=original; fi
unset VP_NODE_MANAGER VP_PM_MANAGER VP_NPM_MANAGER VP_PNPM_MANAGER VP_YARN_MANAGER VP_BUN_MANAGER
case "$scenario" in
system-only*) export VP_NODE_MANAGER=system-only ;;
pm-system-only) export VP_PNPM_MANAGER=system-only ;;
# Only the exact lowercase value is system-only.
mixed-case-system-only) export VP_NODE_MANAGER=System-Only ;;
esac
main
test "$NPM_CONFIG_REGISTRY" = https://custom.example
test "$INSTALL_DIR" = "$test_root/data"
Expand Down Expand Up @@ -186,14 +206,21 @@ for scenario in supported legacy legacy-failure piped-legacy piped-legacy-failur
test "$status" -eq 42
elif [[ "$scenario" == *legacy-failure ]]; then
test "$status" -eq 43
elif [ "$scenario" = system-only-v1 ] || [ "$scenario" = system-only-legacy ]; then
test "$status" -ne 0
grep -q 'does not support VP_NODE_MANAGER=system-only' "$test_root/output"
test -f "$test_root/binary-probed"
elif [ "$status" -ne 0 ]; then
cat "$test_root/output"
exit 1
fi
case "$scenario" in
supported|supported-pr|failure|missing-hash-tools*|escaped-integrity*)
supported|supported-pr|failure|missing-hash-tools*|escaped-integrity*|system-only|pm-system-only|mixed-case-system-only)
test -f "$test_root/binary-invoked"
test ! -f "$test_root/legacy" ;;
system-only-v1|system-only-legacy)
test ! -f "$test_root/binary-invoked"
test ! -f "$test_root/legacy" ;;
legacy|legacy-failure|piped-legacy|piped-legacy-failure|pr)
test -f "$test_root/legacy"
test ! -f "$test_root/binary-invoked" ;;
Expand Down
21 changes: 21 additions & 0 deletions .github/scripts/test-install-legacy-remote.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ try {
# setup-vp evaluates downloaded text in a script block, with no sibling legacy file.
# A child process also keeps installer exit calls from bypassing server cleanup.
$powershell = (Get-Process -Id $PID).Path

# A legacy release cannot honor system-only, so the installer must stop before legacy setup.
# Restore the workflow's value afterwards; the install below reproduces setup-vp with it.
$previousNodeManager = $env:VP_NODE_MANAGER
$env:VP_NODE_MANAGER = 'system-only'
try {
# Windows PowerShell 5.1 turns redirected child stderr into errors, which 'Stop' would throw.
$ErrorActionPreference = 'Continue'
$output = & $powershell -NoProfile -Command '& ([scriptblock]::Create((irm -TimeoutSec 15 $env:VP_TEST_INSTALLER_URL)))' 2>&1 | Out-String
$exitCode = $LASTEXITCODE
$ErrorActionPreference = 'Stop'
if ($exitCode -eq 0) { throw 'The installer accepted system-only for a legacy release' }
if (-not $output.Contains('does not support VP_NODE_MANAGER=system-only')) { throw "Unexpected system-only failure: $output" }
} finally {
$ErrorActionPreference = 'Stop'
$env:VP_NODE_MANAGER = $previousNodeManager
}
if (Test-Path -LiteralPath (Join-Path $env:VP_HOME 'bin/vp.exe')) { throw 'The system-only attempt installed vp' }
$requests = @(Select-String -LiteralPath $stderr -Pattern 'GET /install-legacy.bin ' -SimpleMatch)
if ($requests.Count -ne 1) { throw 'The system-only attempt fetched the legacy script' }

& $powershell -NoProfile -Command '& ([scriptblock]::Create((irm -TimeoutSec 15 $env:VP_TEST_INSTALLER_URL)))'
if ($LASTEXITCODE -ne 0) { throw "Remote install failed with exit code $LASTEXITCODE" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ steps = [
{ argv = ["vp", "not-a-command"], comment = "Once marked, the binary dispatches commands normally", continue-on-failure = true },
]

[[case]]
name = "command_self_setup_system_only_probe"
vp = "global"
env = { VP_SKIP_DEPS_INSTALL = "1" }
steps = [
{ argv = ["vpt", "rm", "$VP_HOME/current/bin/.vp-setup-complete"], snapshot = false },
{ argv = ["vp", "--help"], envs = [["VP_SELF_SETUP_SUPPORT_CHECK", "system-only"]], comment = "The system-only check value adds a second line after the unchanged v1 response" },
{ argv = ["vp", "--help"], envs = [["VP_SELF_SETUP_SUPPORT_CHECK", "System-Only"]], comment = "Only the exact lowercase value asks for system-only support" },
["vpt", "stat-file", "$VP_HOME/current/bin/.vp-setup-complete", "--assert", "missing"],
]

[[case]]
name = "command_self_setup_handoff"
vp = "global"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# command_self_setup_system_only_probe

## `vpt rm $VP_HOME/current/bin/.vp-setup-complete`


## `VP_SELF_SETUP_SUPPORT_CHECK=system-only vp --help`

The system-only check value adds a second line after the unchanged v1 response

```
vite-plus-self-setup-v1
vite-plus-self-setup-system-only
```

## `VP_SELF_SETUP_SUPPORT_CHECK=System-Only vp --help`

Only the exact lowercase value asks for system-only support

```
vite-plus-self-setup-v1
```

## `vpt stat-file $VP_HOME/current/bin/.vp-setup-complete --assert missing`

```
<home>/.vite-plus/current/bin/.vp-setup-complete: missing
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Run the pnpm shim and report whether it selected the system pnpm or a managed
// release. The managed launcher's own output is platform-specific, so it is not
// printed.
const { spawnSync } = require('node:child_process');
const { existsSync } = require('node:fs');
const path = require('node:path');

const result = spawnSync('pnpm', ['--version'], {
encoding: 'utf8',
shell: process.platform === 'win32',
timeout: 120000,
});
console.log(`system pnpm: ${String(result.stdout).includes('system pnpm')}`);
const managed = path.join(process.env.VP_HOME, 'package_manager', 'pnpm', '10.18.0');
console.log(`managed pnpm 10.18.0 installed: ${existsSync(managed)}`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Report the managed installs that the system-only steps could use or create.
// The case's js_runtime links the runner's shared seed, whose versions differ
// between machines, so only the relevant versions are checked.
const { existsSync, readdirSync } = require('node:fs');
const path = require('node:path');

const list = (directory) => (existsSync(directory) ? readdirSync(directory).sort() : []);
const runtimes = path.join(process.env.VP_HOME, 'js_runtime', 'node');
// Snapshots redact a version after a tool name, so the lines name its source.
const versions = { '.node-version pin': '20.18.0', 'VP_NODE_VERSION override': '24.0.0' };
for (const [source, version] of Object.entries(versions)) {
console.log(`managed Node.js (${source}): ${existsSync(path.join(runtimes, version))}`);
}
const managers = path.join(process.env.VP_HOME, 'package_manager');
for (const family of list(managers)) {
console.log(`package_manager/${family}: ${list(path.join(managers, family)).join(', ')}`);
}
if (list(managers).length === 0) console.log('package_manager: (empty)');
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Fake system npm and pnpm beside system-bin/node. Each reports its name, its
// arguments, and whether the shim left PATH unchanged (EXPECTED_PATH).
const { chmodSync, writeFileSync } = require('node:fs');
const path = require('node:path');

for (const name of ['npm', 'pnpm']) {
if (process.platform === 'win32') {
writeFileSync(
path.join('system-bin', `${name}.cmd`),
[
'@echo off',
`echo system ${name} %*`,
'if "%PATH%"=="%EXPECTED_PATH%" (echo same PATH) else (echo changed PATH)',
'',
].join('\r\n'),
);
} else {
const file = path.join('system-bin', name);
writeFileSync(
file,
[
'#!/bin/sh',
`echo "system ${name} $*"`,
'if [ "$PATH" = "$EXPECTED_PATH" ]; then echo "same PATH"; else echo "changed PATH"; fi',
'',
].join('\n'),
);
chmodSync(file, 0o755);
}
}
Loading
Loading