Skip to content

fix: avoid locked exe on forced upgrade#1714

Merged
fengmk2 merged 5 commits into
mainfrom
fix-upgrade-force-windows
Jun 1, 2026
Merged

fix: avoid locked exe on forced upgrade#1714
fengmk2 merged 5 commits into
mainfrom
fix-upgrade-force-windows

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented May 30, 2026

Summary

  • Install forced reinstalls of the active version into a unique semver build-metadata directory
  • Repoint current after the install completes so Windows does not overwrite the running vp.exe
  • Add focused unit coverage for forced reinstall directory selection

Directory structure

Before this change, vp upgrade --force reused the active version directory when the target version matched the current version:

~/.vite-plus/
├── current -> 0.1.23
└── 0.1.23/
    └── bin/
        └── vp.exe   # currently running on Windows

That meant the forced reinstall extracted the downloaded platform package back into 0.1.23/bin/vp.exe, which fails on Windows because the running executable is locked.

After this change, a forced reinstall of the active version writes into a new semver-compatible build-metadata directory, then repoints current only after installation succeeds:

~/.vite-plus/
├── current -> 0.1.23+force.<pid>.<timestamp>
├── 0.1.23/
│   └── bin/
│       └── vp.exe   # previous active install remains untouched
└── 0.1.23+force.<pid>.<timestamp>/
    └── bin/
        └── vp.exe   # freshly installed binary

Normal upgrades to a different version still use the plain target version directory, for example 0.1.24/.

Verification

  • Reproduced the original Windows vp upgrade --force failure with os error 32
  • cargo build -p vite_global_cli
  • Temp VP_HOME end-to-end locked-executable test: upgrade --force succeeds
  • cargo test -p vite_global_cli commands::upgrade::tests
  • cargo fmt --check

Note

Medium Risk
Touches the global CLI upgrade/install swap path and directory cleanup; wrong naming or cleanup could break upgrades or rollbacks, but behavior is scoped to forced same-version reinstalls with new unit and Windows E2E coverage.

Overview
Fixes Windows vp upgrade --force when the target version is already active: the upgrade no longer extracts into the running install folder (which locks vp.exe and caused os error 32).

Upgrade flow now picks an install directory name via target_install_dir_name. For a forced reinstall of the active version, it uses a unique semver build-metadata path like {version}+force.{pid}.{nanos}; otherwise it still uses the plain version folder. current, rollback metadata, and old-version cleanup follow that directory name (not only the semver string).

CI adds a Windows-only E2E step that simulates a locked same-version install under a temp VP_HOME and asserts current moves to a +force. dir, the original version tree stays, and .previous-version is correct.

Tests cover forced-dir selection in upgrade and that cleanup_old_versions treats +force dirs like other semver installs (removable when old, kept when protected).

Reviewed by Cursor Bugbot for commit cc5393e. Configure here.

@netlify
Copy link
Copy Markdown

netlify Bot commented May 30, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit 8d23be7
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a1ce12d5c178d0008b87f4e

@fengmk2 fengmk2 self-assigned this May 30, 2026
@fengmk2 fengmk2 force-pushed the fix-upgrade-force-windows branch 3 times, most recently from 1e65175 to debeadd Compare May 30, 2026 08:27
@fengmk2 fengmk2 force-pushed the fix-upgrade-force-windows branch from debeadd to cc5393e Compare May 30, 2026 08:33
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented May 30, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit cc5393e. Configure here.

fengmk2 added 3 commits May 30, 2026 22:56
Mirror the Windows-only same-version forced-upgrade check as a bash step
gated to non-Windows runners. In-place overwrite of the running binary
does not hard-fail on macOS or modern Linux the way it does on Windows,
but the fix still changes the on-disk layout on every platform, so assert
current repoints at a <version>+force.* dir, the original version tree
survives, and .previous-version is recorded.
Address code-review findings on the forced-reinstall fix:

