Skip to content

Commit d44848f

Browse files
authored
feat(cli): add vp self-update command (#548)
feat(cli): add `vp self-update` command Implement `vp self-update` (alias: `vp upgrade`) that downloads and installs a new vp CLI version from the npm registry with SHA-512 integrity verification. Features: - Version resolution from npm registry (latest, specific version, or dist-tag) - Parallel download of platform binary + main JS package tarballs - SHA-512 SRI integrity verification for both tarballs - Atomic symlink swap on Unix, junction swap on Windows - Production dependency installation via new binary - Shim refresh after update - Automatic cleanup of old versions (keeps 5 most recent) - `--check` flag to check for updates without installing - `--rollback` to revert to previous version - `--force`, `--silent`, `--tag`, `--registry` flags - Path traversal protection in tarball extraction - Post-extraction validation of critical files test(cli): add snap tests for `vp self-update` command - Add self-update help to cli-helper-message snap test - Add command-self-update-check test (--check flag and upgrade alias) - Add command-self-update-rollback test (error when no previous version) - Rename help section to "Maintenance Commands" for consistency - Fix description alignment to match other sections
1 parent 6529a63 commit d44848f

34 files changed

Lines changed: 2151 additions & 177 deletions

File tree

.github/workflows/ci.yml

Lines changed: 212 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ jobs:
135135
- name: Build CLI
136136
run: |
137137
pnpm bootstrap-cli:ci
138-
echo "$HOME/.vite-plus-dev/bin" >> $GITHUB_PATH
138+
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
139139
140140
- name: Print help for built-in commands
141141
run: |
@@ -198,9 +198,9 @@ jobs:
198198
run: |
199199
pnpm bootstrap-cli:ci
200200
if [[ "$RUNNER_OS" == "Windows" ]]; then
201-
echo "$USERPROFILE\.vite-plus-dev\bin" >> $GITHUB_PATH
201+
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
202202
else
203-
echo "$HOME/.vite-plus-dev/bin" >> $GITHUB_PATH
203+
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
204204
fi
205205
206206
- name: Verify CLI installation
@@ -239,16 +239,16 @@ jobs:
239239
where.exe tsc
240240
241241
# Test 2: Verify the package was installed correctly
242-
Get-ChildItem "$env:USERPROFILE\.vite-plus-dev\packages\typescript\"
243-
Get-ChildItem "$env:USERPROFILE\.vite-plus-dev\bin\"
242+
Get-ChildItem "$env:USERPROFILE\.vite-plus\packages\typescript\"
243+
Get-ChildItem "$env:USERPROFILE\.vite-plus\bin\"
244244
245245
# Test 3: Uninstall
246246
vp uninstall -g typescript
247247
248248
# Test 4: Verify uninstall removed shim
249249
Write-Host "Checking bin dir after uninstall:"
250-
Get-ChildItem "$env:USERPROFILE\.vite-plus-dev\bin\"
251-
$shimPath = "$env:USERPROFILE\.vite-plus-dev\bin\tsc.cmd"
250+
Get-ChildItem "$env:USERPROFILE\.vite-plus\bin\"
251+
$shimPath = "$env:USERPROFILE\.vite-plus\bin\tsc.cmd"
252252
if (Test-Path $shimPath) {
253253
Write-Error "tsc shim file still exists at $shimPath"
254254
exit 1
@@ -285,23 +285,23 @@ jobs:
285285
where.exe tsc
286286
287287
REM Test 2: Verify the package was installed correctly
288-
dir "%USERPROFILE%\.vite-plus-dev\packages\typescript\"
289-
dir "%USERPROFILE%\.vite-plus-dev\bin\"
288+
dir "%USERPROFILE%\.vite-plus\packages\typescript\"
289+
dir "%USERPROFILE%\.vite-plus\bin\"
290290
291291
REM Test 3: Uninstall
292292
vp uninstall -g typescript
293293
294294
REM Test 4: Verify uninstall removed shim (.cmd wrapper)
295295
echo Checking bin dir after uninstall:
296-
dir "%USERPROFILE%\.vite-plus-dev\bin\"
297-
if exist "%USERPROFILE%\.vite-plus-dev\bin\tsc.cmd" (
296+
dir "%USERPROFILE%\.vite-plus\bin\"
297+
if exist "%USERPROFILE%\.vite-plus\bin\tsc.cmd" (
298298
echo Error: tsc.cmd shim file still exists
299299
exit /b 1
300300
)
301301
echo tsc.cmd shim removed successfully
302302
303303
REM Test 5: Verify shell script was also removed (for Git Bash)
304-
if exist "%USERPROFILE%\.vite-plus-dev\bin\tsc" (
304+
if exist "%USERPROFILE%\.vite-plus\bin\tsc" (
305305
echo Error: tsc shell script still exists
306306
exit /b 1
307307
)
@@ -317,8 +317,8 @@ jobs:
317317
- name: Test global package install (bash)
318318
run: |
319319
echo "PATH: $PATH"
320-
ls -la ~/.vite-plus-dev/
321-
ls -la ~/.vite-plus-dev/bin/
320+
ls -la ~/.vite-plus/
321+
ls -la ~/.vite-plus/bin/
322322
which node
323323
which npm
324324
which npx
@@ -331,17 +331,17 @@ jobs:
331331
which tsc
332332
333333
# Test 2: Verify the package was installed correctly
334-
ls -la ~/.vite-plus-dev/packages/typescript/
335-
ls -la ~/.vite-plus-dev/bin/
334+
ls -la ~/.vite-plus/packages/typescript/
335+
ls -la ~/.vite-plus/bin/
336336
337337
# Test 3: Uninstall
338338
vp uninstall -g typescript
339339
340340
# Test 4: Verify uninstall removed shim
341341
echo "Checking bin dir after uninstall:"
342-
ls -la ~/.vite-plus-dev/bin/
343-
if [ -f ~/.vite-plus-dev/bin/tsc ]; then
344-
echo "Error: tsc shim file still exists at ~/.vite-plus-dev/bin/tsc"
342+
ls -la ~/.vite-plus/bin/
343+
if [ -f ~/.vite-plus/bin/tsc ]; then
344+
echo "Error: tsc shim file still exists at ~/.vite-plus/bin/tsc"
345345
exit 1
346346
fi
347347
echo "tsc shim removed successfully"
@@ -361,6 +361,197 @@ jobs:
361361
RUST_BACKTRACE=1 pnpm test
362362
git diff --exit-code
363363
364+
cli-self-update:
365+
name: CLI self-update test
366+
needs:
367+
- download-previous-rolldown-binaries
368+
strategy:
369+
fail-fast: false
370+
matrix:
371+
include:
372+
- os: ubuntu-latest
373+
- os: namespace-profile-mac-default
374+
- os: windows-latest
375+
runs-on: ${{ matrix.os }}
376+
steps:
377+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
378+
- uses: ./.github/actions/clone
379+
380+
- name: Configure Git for access to vite-task
381+
run: git config --global url."https://x-access-token:${{ secrets.VITE_TASK_TOKEN }}@github.com/".insteadOf "ssh://git@github.com/"
382+
383+
- run: |
384+
brew install rustup
385+
rustup install stable
386+
echo "PATH=/opt/homebrew/opt/rustup/bin:$PATH" >> $GITHUB_ENV
387+
if: ${{ matrix.os == 'namespace-profile-mac-default' }}
388+
389+
- uses: oxc-project/setup-rust@d286d43bc1f606abbd98096666ff8be68c8d5f57 # v1.0.0
390+
with:
391+
save-cache: ${{ github.ref_name == 'main' }}
392+
cache-key: cli-self-update
393+
394+
- uses: oxc-project/setup-node@fdbf0dfd334c4e6d56ceeb77d91c76339c2a0885 # v1.0.4
395+
396+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
397+
with:
398+
name: rolldown-binaries
399+
path: ./rolldown/packages/rolldown/src
400+
merge-multiple: true
401+
402+
- name: Build with upstream
403+
uses: ./.github/actions/build-upstream
404+
with:
405+
target: ${{ matrix.os == 'ubuntu-latest' && 'x86_64-unknown-linux-gnu' || matrix.os == 'windows-latest' && 'x86_64-pc-windows-msvc' || 'aarch64-apple-darwin' }}
406+
407+
- name: Build CLI
408+
run: |
409+
pnpm bootstrap-cli:ci
410+
if [[ "$RUNNER_OS" == "Windows" ]]; then
411+
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
412+
else
413+
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
414+
fi
415+
416+
- name: Verify CLI installation
417+
run: |
418+
which vp
419+
vp --version
420+
421+
- name: Test self-update (bash)
422+
shell: bash
423+
run: |
424+
# Helper to read the installed CLI version from package.json
425+
get_cli_version() {
426+
node -p "require(require('path').resolve(process.env.USERPROFILE || process.env.HOME, '.vite-plus', 'current', 'package.json')).version"
427+
}
428+
429+
# Save initial (dev build) version
430+
INITIAL_VERSION=$(get_cli_version)
431+
echo "Initial version: $INITIAL_VERSION"
432+
433+
# --check queries npm registry and prints update status
434+
vp self-update --check
435+
# upgrade alias should also work
436+
vp upgrade --check
437+
438+
# full self-update: download, extract, swap
439+
vp self-update --tag test --force
440+
vp --version
441+
vp env doctor
442+
443+
ls -la ~/.vite-plus/
444+
445+
# Verify version changed after update
446+
UPDATED_VERSION=$(get_cli_version)
447+
echo "Updated version: $UPDATED_VERSION"
448+
if [ "$UPDATED_VERSION" == "$INITIAL_VERSION" ]; then
449+
echo "Error: version should have changed after self-update (still $INITIAL_VERSION)"
450+
exit 1
451+
fi
452+
453+
# rollback to the previous version
454+
vp self-update --rollback
455+
vp --version
456+
vp env doctor
457+
458+
# Verify version restored after rollback
459+
ROLLBACK_VERSION=$(get_cli_version)
460+
echo "Rollback version: $ROLLBACK_VERSION"
461+
if [ "$ROLLBACK_VERSION" != "$INITIAL_VERSION" ]; then
462+
echo "Error: version should have been restored after rollback (expected $INITIAL_VERSION, got $ROLLBACK_VERSION)"
463+
exit 1
464+
fi
465+
466+
- name: Test self-update (powershell)
467+
if: matrix.os == 'windows-latest'
468+
shell: pwsh
469+
run: |
470+
Get-ChildItem "$env:USERPROFILE\.vite-plus\"
471+
472+
# Helper to read the installed CLI version from package.json
473+
function Get-CliVersion {
474+
node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'package.json')).version"
475+
}
476+
477+
# Save initial (dev build) version
478+
$initialVersion = Get-CliVersion
479+
Write-Host "Initial version: $initialVersion"
480+
481+
# --check queries npm registry and prints update status
482+
vp self-update --check
483+
# upgrade alias should also work
484+
vp upgrade --check
485+
486+
# full self-update: download, extract, swap
487+
vp self-update --tag test --force
488+
vp --version
489+
vp env doctor
490+
491+
Get-ChildItem "$env:USERPROFILE\.vite-plus\"
492+
493+
# Verify version changed after update
494+
$updatedVersion = Get-CliVersion
495+
Write-Host "Updated version: $updatedVersion"
496+
if ($updatedVersion -eq $initialVersion) {
497+
Write-Error "Error: version should have changed after self-update (still $initialVersion)"
498+
exit 1
499+
}
500+
501+
# rollback to the previous version
502+
vp self-update --rollback
503+
vp --version
504+
vp env doctor
505+
506+
# Verify version restored after rollback
507+
$rollbackVersion = Get-CliVersion
508+
Write-Host "Rollback version: $rollbackVersion"
509+
if ($rollbackVersion -ne $initialVersion) {
510+
Write-Error "Error: version should have been restored after rollback (expected $initialVersion, got $rollbackVersion)"
511+
exit 1
512+
}
513+
514+
- name: Test self-update (cmd)
515+
if: matrix.os == 'windows-latest'
516+
shell: cmd
517+
run: |
518+
REM Save initial (dev build) version
519+
for /f "usebackq delims=" %%v in (`node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'package.json')).version"`) do set INITIAL_VERSION=%%v
520+
echo Initial version: %INITIAL_VERSION%
521+
522+
REM --check queries npm registry and prints update status
523+
vp self-update --check
524+
REM upgrade alias should also work
525+
vp upgrade --check
526+
527+
REM full self-update: download, extract, swap
528+
vp self-update --tag test --force
529+
vp --version
530+
vp env doctor
531+
532+
dir "%USERPROFILE%\.vite-plus\"
533+
534+
REM Verify version changed after update
535+
for /f "usebackq delims=" %%v in (`node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'package.json')).version"`) do set UPDATED_VERSION=%%v
536+
echo Updated version: %UPDATED_VERSION%
537+
if "%UPDATED_VERSION%"=="%INITIAL_VERSION%" (
538+
echo Error: version should have changed after self-update, still %INITIAL_VERSION%
539+
exit /b 1
540+
)
541+
542+
REM rollback to the previous version
543+
vp self-update --rollback
544+
vp --version
545+
vp env doctor
546+
547+
REM Verify version restored after rollback
548+
for /f "usebackq delims=" %%v in (`node -p "require(require('path').resolve(process.env.USERPROFILE, '.vite-plus', 'current', 'package.json')).version"`) do set ROLLBACK_VERSION=%%v
549+
echo Rollback version: %ROLLBACK_VERSION%
550+
if not "%ROLLBACK_VERSION%"=="%INITIAL_VERSION%" (
551+
echo Error: version should have been restored after rollback, expected %INITIAL_VERSION%, got %ROLLBACK_VERSION%
552+
exit /b 1
553+
)
554+
364555
install-e2e-test:
365556
name: Local CLI `vite install` E2E test
366557
needs:
@@ -398,7 +589,7 @@ jobs:
398589
- name: Build CLI
399590
run: |
400591
pnpm bootstrap-cli:ci
401-
echo "$HOME/.vite-plus-dev/bin" >> $GITHUB_PATH
592+
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
402593
403594
- name: Run local CLI `vite install`
404595
run: |
@@ -440,6 +631,7 @@ jobs:
440631
- lint
441632
- run
442633
- cli-e2e-test
634+
- cli-self-update
443635
steps:
444636
- run: exit 1
445637
# Thank you, next https://github.com/vercel/next.js/blob/canary/.github/workflows/build_and_test.yml#L379

.github/workflows/e2e-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,8 @@ jobs:
266266
- name: Install vp CLI
267267
shell: bash
268268
run: |
269-
node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts vp --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-cli-0.0.0.tgz
270-
echo "$HOME/.vite-plus-dev/bin" >> $GITHUB_PATH
269+
node $GITHUB_WORKSPACE/packages/tools/src/install-global-cli.ts --tgz $GITHUB_WORKSPACE/tmp/tgz/vite-plus-cli-0.0.0.tgz
270+
echo "$HOME/.vite-plus/bin" >> $GITHUB_PATH
271271
272272
- name: Migrate in ${{ matrix.project.name }}
273273
working-directory: ${{ runner.temp }}/vite-plus-ecosystem-ci/${{ matrix.project.name }}${{ matrix.project.directory && format('/{0}', matrix.project.directory) || '' }}

.github/workflows/test-standalone-install.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,16 @@ jobs:
103103
which npx
104104
which vp
105105
106+
- name: Verify self-update
107+
run: |
108+
# --check queries npm registry and prints update status
109+
vp self-update --check
110+
vp self-update 0.0.0-b356849c.20260207-0631
111+
vp --version
112+
# rollback to the previous version (should succeed after a real update)
113+
vp self-update --rollback
114+
vp --version
115+
106116
test-install-sh-arm64:
107117
name: Test install.sh (Linux ARM64 glibc via QEMU)
108118
runs-on: ubuntu-latest
@@ -155,6 +165,13 @@ jobs:
155165
export VITE_LOG=trace
156166
vp env run --node 24 -- node -p \"process.versions\"
157167
168+
# Verify self-update
169+
vp self-update --check
170+
vp self-update 0.0.0-b356849c.20260207-0631
171+
vp --version
172+
vp self-update --rollback
173+
vp --version
174+
158175
# FIXME: qemu: uncaught target signal 11 (Segmentation fault) - core dumped
159176
# vp new create-vite --no-interactive --no-agent -- hello --no-interactive -t vanilla
160177
# cd hello && vp run build
@@ -180,6 +197,17 @@ jobs:
180197
run: |
181198
echo "$USERPROFILE\.vite-plus\bin" >> $GITHUB_PATH
182199
200+
- name: Verify self-update
201+
shell: pwsh
202+
run: |
203+
# --check queries npm registry and prints update status
204+
vp self-update --check
205+
vp self-update 0.0.0-b356849c.20260207-0631
206+
vp --version
207+
# rollback to the previous version (should succeed after a real update)
208+
vp self-update --rollback
209+
vp --version
210+
183211
- name: Verify installation on powershell
184212
shell: pwsh
185213
working-directory: ${{ runner.temp }}
@@ -229,6 +257,7 @@ jobs:
229257
working-directory: ${{ runner.temp }}
230258
run: |
231259
echo PATH: %PATH%
260+
dir "%USERPROFILE%\.vite-plus"
232261
dir "%USERPROFILE%\.vite-plus\bin"
233262
234263
REM test new command

0 commit comments

Comments
 (0)