Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .github/workflows/deploy-docs-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ on:
- 'docs/**'
- 'packages/cli/install.sh'
- 'packages/cli/install.ps1'
- 'packages/cli/legacy_install.sh'
- 'packages/cli/legacy_install.ps1'
- '.github/workflows/deploy-docs-preview.yml'

concurrency:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ on:
- 'docs/**'
- 'packages/cli/install.sh'
- 'packages/cli/install.ps1'
- 'packages/cli/legacy_install.sh'
- 'packages/cli/legacy_install.ps1'
- '.github/workflows/deploy-docs.yml'
workflow_dispatch:

Expand Down
80 changes: 80 additions & 0 deletions .github/workflows/publish-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,86 @@ jobs:
# and lets the comment job read the size via `docker manifest inspect`.
provenance: false

# End-to-end the current install.sh against this PR's bridge build: the
# script targets the split XDG layout and carries no legacy compatibility,
# so it can only be validated against a CLI that supports it. The legacy
# installers are covered against the released CLI by
# test-standalone-install.yml. Same-repo only, like the bridge registration
# (fork PRs get no admin token, so no bridge build exists).
test-install-split:
if: >-
github.repository == 'voidzero-dev/vite-plus' &&
contains(github.event.pull_request.labels.*.name, 'preview-build')
name: Test install.sh (split layout, preview build)
needs: publish
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2

- name: Run install.sh against the bridge build
run: cat packages/cli/install.sh | VP_PR_VERSION=${{ github.event.pull_request.number }} bash

- name: Verify split layout
run: |
[ -x "$HOME/.local/bin/vp" ] || { echo "::error::vp shim missing at ~/.local/bin/vp"; exit 1; }
[ -d "$HOME/.local/share/vite-plus/current" ] || { echo "::error::versions missing from the data dir"; exit 1; }
[ -f "$HOME/.config/vite-plus/env" ] || { echo "::error::env script missing from the config dir"; exit 1; }
[ ! -e "$HOME/.vite-plus" ] || { echo "::error::legacy root must not be created by a split install"; exit 1; }
"$HOME/.local/bin/vp" --version

- name: Implode split install
run: |
"$HOME/.local/bin/vp" implode -y
[ ! -e "$HOME/.local/share/vite-plus" ] || { echo "::error::data dir remains after implode"; exit 1; }
[ ! -e "$HOME/.config/vite-plus" ] || { echo "::error::config dir remains after implode"; exit 1; }
# Shared ~/.local/bin must not be removed; only the vp shim.
[ ! -e "$HOME/.local/bin/vp" ] || { echo "::error::vp shim remains after implode"; exit 1; }

# Upgrade path: existing ~/.vite-plus must keep the legacy root when the new
# install.sh runs (grandfathering). Same preview-build gate as split install.
test-install-legacy-upgrade:
if: >-
github.repository == 'voidzero-dev/vite-plus' &&
contains(github.event.pull_request.labels.*.name, 'preview-build')
name: Test install.sh (legacy upgrade, preview build)
needs: publish
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
permissions:
contents: read
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2

- name: Seed empty legacy root
run: |
mkdir -p "$HOME/.vite-plus/bin"
# Presence of the root is enough for install.sh LEGACY_LAYOUT=true.

- name: Run install.sh against the bridge build
run: cat packages/cli/install.sh | VP_PR_VERSION=${{ github.event.pull_request.number }} bash

- name: Verify legacy layout preserved
run: |
[ -x "$HOME/.vite-plus/bin/vp" ] || { echo "::error::vp missing at ~/.vite-plus/bin/vp"; exit 1; }
[ -d "$HOME/.vite-plus/current" ] || { echo "::error::current missing under legacy root"; exit 1; }
[ -f "$HOME/.vite-plus/env" ] || { echo "::error::env script should live under legacy root"; exit 1; }
[ ! -e "$HOME/.local/share/vite-plus" ] || { echo "::error::split data dir must not be created on legacy upgrade"; exit 1; }
"$HOME/.vite-plus/bin/vp" --version

- name: Implode legacy install
run: |
"$HOME/.vite-plus/bin/vp" implode -y
[ ! -e "$HOME/.vite-plus" ] || { echo "::error::legacy root remains after implode"; exit 1; }