- Hoist target_install_dir_name / is_install_dir_for_version into
  vite_setup::install so vp-setup recognizes a `<version>+force.*` dir
  as already-installed instead of re-downloading (the installer's
  same-version check previously used `==` and missed forced-reinstall
  dirs). Co-locates the helpers with cleanup_old_versions and documents
  the contract that the forced dir name stays semver-parseable so it is
  still garbage-collected.
- CI: resolve the published `latest` via `npm view` instead of parsing
  the local build version, which can be ahead of the registry
  mid-release-cycle and would make `vp upgrade <version>` fail to
  download. Drops the `vp --version | sed | head` parse (head-under-
  pipefail footgun) and guards the temp dir with ${RUNNER_TEMP:?}.
@fengmk2 fengmk2 marked this pull request as ready for review June 1, 2026 01:21
@fengmk2 fengmk2 requested review from cpojer and wan9chi June 1, 2026 01:21
@fengmk2 fengmk2 merged commit e1b4495 into main Jun 1, 2026
53 checks passed
@fengmk2 fengmk2 deleted the fix-upgrade-force-windows branch June 1, 2026 02:01
@fengmk2 fengmk2 mentioned this pull request Jun 1, 2026
fengmk2 added a commit that referenced this pull request Jun 1, 2026
Release vite-plus v0.1.24.

A new `vp pm stage` publishing workflow, hardened installs and upgrades,
a Node-version mismatch reinstall prompt, and the bundled
vite/vitest/tsdown stack moves forward.

### Features

