Skip to content

feat(installer): add standalone Windows .exe installer (vp-setup.exe)#1293

Draft
fengmk2 wants to merge 39 commits intomainfrom
windows-installer
Draft

feat(installer): add standalone Windows .exe installer (vp-setup.exe)#1293
fengmk2 wants to merge 39 commits intomainfrom
windows-installer

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented Apr 4, 2026

Add a standalone vp-setup.exe Windows installer binary that installs
the vp CLI without requiring PowerShell, complementing the existing
irm https://vite.plus/ps1 | iex script-based installer.

  • Create vite_setup shared library crate extracting installation logic
    (platform detection, registry queries, integrity verification, tarball
    extraction, symlink/junction management) from vite_global_cli
  • Create vite_installer binary crate producing vp-setup.exe with
    interactive prompts, silent mode (-y), progress bars, and Windows PATH
    modification via direct registry API (no PowerShell dependency)
  • Update vite_global_cli to use vite_setup instead of inline upgrade
    modules, ensuring upgrade and installer share identical logic
  • Add CI build/upload steps for installer in release workflow, attached
    as GitHub Release assets
  • Add RFC document at rfcs/windows-installer.md

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 4, 2026

Deploy Preview for viteplus-preview ready!

Name Link
🔨 Latest commit a8c95ab
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/69d1f6dbf2277900095994f1
😎 Deploy Preview https://deploy-preview-1293--viteplus-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@fengmk2 fengmk2 self-assigned this Apr 4, 2026
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 4, 2026


How to use the Graphite Merge Queue