# Post (or update) a single sticky PR comment with the preview image tag after
# it publishes. Re-runs reuse the same comment via the hidden marker instead of
# creating a new one.
Expand Down
29 changes: 19 additions & 10 deletions .github/workflows/test-standalone-install.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ permissions: {}
on:
workflow_dispatch:
pull_request:
# Note: these jobs install the *released* CLI, which predates the split
# XDG layout, so they exercise the frozen legacy installers
# (legacy_install.sh / legacy_install.ps1). The current install.sh /
# install.ps1 target the split layout and are covered by the
# preview-build e2e in publish-preview.yml. The minimum-release-age job
# stays on install.ps1 because the gate lives in the current script and
# blocks before layout matters.
paths:
- 'packages/cli/install.sh'
- 'packages/cli/install.ps1'
- 'packages/cli/legacy_install.sh'
- 'packages/cli/legacy_install.ps1'
- 'crates/vp_installer/**'
- 'crates/vp_pm_cli/**'
- 'crates/vp_setup/**'
Expand Down Expand Up @@ -41,7 +50,7 @@ jobs:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2

- name: Run install.sh
run: cat packages/cli/install.sh | bash
run: cat packages/cli/legacy_install.sh | bash

- name: Verify installation
working-directory: ${{ runner.temp }}
Expand Down Expand Up @@ -130,7 +139,7 @@ jobs:

