Skip to content

Commit def89c2

Browse files
committed
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 c96f980 commit def89c2

8 files changed

Lines changed: 79 additions & 13 deletions

File tree

crates/vite_global_cli/src/cli.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,9 +1602,6 @@ fn apply_custom_help(cmd: clap::Command) -> clap::Command {
16021602
{bold}run{reset} Run tasks
16031603
{bold}env{reset} Manage Node.js versions
16041604
1605-
{bold_underline}Self-Management:{reset}
1606-
{bold}self-update, upgrade{reset} Update vp itself to the latest version
1607-
16081605
{bold_underline}Package Manager Commands:{reset}
16091606
{bold}install, i{reset} Install all dependencies, or add packages if package names are provided
16101607
{bold}add{reset} Add packages to dependencies
@@ -1619,6 +1616,9 @@ fn apply_custom_help(cmd: clap::Command) -> clap::Command {
16191616
{bold}unlink{reset} Unlink packages
16201617
{bold}update, up{reset} Update packages to their latest versions
16211618
{bold}why, explain{reset} Show why a package is installed
1619+
1620+
{bold_underline}Maintenance Commands:{reset}
1621+
{bold}self-update, upgrade{reset} Update vp itself to the latest version
16221622
"
16231623
);
16241624
let help_template = format!(

packages/global/snap-tests/cli-helper-message/snap.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ Package Manager Commands:
3131
update, up Update packages to their latest versions
3232
why, explain Show why a package is installed
3333

34+
Maintenance Commands:
35+
self-update, upgrade Update vp itself to the latest version
36+
3437
Options:
3538
-V, --version Print version
3639
-h, --help Print help
@@ -336,3 +339,20 @@ Global Packages:
336339
vp uninstall -g <package> # Uninstall a global package
337340
vp update -g [package] # Update global package(s)
338341
vp list -g [package] # List installed global packages
342+
343+
> vp self-update -h # show self-update help message
344+
Update vp itself to the latest version
345+
346+
Usage: vp self-update [OPTIONS] [VERSION]
347+
348+
Arguments:
349+
[VERSION] Target version (e.g., "0.2.0"). Defaults to latest
350+
351+
Options:
352+
--tag <TAG> npm dist-tag to install (default: "latest", also: "test") [default: latest]
353+
--check Check for updates without installing
354+
--rollback Revert to the previously active version
355+
--force Force reinstall even if already on the target version
356+
--silent Suppress output
357+
--registry <REGISTRY> Custom npm registry URL
358+
-h, --help Print help

packages/global/snap-tests/cli-helper-message/steps.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"vp why -h # show why help message",
1414
"vp info -h # show info help message",
1515
"vp pm -h # show pm help message",
16-
"vp env # show env help message"
16+
"vp env # show env help message",
17+
"vp self-update -h # show self-update help message"
1718
]
1819
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
> vp self-update --check # check for updates without installing
2+
info: checking for updates...
3+
info: found vite-plus-cli@<semver>
4+
Update available: <semver> → <semver>
5+
Run `vp self-update` to update.
6+
7+
> vp upgrade --check # check using upgrade alias
8+
info: checking for updates...
9+
info: found vite-plus-cli@<semver>
10+
Update available: <semver> → <semver>
11+
Run `vp self-update` to update.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"ignoredPlatforms": ["win32"],
3+
"commands": [
4+
"vp self-update --check # check for updates without installing",
5+
"vp upgrade --check # check using upgrade alias"
6+
]
7+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[1]> vp self-update --rollback # should fail with no previous version
2+
Error: Self-update error: No previous version found. Cannot rollback.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"ignoredPlatforms": ["win32"],
3+
"commands": ["vp self-update --rollback # should fail with no previous version"]
4+
}

rfcs/self-update-command.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ GET {registry}/vite-plus-cli/{version_or_tag}
180180
- Default to `latest`
181181

182182
Parse the JSON response to extract:
183+
183184
- `version`: the resolved semver version
184185
- `optionalDependencies`: to find the platform-specific package name
185186