Add the label auto-merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@fengmk2 fengmk2 force-pushed the windows-installer branch from 1b3a6a3 to 4dc36b5 Compare April 4, 2026 12:59
fn main() {
// On Windows, set DEPENDENTLOADFLAG to only search system32 for DLLs at load time.
// This prevents DLL hijacking when the installer is downloaded to a folder
// containing malicious DLLs (e.g. Downloads). Matches rustup's approach.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@fengmk2 fengmk2 force-pushed the windows-installer branch from 672909f to 816d7f1 Compare April 4, 2026 15:07

- name: Install via vp-setup.exe (silent)
shell: pwsh
run: ${{ format('{0}/target/release/vp-setup.exe', env.DEV_DRIVE) }}

### Phase 2: Direct Download URL

Host at `https://vite.plus/vp-setup.exe` with architecture auto-detection (default x64).
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

should use void worker to handle the architecture detect logic.


## Code Signing

Windows Defender SmartScreen flags unsigned executables downloaded from the internet. This is a significant UX problem for a download-and-run installer.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

image.png

Need to resolve the signature issue

Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 5, 2026

image.png

Double-click execution effect

fengmk2 added 14 commits April 5, 2026 13:20
Add a standalone `vp-setup.exe` Windows installer binary that installs
the vp CLI without requiring PowerShell, complementing the existing
`irm https://vite.plus/ps1 | iex` script-based installer.

- Create `vite_setup` shared library crate extracting installation logic
  (platform detection, registry queries, integrity verification, tarball
  extraction, symlink/junction management) from `vite_global_cli`
- Create `vite_installer` binary crate producing `vp-setup.exe` with
  interactive prompts, silent mode (-y), progress bars, and Windows PATH
  modification via direct registry API (no PowerShell dependency)
- Update `vite_global_cli` to use `vite_setup` instead of inline upgrade
  modules, ensuring upgrade and installer share identical logic
- Add CI build/upload steps for installer in release workflow, attached
  as GitHub Release assets
- Add RFC document at rfcs/windows-installer.md
- Remove dirs_home() and dead fallback in resolve_install_dir(), use
  get_vp_home() directly
- Resolve install dir once in run() and pass through to all functions
- Extract read_current_version() into vite_setup as a public function,
  reuse in save_previous_version() and the installer
- Merge Cli/Options structs into single Options struct in cli.rs
- Extract replace_windows_exe() helper to eliminate copy-paste in
  setup_bin_shims()
- Remove unused LPWSTR type alias from windows_path.rs
- Remove excessive "Step N" comments
- Fix mixed path separator in interactive menu display
- Add interactive "Customize installation" submenu (option 2) allowing
  users to change version, registry, Node.js manager, and PATH settings
- Add env file creation via `vp env setup --env-only` when Node.js
  manager is skipped (ensures shell env files exist in all code paths)
- Add build.rs with /DEPENDENTLOADFLAG:0x800 linker flag for DLL
  hijacking prevention at load time (complements runtime mitigation)
- Add test-vp-setup-exe CI job to test-standalone-install.yml testing
  silent installation from cmd, pwsh, and bash on Windows
The junction crate was moved to vite_setup when extracting the shared
installation logic. cargo-shear correctly flagged it as unused.
Replace the flat numbered list with a phased ASCII diagram grouped
into Resolve, Download & Verify, Install, Activate, and Configure
phases. Add a function-to-crate mapping table and document the
failure recovery boundary (pre/post Activate phase). Annotate
conditional steps (save_previous_version only on upgrade,
modify PATH gated by --no-modify-path).
- Update status from "Draft" to "Implemented"
- Fix Code Sharing table: all install functions are in vite_setup::install,
  not separate submodules
- Fix Dependency Graph: remove junction (indirect via vite_setup), add
  actual deps (vite_path, owo-colors)
- Fix Customization submenu to match code (numbered items, no install dir)
- Replace winreg code sample with raw FFI description (matches implementation)
- Replace windows_sys DLL sample with raw FFI (matches implementation)
- Remove winreg from Binary Size Budget, add raw FFI note
- Fix Alternatives #4: raw FFI, not winreg
- Fix CI snippets: rename vite-init to vp-setup, update test workflow
  to match actual test-vp-setup-exe job
- Mark Implementation Phases 1-3 as done, Phase 4 as future
…ccess

The zero-dependency pattern made sense for vite_trampoline (copied 5-10
times as shim files) but not for a single downloadable installer where
readability matters more. Switch from 225 lines of unsafe raw Win32 FFI
to ~80 lines of safe Rust using the winreg crate (~50-100 KB after LTO).

WM_SETTINGCHANGE broadcast still uses a single raw FFI call since
winreg doesn't wrap SendMessageTimeoutW.
- Extract VP_BINARY_NAME constant to avoid duplicated cfg!(windows)
  binary name literals
- Remove redundant exists() check in replace_windows_exe — just attempt
  the rename unconditionally (rename failure is already ignored)
- Trim verbose WHAT-comments to concise single-line doc comments
Fix all high/P1/P2 issues from both adversarial and standard reviews:

1. Node.js manager auto-detect: port the full auto-detect logic from
   install.ps1/install.sh instead of unconditionally enabling shims.
   Checks VP_NODE_MANAGER env, existing shims, CI/devcontainer, system
   node availability — and prompts interactively when system node exists
   (matching install.ps1 behavior). Silent mode (-y) skips the prompt
   and does not enable shims when system node is present.

2. Same-version repair: when the target version is already installed,
   skip download/extract/deps but still run all post-activation setup
   (shims, Node.js manager, PATH, env files). This allows rerunning
   the installer to repair a broken installation.

3. Rollback protection: include the previous version in protected_versions
   during cleanup, matching the vp upgrade implementation. Prevents
   cleanup from deleting the rollback target.

4. Post-activation best-effort: setup_bin_shims, refresh_shims, and
   modify_path are now wrapped in if-let-Err with warnings instead of
   propagating errors. After activation (current junction swap), the
   core install has succeeded — configuration failures should not cause
   exit code 1.
- Move same-version check before platform package HTTP request: use
  resolve_version_string (1 HTTP call) first, then skip
  resolve_platform_package (2nd HTTP call) when version matches.
  Saves 1 HTTP request for tag matches, both for exact version matches.
- Fix else { if let → else if let (clippy collapsible_else_if)
- Consolidate VP_NODE_MANAGER handling: both "yes" and "no" now checked
  in should_enable_node_manager instead of split across cli.rs and main.rs
- Make create_env_files return Result and report via print_warn,
  consistent with other best-effort post-activation steps
Move all user prompts to the menu phase — do_install is now prompt-free.

The auto_detect_node_manager() function is pure logic (no I/O) that
resolves the Node.js manager default based on: VP_NODE_MANAGER env,
existing shims, CI/devcontainer, system node presence. The result is
stored in opts.no_node_manager before showing the interactive menu,
so the user sees the resolved value ("enabled"/"disabled") and can
toggle it in the customize submenu before installation begins.

When system node is present and no other signal overrides, the default
is "disabled" — the user can enable it via the customize menu.
In silent mode (-y), this means shims are not created unless explicitly
requested via VP_NODE_MANAGER=yes or auto-detected (CI, no system node).
…tive mode

When system node is present:
- Interactive mode: default to enabled (matching install.ps1's Y/n
  prompt where Enter = yes). User can disable via customize menu.
- Silent mode (-y): default to disabled (don't silently take over).

This matches install.ps1 behavior where most interactive users who
press Enter get node management enabled by default.
fengmk2 added 24 commits April 5, 2026 13:20
Replace dynamic matrix.shell in shell: field with explicit shell values
per step. The matrix.shell expression was not recognized by the GitHub
Actions YAML parser when used in the shell: context. Use a single job
that verifies installation from pwsh, cmd, and bash sequentially.
- Update interactive menu examples to show "enabled"/"disabled" instead
  of "auto-detect" (value is now pre-computed)
- Add Node.js Manager Auto-Detection section with priority table
  documenting the full auto-detect logic matching install.ps1/install.sh
- Restructure Installation Flow diagram: local version check before HTTP,
  split resolve_version into resolve_version_string + resolve_platform_package,
  same-version repair path skips to CONFIGURE, cleanup moved to ACTIVATE
- Update Existing Installation Handling: same version now repairs instead
  of exiting early
- Document best-effort post-activation behavior and failure recovery
- Update function mapping table with split registry functions
- Update Test Workflow snippet to match actual single-job structure
- Update Phase 2 description (winreg, pre-computed node manager, repair)
This function follows the same pattern as refresh_shims (spawn vp with
different args) and belongs in the shared library alongside it.
… builds

Use setup-dev-drive with ReFS (matching ci.yml) to speed up cargo build.
Windows Defender skips ReFS dev drives, which significantly reduces
build times on GitHub Actions Windows runners.
- Extract VP_BINARY_NAME const to vite_setup::lib.rs, replacing 5
  inline cfg!(windows) expressions across vite_setup, vite_installer,
  and vite_global_cli
- Fix create_env_files to check exit code and log warning on failure,
  matching refresh_shims behavior
- Fix else { if let → else if let (clippy collapsible_else_if)
When double-clicked, the console window closes immediately after
installation finishes. Users never see the success/error message.
Add a 'Press Enter to close...' prompt in interactive mode so users
can read the output. Silent mode (-y, CI) exits immediately.
@fengmk2 fengmk2 force-pushed the windows-installer branch from 4fe6fd3 to a87b42c Compare April 5, 2026 05:23
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 5, 2026

Manual Testing Plan: vp-setup.exe Windows Installer

Prerequisites

  • Windows 10/11 machine (x64 or ARM64)
  • Build vp-setup.exe locally or download from the PR's CI artifacts
  • Clean state: no existing ~/.vite-plus directory (back up and remove if present)

1. Fresh Install — Double-Click

  • 1.1 Download vp-setup.exe to Downloads folder → Browser shows SmartScreen warning
  • 1.2 Click "..." → "Keep" → "Keep anyway" → File saved
  • 1.3 Double-click vp-setup.exe → Console window opens, interactive menu displayed
  • 1.4 Press Enter (default: Proceed) → Installation runs, success message shown
  • 1.5 Verify "Press Enter to close..." prompt → Window stays open until Enter pressed
  • 1.6 Open a new terminal, run vp --version → Version printed
  • 1.7 Run vp --help → Help output shown

2. Fresh Install — Shell Invocations

  • 2.1 Run from PowerShell: .\vp-setup.exe → Interactive menu shown
  • 2.2 Run from cmd.exe: vp-setup.exe → Interactive menu shown
  • 2.3 Run from Git Bash: ./vp-setup.exe → Interactive menu shown
  • 2.4 Run with -y: vp-setup.exe -y → No prompts, installs directly, exits without pause

3. Interactive Menu — Customize

  • 3.1 At main menu, press 2 (Customize) → Customize submenu shown with 4 options
  • 3.2 Press 1, type 0.1.14-alpha.1, Enter → Version changes to 0.1.14-alpha.1
  • 3.3 Press 3 to toggle Node.js manager → Toggles between "enabled" / "disabled"
  • 3.4 Press 4 to toggle Modify PATH → Toggles between "yes" / "no"
  • 3.5 Press Enter to go back → Main menu shows updated values
  • 3.6 Press 3 (Cancel) → "Installation cancelled." printed

4. Node.js Manager Auto-Detection

  • 4.1 System has Node.js installed, run interactively → Node.js manager defaults to "enabled"
  • 4.2 System has Node.js installed, run with -y → Node.js manager defaults to "disabled" (no shim takeover)
  • 4.3 Set VP_NODE_MANAGER=no, run → Node.js manager disabled regardless of mode
  • 4.4 Set VP_NODE_MANAGER=yes, run with -y → Node.js manager enabled even in silent mode

5. Same-Version Repair

  • 5.1 With vp already installed, run vp-setup.exe -y → Prints "already installed, verifying setup...", skips download
  • 5.2 Delete ~/.vite-plus/bin/vp.exe, run vp-setup.exe -y → Repairs: recreates bin shim, vp --version works after
  • 5.3 Remove ~/.vite-plus/bin from User PATH, run vp-setup.exe -y → Repairs: PATH re-added

6. Upgrade Path

  • 6.1 Install an older version: vp-setup.exe -y --version 0.1.14-alpha.1 → Installs specified version
  • 6.2 Run vp-setup.exe -y (no version flag) → Upgrades to latest, prints "upgrading from X to Y"
  • 6.3 Run vp upgrade --rollback → Rolls back to the previous version

7. Post-Installation Verification

  • 7.1 Check ~/.vite-plus/current → Junction points to installed version dir
  • 7.2 Check ~/.vite-plus/bin/vp.exe → Exists (trampoline shim)
  • 7.3 Check User PATH ($env:Path) → Contains ~/.vite-plus/bin
  • 7.4 Run vp env doctor → No errors
  • 7.5 Run vp create vite -- test-app --no-interactive -t vanilla → Project created successfully
  • 7.6 cd test-app && vp run build → Build succeeds

8. Environment Variables

  • 8.1 Set VP_VERSION=0.1.14-alpha.1, run vp-setup.exe -y → Installs specified version
  • 8.2 Set VP_HOME=C:\custom-dir, run vp-setup.exe -y → Installs to custom directory
  • 8.3 Set CI=true, run vp-setup.exe (no -y) → Runs non-interactively (no prompts)

9. Error Handling

  • 9.1 Run with --version nonexistent-version → Error message, exit code 1
  • 9.2 Run with no internet connection → Error message about network/registry
  • 9.3 Run with --no-modify-path → Installs successfully, PATH not modified

@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 5, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8c95ab916

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let target_version =
registry::resolve_version_string(version_or_tag, opts.registry.as_deref()).await?;

let same_version = current_version.as_deref() == Some(target_version.as_str());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reinstall when same-version install is missing binaries

do_install currently treats a version match as sufficient to skip the download/install phases, but that check only compares the current link name and never verifies the installed artifacts are actually present. In a partial/corrupt install (for example current/bin/vp.exe or vp-shim.exe removed), this path reports success after best-effort setup while leaving vp unusable. Please gate the same-version fast path on required binary existence (or fall back to full reinstall when artifacts are missing).

Useful? React with 👍 / 👎.

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