- name: Run install.sh
run: |
output=$(cat packages/cli/install.sh | bash 2>&1) || {
output=$(cat packages/cli/legacy_install.sh | bash 2>&1) || {
echo "$output"
echo "Install script exited with non-zero status"
exit 1
Expand Down Expand Up @@ -169,7 +178,7 @@ jobs:
ubuntu:20.04 bash -c "
ls -al ~/
apt-get update && apt-get install -y curl ca-certificates
cat /workspace/packages/cli/install.sh | bash
cat /workspace/packages/cli/legacy_install.sh | bash
if [ -f ~/.profile ]; then
source ~/.profile
elif [ -f ~/.bashrc ]; then
Expand Down Expand Up @@ -228,7 +237,7 @@ jobs:
alpine:3.21 sh -c "
# libstdc++: required by unofficial-builds Node.js musl binary
apk add --no-cache bash curl ca-certificates libstdc++
cat /workspace/packages/cli/install.sh | bash
cat /workspace/packages/cli/legacy_install.sh | bash
export PATH=\"\$HOME/.vite-plus/bin:\$PATH\"

vp --version
Expand Down Expand Up @@ -285,7 +294,7 @@ jobs:
alpine:3.21 sh -c "
# libstdc++ is needed by unofficial-builds Node.js musl binary
apk add --no-cache bash curl ca-certificates libstdc++
cat /workspace/packages/cli/install.sh | bash
cat /workspace/packages/cli/legacy_install.sh | bash
export PATH=\"\$HOME/.vite-plus/bin:\$PATH\"

vp --version
Expand Down Expand Up @@ -350,13 +359,13 @@ jobs:
- name: Run install.ps1
shell: powershell
run: |
& ./packages/cli/install.ps1
& ./packages/cli/legacy_install.ps1

- name: Run install.ps1 via irm simulation (catches BOM issues)
shell: powershell
run: |
$ErrorActionPreference = "Stop"
Get-Content ./packages/cli/install.ps1 -Raw | Invoke-Expression
Get-Content ./packages/cli/legacy_install.ps1 -Raw | Invoke-Expression

- name: Set PATH
shell: bash
Expand Down Expand Up @@ -423,7 +432,7 @@ jobs:
- name: Run install.ps1
shell: pwsh
run: |
& ./packages/cli/install.ps1
& ./packages/cli/legacy_install.ps1

- name: Set PATH
shell: bash
Expand Down Expand Up @@ -514,7 +523,7 @@ jobs:
- name: Run install.ps1 via iex under PowerShell 7.6
shell: pwsh
run: |
& $env:PWSH76 -NoProfile -Command "Get-Content ./packages/cli/install.ps1 -Raw | Invoke-Expression"
& $env:PWSH76 -NoProfile -Command "Get-Content ./packages/cli/legacy_install.ps1 -Raw | Invoke-Expression"

- name: Set PATH
shell: bash
Expand Down Expand Up @@ -667,7 +676,7 @@ jobs:
- name: Run install.ps1
shell: pwsh
run: |
& ./packages/cli/install.ps1
& ./packages/cli/legacy_install.ps1

- name: Set PATH
shell: bash
Expand Down
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ vite-plus/
└── crates/vp_trampoline/ # Windows shim trampoline
```

On-disk paths (bin, data, cache, and derived helpers) are resolved centrally via `vp_shared::VpDirs` (`crates/vp_shared/src/dirs.rs`, strategy chain in `dirs/resolution.rs`) — legacy monolithic `~/.vite-plus` root or split XDG/platform layout; no call site constructs `~/.vite-plus/...` or reads `XDG_*` itself.

`packages/test` is no longer tracked. The public test API is `vite-plus/test*`, generated by `packages/cli/build.ts` as shims over upstream `vitest` and `@vitest/browser*` exports.

## Where to Start
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pnpm bootstrap-cli
vp --version
```

This builds all packages, compiles the Rust `vp` binary, and installs the CLI to `~/.vite-plus`.
This builds all packages, compiles the Rust `vp` binary, and installs the CLI to `~/.vite-plus` (the legacy monolithic layout; on-disk paths are resolved by `vp_shared::VpDirs` in `crates/vp_shared/src/dirs.rs`).

To switch back to a release version, use `vp upgrade --force` (`current` points to `local-dev-*` but the binary version may still match the release, so `--force` is needed)

Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ sugar_path = { version = "3", features = ["cached_current_dir"] }
supports-color = "3"
syn = { version = "2", default-features = false }
tar = "0.4.43"
temp-env = "0.3.6"
tempfile = "3.14.0"
terminal_size = "0.4.2"
test-log = { version = "0.2.18", features = ["trace"] }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import fs from 'node:fs';
import path from 'node:path';

const expected = path.resolve('external/vp');
// Shims of a legacy install are relative links into its own current/bin/vp.
const expected = path.join('..', 'current', 'bin', 'vp');

for (const shim of ['vp', 'node', 'npm', 'npx', 'corepack', 'vpx', 'vpr']) {
const shimPath = path.join('home', 'bin', shim);
const shimPath = path.join('external', 'bin', shim);
const target = fs.readlinkSync(shimPath);
if (target !== expected) {
throw new Error(`${shim} points to ${target}, expected ${expected}`);
}
}

console.log('all shims point to external vp');
console.log('all shims point to the external install');
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@ name = "command_env_setup_external_vp"
vp = "global"
skip-platforms = ["windows"]
steps = [
{ argv = ["vpt", "mkdir", "-p", "external", "home"], comment = "Prepare isolated external install and VP_HOME", snapshot = false },
{ argv = ["vpt", "cp", "$VP_HOME/bin/vp", "external/vp"], comment = "Simulate a Homebrew-style vp outside VP_HOME", snapshot = false },
{ argv = ["vpt", "chmod", "+x", "external/vp"], snapshot = false },
{ argv = ["vpt", "mkdir", "-p", "external/current/bin", "external/bin", "external/js_runtime/node/22.18.0/bin"], comment = "A second, complete legacy install outside the case home", snapshot = false },
{ argv = ["vpt", "cp", "$VP_HOME/current/bin/vp", "external/current/bin/vp"], comment = "The external install's vp binary", snapshot = false },
{ argv = ["vpt", "cp", "$VP_HOME/current/bin/vp", "external/bin/vp"], comment = "Marks the external layout as a legacy install for detection", snapshot = false },
{ argv = ["vpt", "chmod", "+x", "external/current/bin/vp"], snapshot = false },
{ argv = ["vpt", "chmod", "+x", "external/bin/vp"], snapshot = false },
{ argv = ["vpt", "write-file", ".node-version", "22.18.0\n"], comment = "Project Node.js version", snapshot = false },
{ argv = ["vpt", "write-file", "home/js_runtime/node/22.18.0/bin/node", "#!/bin/sh\necho vp-managed-node-22.18.0\n"], comment = "Preinstall managed Node runtime", snapshot = false },
{ argv = ["vpt", "chmod", "+x", "home/js_runtime/node/22.18.0/bin/node"], snapshot = false },
{ argv = ["./external/vp", "env", "setup"], envs = [["VP_HOME", "${workspace}/home"]], comment = "Setup shims from external vp", snapshot = false },
{ argv = ["vpt", "write-file", "external/js_runtime/node/22.18.0/bin/node", "#!/bin/sh\necho vp-managed-node-22.18.0\n"], comment = "Preinstall managed Node runtime", snapshot = false },
{ argv = ["vpt", "chmod", "+x", "external/js_runtime/node/22.18.0/bin/node"], snapshot = false },
# Clear the runner-injected VP_HOME (deprecated, but still the
# highest-priority layout rule) so env setup self-locates the external
# install instead of pinning the case home.
{ argv = ["./external/current/bin/vp", "env", "setup"], envs = [["VP_HOME", ""]], comment = "env setup targets the invoking install via self-location (no VP_HOME)", snapshot = false },
# The legacy step set VP_BYPASS to reach a system node, which the hermetic
# case PATH does not have; the node shim resolving the pinned 22.18.0 from
# the seeded runtime serves the same purpose (any node can run the asserts).
{ argv = ["node", "assert-shims.mjs"], comment = "Shims should point to external vp, not VP_HOME/current/bin/vp" },
{ argv = ["node", "-v"], envs = [["VP_HOME", "${workspace}/home"], ["PATH", "${workspace}/home/bin:${PATH}"]], comment = "node shim uses the project version" },
{ argv = ["node", "assert-shims.mjs"], comment = "Shims point to the external install's vp, not the case home's" },
{ argv = ["node", "-v"], envs = [["VP_HOME", ""], ["PATH", "${workspace}/external/bin:${PATH}"]], comment = "node shim uses the project version" },
]
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
# command_env_setup_external_vp

## `vpt mkdir -p external home`
## `vpt mkdir -p external/current/bin external/bin external/js_runtime/node/22.18.0/bin`

Prepare isolated external install and VP_HOME
A second, complete legacy install outside the case home


## `vpt cp $VP_HOME/bin/vp external/vp`
## `vpt cp $VP_HOME/current/bin/vp external/current/bin/vp`

Simulate a Homebrew-style vp outside VP_HOME
The external install's vp binary


## `vpt chmod +x external/vp`
## `vpt cp $VP_HOME/current/bin/vp external/bin/vp`

Marks the external layout as a legacy install for detection


## `vpt chmod +x external/current/bin/vp`


## `vpt chmod +x external/bin/vp`


## `vpt write-file .node-version '22.18.0
Expand All @@ -19,30 +27,30 @@ Simulate a Homebrew-style vp outside VP_HOME
Project Node.js version


## `vpt write-file home/js_runtime/node/22.18.0/bin/node '#'\!'/bin/sh
## `vpt write-file external/js_runtime/node/22.18.0/bin/node '#'\!'/bin/sh
echo vp-managed-node-22.18.0
'`

Preinstall managed Node runtime


## `vpt chmod +x home/js_runtime/node/22.18.0/bin/node`
## `vpt chmod +x external/js_runtime/node/22.18.0/bin/node`


## `VP_HOME=${workspace}/home ./external/vp env setup`
## `VP_HOME= ./external/current/bin/vp env setup`

Setup shims from external vp
env setup targets the invoking install via self-location (no VP_HOME)


## `node assert-shims.mjs`

Shims should point to external vp, not VP_HOME/current/bin/vp
Shims point to the external install's vp, not the case home's

```
all shims point to external vp
all shims point to the external install
```

## `VP_HOME=${workspace}/home PATH=${workspace}/home/bin:${PATH} node -v`
## `VP_HOME= PATH=${workspace}/external/bin:${PATH} node -v`

node shim uses the project version

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,14 @@ d="$(dirname "$(dirname "$(dirname "$0")")")"
__vp_shell=/bin/sh
[ -x "$__vp_shell" ] || __vp_shell=$(command -v sh)
if [ -n "${VP_HOME-}" ]; then
if [ -n "${VP_BIN_DIR-}" ]; then
__vp_bin="$VP_BIN_DIR"
elif [ -n "${VP_HOME-}" ]; then
__vp_bin="$VP_HOME/bin"
elif [ -n "${HOME-}" ]; then
elif [ -n "${HOME-}" ] && [ -d "$HOME/.vite-plus/bin" ]; then
__vp_bin="$HOME/.vite-plus/bin"
elif [ -n "${HOME-}" ]; then
__vp_bin="$HOME/.local/bin"
else
__vp_bin=""
fi
Expand Down
Loading