@@ -212,6 +213,7 @@ Download two tarballs from the npm registry:
212213
```
213214

214215
Verification flow:
216+
215217
1. Download tarball to temp file
216218
2. Compute SHA-512 hash of the downloaded file
217219
3. Base64-encode and compare against `integrity` field (format: `sha512-{base64}`)
@@ -241,10 +243,12 @@ fn verify_integrity(data: &[u8], expected: &str) -> Result<(), Error> {
241243
```
242244

243245
To get the `integrity` field for the platform package, we need to query its metadata separately:
246+
244247
- Main package metadata: `{registry}/vite-plus-cli/{version}` → contains `dist.integrity`
245248
- Platform package metadata: `{registry}/@voidzero-dev/vite-plus-cli-{suffix}/{version}` → contains `dist.integrity`
246249

247250
Platform detection reuses existing logic from `vite_js_runtime` or mirrors the bash script's approach:
251+
248252
- `uname -s` → os (darwin, linux)
249253
- `uname -m` → arch (x64, arm64)
250254
- Linux: detect gnu vs musl libc
@@ -287,6 +291,7 @@ junction::create(version_dir, &current_link)?;
287291
```
288292

289293
Key differences on Windows:
294+
290295
- **Junctions** (`mklink /J`) are used instead of symlinks — junctions don't require admin privileges
291296
- Junctions only work for directories (which `current` is), and use absolute paths internally
292297
- The swap is **not atomic** — there's a brief window (~milliseconds) where `current` doesn't exist
@@ -311,6 +316,7 @@ The running `vp` process is **not** the binary being replaced. The flow is:
311316
```
312317

313318
After the `current` link swap, any **new** invocation of `vp` will use the new binary. The currently running process continues to execute from the old version's binary file on disk:
319+
314320
- **Unix**: The old binary remains valid because Unix doesn't delete open files until all file descriptors are closed
315321
- **Windows**: The old `.exe` file is locked while running, but since we install to a **new version directory** (not overwriting in-place), there's no conflict. The old version directory is preserved (kept in the "last 5" cleanup policy)
316322

@@ -319,26 +325,28 @@ After the `current` link swap, any **new** invocation of `vp` will use the new b
319325
The `--rollback` flag switches the `current` symlink to the previously active version.
320326

321327
To track the previous version, we can:
328+
322329
1. Read the `current` symlink target before updating
323330
2. After the update, write the previous version to `~/.vite-plus/.previous-version`
324331

325332
For `--rollback`:
333+
326334
1. Read `~/.vite-plus/.previous-version`
327335
2. Verify that version directory still exists
328336
3. Swap `current` symlink to point to it
329337
4. Update `.previous-version` to point to the version we just rolled back from
330338

331339
### Error Handling
332340

333-
| Error | Recovery |
334-
|-------|----------|
335-
| Network failure during download | Clean up partial temp files, exit with helpful message |
336-
| Integrity mismatch (SHA-512) | Delete downloaded file, report expected vs actual hash, abort |
337-
| Corrupted tarball | Verify extraction success, clean up version dir if partial |
338-
| `vp install` fails | Remove the version dir, keep current version unchanged |
339-
| Disk full | Detect and report, clean up partial state |
340-
| Permission denied | Report with suggestion to check directory ownership |
341-
| Registry returns error | Parse npm error JSON, show human-readable message |
341+
| Error | Recovery |
342+
| ------------------------------- | ------------------------------------------------------------- |
343+
| Network failure during download | Clean up partial temp files, exit with helpful message |
344+
| Integrity mismatch (SHA-512) | Delete downloaded file, report expected vs actual hash, abort |
345+
| Corrupted tarball | Verify extraction success, clean up version dir if partial |
346+
| `vp install` fails | Remove the version dir, keep current version unchanged |
347+
| Disk full | Detect and report, clean up partial state |
348+
| Permission denied | Report with suggestion to check directory ownership |
349+
| Registry returns error | Parse npm error JSON, show human-readable message |
342350

343351
Key principle: **The `current` symlink is only swapped after all steps succeed.** If any step fails, the existing installation is untouched.
344352