- `vp pm stage`: a new `vp pm` subcommand exposing npm's
staged-publishing workflow (upload a build to a staging area without
2FA, then approve or reject it from a trusted device); it maps to `pnpm
stage` / `npm stage` / `yarn npm ... --staged` per package manager, with
an npm fallback for yarn Classic and bun
([#1715](#1715)), by
@fengmk2
- `vp`: prompt to reinstall when up-to-date global packages were built
against a different Node.js than the active one (defaults to no); adds
`--reinstall-node-mismatch` and `--ignore-node-mismatch`, and skips the
prompt in CI
([#1666](#1666)), by
@liangmiQwQ
- `vp format`: add `format` as a visible alias of `vp fmt`, so the
common slip `vp format` resolves correctly and `vp format --init` /
`--migrate` apply the same `vite.config.ts` wiring as `vp fmt`
([#1727](#1727)), by
@semimikoh

### Fixes & Enhancements

- `vp install` / Node runtime download: HTTP retries now wrap the whole
body stream, hash verification, and archive extraction (not just the
request headers), so truncated or corrupt downloads of package managers
and Node are re-fetched instead of failing on the first attempt
([#1719](#1719)), by
@fengmk2
- `vp upgrade --force` on Windows: install into a fresh directory before
repointing `current`, so the forced reinstall no longer fails trying to
overwrite the running `vp.exe`
([#1714](#1714)), by
@fengmk2
- `vp install -g`: install global packages directly into their final
prefix instead of a temp dir that gets moved, so packages whose
postinstall scripts bake in absolute or relative temp paths still
resolve their bins; a failed package in a multi-package install no
longer removes the shims of the ones that already succeeded
([#1698](#1698)), by
@liangmiQwQ
- `vp why`: remove the `-g` / `--global` flag, which delegated to the
package manager's global mode and ignored Vite+-managed global packages;
`vp why` stays project-scoped while `vp outdated -g` keeps using the
managed global flow
([#1720](#1720)), by
@liangmiQwQ
- Windows installer: remove the existing `current` link via PowerShell
(detecting junctions, symlinks, and stale directories) instead of `cmd
/c rmdir`, which could fail with "The directory is not empty"
([#1726](#1726)), by
@TheAlexLichter
- `vp create`: skip editor-config detection and package-local editor
settings by default when creating a project inside an existing monorepo;
`--editor <name>` stays an explicit opt-in and `--no-editor` an opt-out
([#1729](#1729)), by
@jong-kyung
- `vp create vite:monorepo` (pnpm): keep the aliased `vite`/`vitest` in
the website app's `package.json` so the workspace `overrides.vite:
catalog:` has a direct consumer and `vp why vite` resolves to
`@voidzero-dev/vite-plus-core`; npm/yarn/bun still drop the dead-weight
keys ([#1728](#1728)), by
@fengmk2
- `vp pack`: rewrite direct `createRequire(...)("picomatch")` calls in
bundled tsdown output to the local bundled CJS entry, so packing no
longer depends on an undeclared runtime `picomatch` under pnpm `hoist:
false` ([#1732](#1732)),
by @fengmk2
- `vp migrate`: resolve a `catalog:` husky pin from the workspace
catalog (`pnpm-workspace.yaml`, `.yarnrc.yml`, or `package.json`
catalogs) during the git-hooks preflight, so a compatible catalog-pinned
husky no longer triggers a false "could not determine husky version"
warning and skips hook setup
([#1710](#1710)), by
@fengmk2

### Docs

- Add a **Copy Prompt** button to the docs site that copies an
AI-friendly getting-started prompt (intro, `llms-full.txt` pointer,
install commands, and core `vp` commands) for handing straight to a
coding agent
([#1706](#1706)), by
@fengmk2
- Update `troubleshooting.md`: `vite.config.ts` related issues are
resolved by updating oxlint and oxfmt
([#1708](#1708)), by
@leaysgur
- Clarify the product and repository documentation locations and the new
Run guide/config paths in `AGENTS.md`
([#1707](#1707)), by
@leaysgur

### Chore

- `vp` install: reduce retained `vp` versions from 5 to 3 across the
installer, `vp upgrade`, and the shell/PowerShell bootstrap scripts
(active and previous versions stay protected for rollback); document the
3-version retention and `vp upgrade --rollback`
([#1716](#1716)), by
@fengmk2
- Exclude the snap-tests directory from Vitest config discovery so the
VS Code Vitest extension stops generating a stray
`.vitest-plugin-loaded` file
([#1723](#1723)), by
@liangmiQwQ
- Refresh trusted stack stats on the docs homepage
([#1734](#1734)), by
@voidzero-guard[bot]
- Update @wan9chi's GitHub handle (formerly `branchseer`)
([#1705](#1705)), by
@wan9chi
- Update GitHub Actions
([#1724](#1724),
[#1730](#1730)), by
@renovate[bot]
- Upgrade upstream dependencies: vite `8.0.14 → 8.0.16`, vitest `4.1.7 →
4.1.8`, tsdown `0.22.0 → 0.22.1`, `@vitejs/devtools` `0.2.0 → 0.3.1`
([#1713](#1713),
[#1735](#1735),
[#1737](#1737)), by
@voidzero-guard[bot]

### Bundled Versions

| Tool | Version | Source |
| --- | --- | --- |
| vite | `8.0.16` |
[`f94df87`](vitejs/vite@f94df87)
|
| rolldown | `1.0.3` |
[`a287faa`](rolldown/rolldown@a287faa)
|
| tsdown | `0.22.1` | [npm](https://npmx.dev/package/tsdown/v/0.22.1) |
| vitest | `4.1.8` | [npm](https://npmx.dev/package/vitest/v/4.1.8) |
| oxlint | `1.67.0` | [npm](https://npmx.dev/package/oxlint/v/1.67.0) |
| oxlint-tsgolint | `0.23.0` |
[npm](https://npmx.dev/package/oxlint-tsgolint/v/0.23.0) |
| oxfmt | `0.52.0` | [npm](https://npmx.dev/package/oxfmt/v/0.52.0) |

### New Contributors

Welcome to our new contributor @semimikoh! 🎉

**Full Changelog**:
v0.1.23...v0.1.24

Merging this PR will trigger the release workflow.

---------

Co-authored-by: voidzero-guard[bot] <278573678+voidzero-guard[bot]@users.noreply.github.com>
Co-authored-by: MK <fengmk2@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants