feat: XDG-compliant directory layout via unified Dirs resolution - #2346
Draft
forehalo wants to merge 5 commits into
Draft
feat: XDG-compliant directory layout via unified Dirs resolution#2346forehalo wants to merge 5 commits into
forehalo wants to merge 5 commits into
Conversation
✅ Deploy Preview for viteplus-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
forehalo
force-pushed
the
feat/dirs-path-resolution
branch
4 times, most recently
from
August 5, 2026 17:13
b8069a5 to
2f8be03
Compare
Add vp_shared::Dirs as the single owner of on-disk placement decisions. An internal DirsInner enum selects the layout once per resolution (first match wins): - Home (legacy monolithic root): VP_HOME is set, the vp binary self-locates at <root>/current/bin/vp, a legacy layout is found on PATH, or ~/.vite-plus exists on disk. Existing installs keep working untouched, byte-identical paths. - Custom (split XDG layout, fresh installs): each category resolves through its own VP_*_DIR override -> XDG_* -> platform-default chain (bin: VP_BIN_DIR -> XDG_BIN_HOME -> XDG_DATA_HOME/../bin -> ~/.local/bin, mirroring uv). Only VP_BIN_DIR/VP_DATA_DIR/VP_CACHE_DIR exist as dedicated overrides; config and state rely on XDG_CONFIG_HOME / XDG_STATE_HOME. Relative values are ignored per the spec. XDG_* names are defined alongside vp's own variables in env_vars.rs, but only vp_* path overrides live in EnvConfig; Dirs reads XDG vars itself behind an injectable, parallel-safe resolution core (no env mutation or serial_test anywhere in Dirs tests; test helpers live in a #[cfg(test)] impl block, and cross-crate tests sandbox through EnvConfig::for_test_with_home). Migrate every Rust call site to Dirs category accessors and named helpers, delete get_vp_home() and the home.rs module (folded into dirs.rs), and drop per-crate wrapper helpers so Dirs is the sole path source. bins/*.json metadata moves to the data category. On the TS side, the global CLI injects the resolved VP_BIN_DIR / VP_DATA_DIR / VP_CACHE_DIR into JS child processes under the split layout only (never overriding user-set vars); org-tarball.ts prefers VP_CACHE_DIR; generated git hook scripts fall back through VP_BIN_DIR, VP_HOME/bin, ~/.vite-plus/bin, ~/.local/bin. VP_HOME, the installers, and the generated env* shell scripts are unchanged in behavior: fresh installs still land in ~/.vite-plus, so the split layout is opt-in via VP_*_DIR/XDG until the follow-up stack removes the legacy variables and switches installer defaults. Groundwork for voidzero-dev#827.
Fresh installs now land in the split XDG layout: versions and the current symlink under the data dir (~/.local/share/vite-plus, %LOCALAPPDATA%\vite-plus\data), the vp wrapper and shims in the bin dir (~/.local/bin, %LOCALAPPDATA%\vite-plus\bin), and env scripts in the config dir (~/.config/vite-plus, %APPDATA%\vite-plus). install.sh, install.ps1, and the Dockerfile switch their defaults accordingly; explicit VP_HOME/--install-dir or an existing ~/.vite-plus keeps the legacy monolithic layout, so re-running the installer upgrades existing installs in place. VP_HOME becomes DEPRECATED_VP_HOME: still honored by the CLI as the highest-priority layout rule (legacy monolithic root) so older env scripts and custom-location installs keep working, and kept by the installers as an install-dir override — but the installers and the generated env* scripts no longer set it, and the Windows trampoline and vp_installer only export it for legacy-layout installs. This also gives an explicit escape hatch for layouts the detection heuristics cannot disambiguate (e.g. VP_BIN_DIR pointed inside the data dir). Layout detection learns to distinguish split installs: a <X>/current/bin/vp shape classifies as legacy when <X>/bin/vp exists, otherwise X is the split data dir and the data category is pinned to the running binary's payload. PATH inference canonicalizes vp entries through the same shape check. vp implode removes the split layout (data/config/state/cache dirs plus only the vp-owned shim names from the bin dir; a shared ~/.local/bin is never removed) and strips profile lines for both layouts. vp-use.cmd and the trampoline gain the split fallback path. Snapshot fixtures move off per-step VP_HOME: the runner provisions the complete legacy shape, and isolated-install cases use real on-disk layouts driven through their own binaries (with VP_HOME cleared so self-location governs). Docs describe the split layout as the fresh-install default and VP_HOME as deprecated.
…tallers Self-location never fired in production: canonicalizing current_exe resolves the current symlink, yielding <X>/<version>/bin/vp rather than the <X>/current/bin/vp shape the check required. Detection now accepts both shapes (install_parent_candidate) and validates the install on disk (classify_exe): <X>/bin/vp plus <X>/current/bin/vp means a legacy root, only the latter means a split data dir, neither means not an install. Verified end-to-end with a sandboxed split install (env scripts land in ~/.config/vite-plus, doctor pins the split data dir). CI compatibility: install.sh/install.ps1 are fetched independently of the CLI release line, so the released (pre-split) CLI must not be installed by the new split-only scripts. The pre-XDG scripts are preserved as frozen legacy_install.sh/legacy_install.ps1 for exactly that combination, and test-standalone-install.yml now runs them (job names unchanged for branch protection). The new scripts stay free of compatibility logic; split-layout e2e for them runs against the registry-bridge preview build in publish-preview.yml (same-repo PRs, like the bridge registration). The Docker image carries both candidate bin dirs on PATH and cleans both runtime locations so it works across the transition, and the docs site serves the legacy installers. Also gate PlatformDefaults::unix to non-Windows/test builds to fix the Windows cross-compile (-D warnings) dead-code error, and format docs with vp check --fix.
The test-install-split job landed between publish-docker-preview's env block and its steps, producing a duplicate steps key that failed the zizmor security analysis. Move it after the complete docker job.
test_uninstall_removes_shims_from_metadata and test_restore_previous_install_state_removes_partial_new_bins managed shims in <temp>/bin while the code under test removes them from Dirs::bin_dir(). Under the reworked for_test_with_home (user_home override) a fresh temp home resolves the split layout (<temp>/.local/bin), so the two never met. Unix masked the mismatch: the shim symlinks dangle, and dangling-symlink exists() is false, so the "removed" assertions passed vacuously. On Windows the trampoline .exe shims are real files, so Test (Windows) failed. Create <temp>/.vite-plus in both tests so resolution selects the legacy layout and bin_dir() equals the directory the tests manage.
forehalo
force-pushed
the
feat/dirs-path-resolution
branch
from
August 6, 2026 07:54
8fa20f7 to
08b7589
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Close #827
Summary
Makes vite-plus follow the XDG Base Directory specification for fresh installs while keeping every existing install working untouched. Two commits:
vp_shared::Dirs— a single owner of all on-disk placement decisions; all priority chains are internal details.VP_HOMEis deprecated but still honored.Layout resolution (first match wins)
VP_HOMEset (deprecated) → legacy monolithic layout. Still honored so older env scripts and custom-location installs keep working; also an explicit escape hatch for layouts the heuristics can't disambiguate.Executable self-location —
current_exematches<X>/current/bin/vp:<X>/bin/vpexists → legacy root; otherwiseXis a split install's data dir (data category pinned to the running binary's payload). Covers IDE/trampoline launches without PATH context.PATHinference — legacy sibling check, then canonicalizevpentries through the same shape check.~/.vite-plusexists → legacy layout, grandfathered; nothing is moved.Split XDG layout (fresh installs):
VP_BIN_DIR→XDG_BIN_HOME→XDG_DATA_HOME/../bin→~/.local/bin(uv's chain)XDG_CONFIG_HOME/vite-plus→~/.config/vite-plusbins/*.json)VP_DATA_DIR→XDG_DATA_HOME/vite-plus→~/.local/share/vite-plusXDG_STATE_HOME/vite-plus→~/.local/state/vite-plusVP_CACHE_DIR→XDG_CACHE_HOME/vite-plus→~/.cache/vite-plusOnly bin/data/cache get dedicated
VP_*_DIRoverrides (mirroring uv'sUV_TOOL_BIN_DIR/UV_PYTHON_INSTALL_DIR/UV_CACHE_DIR); config and state rely on the standard XDG vars. Relative values are ignored per the spec. Windows:%LOCALAPPDATA%\vite-plus\{bin,data,state,cache}, config under%APPDATA%\vite-plus.What changed
crates/vp_shared/src/dirs.rs(new, absorbshome.rs):Dirs+DirsInner(Home/Custom), pure injectable resolution core (no env mutation orserial_test), 5 category accessors, 12 named helpers,is_legacy_layout(). Test helpers live in a#[cfg(test)] implblock; cross-crate tests sandbox viaEnvConfig::for_test_with_home.VP_BIN_DIR/VP_DATA_DIR/VP_CACHE_DIR,DEPRECATED_VP_HOME, and theXDG_*_HOMEnames are constants inenv_vars.rs;EnvConfigcarries onlyvp_*path overrides —Dirsreads the XDG vars itself.get_vp_home()uses and hand-rolled$VP_HOME/...constructions across the workspace now go throughDirs; per-crate wrapper helpers deleted.install.sh/install.ps1/Dockerfile default to the split layout; explicitVP_HOME/--install-diror an existing~/.vite-pluskeeps legacy.vp_installerresolves anInstallLayoutfromDirs. The Windows trampoline andvp-use.cmdsupport both layouts (and only exportVP_HOMEfor legacy).vp imploderemoves either layout: split = data/config/state/cache dirs plus only vp-owned shim names from the bin dir (a shared~/.local/binis never removed); profile stripping handles both.VP_BIN_DIR/VP_DATA_DIR/VP_CACHE_DIRinto JS child processes under the split layout only;org-tarball.tsprefersVP_CACHE_DIR; git hook scripts fall back throughVP_BIN_DIR→VP_HOME/bin→~/.vite-plus/bin→~/.local/bin.installer-env-vars.md;env.md/install.md/implode.md/CONTRIBUTING.md/AGENTS.mdupdated; supersede note inrfcs/env-command.md.Migration impact
~/.vite-plusinstalls: zero impact (grandfathered; re-running the installer upgrades in place).VP_HOMEinstalls: keep working — the variable is still honored (deprecated), and the binary also self-locates without it.~/.local/binis the only PATH entry needed.Validation
cargo check/clippy --workspace --all-targets: zero warnings;cargo fmtcleancargo teston all touched crates: ~1500 tests green