@@ -444,10 +452,12 @@ SelfUpdate {
444452
**Decision**: Use `vp self-update` (with hyphen).
445453

446454
**Alternatives considered**:
455+
447456
- `vp upgrade` — used by Deno, Bun, proto; shorter but ambiguous with `vp update` (packages)
448457
- `vp self upgrade` — used by rustup (`rustup self update`); requires subcommand group
449458

450459
**Rationale**:
460+
451461
- Matches pnpm (`pnpm self-update`) and mise (`mise self-update`) conventions
452462
- Zero ambiguity with `vp update` (which updates npm packages)
453463
- The hyphen is consistent with `list-remote` in `vp env`
@@ -459,6 +469,7 @@ SelfUpdate {
459469
**Decision**: Implement the update logic entirely in Rust.
460470

461471
**Rationale**:
472+
462473
- No dependency on bash or curl being installed
463474
- Better error handling and progress reporting
464475
- Consistent behavior across platforms
@@ -469,6 +480,7 @@ SelfUpdate {
469480
**Decision**: Download tarballs from the same npm registry used by `install.sh`.
470481

471482
**Rationale**:
483+
472484
- No new infrastructure needed
473485
- Same release pipeline, same artifacts
474486
- Supports custom registries and mirrors via `--registry` or `NPM_CONFIG_REGISTRY`
@@ -479,6 +491,7 @@ SelfUpdate {
479491
**Decision**: Do not check for updates on every `vp` invocation.
480492

481493
**Rationale**:
494+
482495
- Avoids unexpected network requests that slow down commands
483496
- Avoids privacy concerns (phoning home on every run)
484497
- Users can opt into periodic checks via their own cron/launchd if desired
@@ -489,6 +502,7 @@ SelfUpdate {
489502
**Decision**: Maintain the same cleanup policy as `install.sh` (keep 5 most recent versions).
490503

491504
**Rationale**:
505+
492506
- Consistent with existing behavior
493507
- Provides rollback safety net without unbounded disk usage
494508
- Each version is ~20-30MB, so 5 versions is ~100-150MB total
@@ -498,6 +512,7 @@ SelfUpdate {
498512
### Phase 0 (P0): Core Self-Update
499513

500514
**Scope:**
515+
501516
- `vp self-update` — downloads and installs the latest version
502517
- `vp self-update <version>` — installs a specific version
503518
- `--tag`, `--force`, `--silent` flags
@@ -506,6 +521,7 @@ SelfUpdate {
506521
- Error handling with clean rollback
507522

508523
**Files to create/modify:**
524+
509525
- `crates/vite_global_cli/src/commands/self_update/mod.rs` (new)
510526
- `crates/vite_global_cli/src/commands/self_update/registry.rs` (new)
511527
- `crates/vite_global_cli/src/commands/self_update/platform.rs` (new)
@@ -515,6 +531,7 @@ SelfUpdate {
515531
- `crates/vite_global_cli/src/cli.rs` (add command variant + routing)
516532

517533
**Success Criteria:**
534+
518535
- [ ] `vp self-update` downloads and installs the latest version
519536
- [ ] `vp self-update 0.x.y` installs a specific version
520537
- [ ] Downloaded tarballs are verified against npm registry `integrity` (SHA-512)
@@ -526,21 +543,25 @@ SelfUpdate {
526543
### Phase 1 (P1): Rollback and Check
527544

528545
**Scope:**
546+
529547
- `--rollback` flag with `.previous-version` tracking
530548
- `--check` flag for update availability check
531549

532550
**Success Criteria:**
551+
533552
- [ ] `vp self-update --rollback` reverts to previous version
534553
- [ ] `vp self-update --check` shows available update without installing
535554

536555
### Phase 2 (P2): Enhanced UX
537556

538557
**Scope:**
558+
539559
- Progress bar for downloads (using `indicatif` or similar)
540560
- Release notes URL in update success message
541561
- `--registry` flag for custom npm registry
542562

543563
**Success Criteria:**
564+
544565
- [ ] Download progress is visible for large binaries
545566
- [ ] Release notes link is shown after successful update
546567

0 commit comments

Comments
 (0)