Skip to content

fix(run): match environment variable names by platform rules - #747

Merged
wan9chi merged 2 commits into
mainfrom
fix/windows-env-name-comparison
Sep 24, 2026
Merged

wan9chi merged 2 commits into
mainfrom
fix/windows-env-name-comparison

Conversation

@wan9chi

@wan9chi wan9chi commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Motivation

Environment variable names ignore ASCII case on Windows, but the env maps used to plan, spawn, and serve tasks compared names exactly:

  • For task command assignments such as Foo=a FOO=b, map iteration order decided which value a task saw, and an assignment could fail to replace an inherited variable spelled differently.
  • The FORCE_COLOR fallback added a second entry next to a differently spelled parent value.
  • VP_RUN_CONCURRENCY_LIMIT and runner getEnv lookups missed differently spelled variables.
  • PATH lookups and prefix queries handled case by hand in several places.

Summary

  • Add vt_casefold. Folded<S, R> compares a string by the case rule R while keeping its original spelling. EnvName<S> uses the platform's rule (ASCII letters ignore case on Windows; exact elsewhere), and EnvName<OsStr> is the borrowed form for map lookups.
  • Key the env maps in vt_shell, vt_plan, vt, vt_server, and fspy::Command by EnvName, replacing the manual case handling.
  • When an assignment overrides a variable spelled differently, the task gets the assignment's spelling, and its fingerprint entry replaces the one recorded under the other spelling. Cache keys and fingerprints still store plain strings.
  • TaskParsedCommand drops its unused wincode and Diff derives, which don't support EnvName keys.
  • Session::init_with, ScriptCommand.envs, SyntheticPlanRequest.envs, SpawnCommand.spawn_envs, and get_path_env now use EnvName keys; callers depend on vt_casefold directly.

Non-ASCII letters still compare exactly, as documented on EnvNameCaseRule.

@wan9chi
wan9chi added this pull request to stack #748 September 22, 2026 15:38
@wan9chi wan9chi changed the title fix/windows env name comparison fix(run): ignore ASCII case in Windows environment overrides Sep 22, 2026
@wan9chi
wan9chi force-pushed the fix/windows-env-name-comparison branch from 666eba1 to 32ae3e1 Compare September 22, 2026 15:40
@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

fspy benchmark

linux

dynamic/launch             change  -1.38%  [-11.56% .. +12.32%]  overhead  +306.75%
dynamic/access             change  +1.61%  [ -3.93% .. +10.70%]  overhead   +20.44%
dynamic/access-relative    change  +0.29%  [ -5.28% .. +15.90%]  overhead   +65.45%
dynamic/access-contended   change  -7.70%  [-21.13% .. +15.67%]  overhead    +4.08%
static/launch              change  +0.32%  [-13.05% .. +14.11%]  overhead  +697.97%
static/access              change  +0.94%  [ -9.31% ..  +8.22%]  overhead +1402.77%
static/access-relative     change  -0.39%  [-15.31% ..  +7.74%]  overhead +1829.09%
static/access-contended    change  -1.15%  [-11.14% ..  +7.72%]  overhead +1220.23%

macos

dynamic/launch             change  +0.07%  [ -3.08% ..  +2.57%]  overhead  +214.20%
dynamic/access             change  -0.37%  [ -3.63% ..  +3.37%]  overhead    +2.23%
dynamic/access-relative    change  +0.42%  [ -3.68% ..  +3.36%]  overhead  +253.14%
dynamic/access-contended   change  -2.01%  [-15.47% ..  +7.55%]  overhead    +4.66%

windows

dynamic/launch             change  -1.43%  [ -9.35% ..  +9.38%]  overhead   +23.28%
dynamic/access             change  +0.18%  [ -1.82% ..  +2.86%]  overhead    +1.70%
dynamic/access-relative    change  +0.18%  [ -1.09% ..  +1.62%]  overhead    +1.83%
dynamic/access-contended   change  +0.11%  [ -9.08% ..  +4.10%]  overhead    +3.26%

@wan9chi
wan9chi removed this pull request from stack #748 September 23, 2026 06:58
wan9chi and others added 2 commits September 24, 2026 10:31
Environment variable names ignore ASCII case on Windows, but env maps
compare names exactly, so each lookup and override has to handle case by
hand. Add `Folded<S, R>`, generic over string storage and case rule, with
an `EnvName` alias that follows the platform's rule. `S` may be unsized,
so `Folded<OsStr, R>` serves as the borrowed form for map lookups. An
optional `serde` feature encodes a value as its storage. Nothing uses the
crate yet.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Environment variable names ignore ASCII case on Windows, but the env maps
used to plan, spawn, and serve tasks compared names exactly. For task
command assignments such as `Foo=a FOO=b`, map iteration order decided
which value a task saw, and an assignment could fail to replace an
inherited variable spelled differently. The `FORCE_COLOR` fallback added
a second entry next to a differently spelled parent value, and
`VP_RUN_CONCURRENCY_LIMIT` and runner `getEnv` lookups missed
differently spelled variables. PATH lookups and prefix queries handled
case by hand.

Key the env maps in `vt_shell`, `vt_plan`, `vt`, `vt_server`, and
`fspy::Command` by `EnvName`, which drops the manual case handling. When
an assignment overrides a variable spelled differently, the task gets
the assignment's spelling, and its fingerprint entry replaces the one
recorded under the other spelling. Cache keys and fingerprints still
store plain strings. `TaskParsedCommand` drops its unused wincode and
`Diff` derives, which don't support `EnvName` keys.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@wan9chi
wan9chi force-pushed the fix/windows-env-name-comparison branch from 32ae3e1 to 39c1af6 Compare September 24, 2026 02:33
@wan9chi wan9chi changed the title fix(run): ignore ASCII case in Windows environment overrides fix(run): match environment variable names by platform rules Sep 24, 2026
@wan9chi
wan9chi marked this pull request as ready for review September 24, 2026 02:49
@wan9chi
wan9chi merged commit 0e1decc into main Sep 24, 2026
19 checks passed
@wan9chi
wan9chi deleted the fix/windows-env-name-comparison branch September 24, 2026 02:49
wan9chi added a commit to voidzero-dev/vite-plus that referenced this pull request Sep 25, 2026
Bumps vite-task from `790b152` to `7d69d65`.

Changelog:
voidzero-dev/vite-task@790b152...7d69d65#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed

## Changes from vite-task

- **Changed** A task's cache settings now go inside `cache`, e.g.
`cache: { env: ["NODE_ENV"], input: ["src/**"] }`. `cache: true` is the
same as `cache: {}`. Top-level `env`, `untrackedEnv`, `input`, and
`output` are rejected with an error that points to `vp migrate`
([voidzero-dev/vite-task#749](voidzero-dev/vite-task#749)).
- **Fixed** On Windows, environment variable names used by `vp run`
match regardless of ASCII letter case
([voidzero-dev/vite-task#747](voidzero-dev/vite-task#747)).
- **Fixed** Cached tasks on macOS no longer intermittently fail with
`oils I/O error (main): No such process`
([voidzero-dev/vite-task#703](voidzero-dev/vite-task#703)).

## Changes in this PR

- `Session::init_with`, `ScriptCommand::envs`, and
`SyntheticPlanRequest::envs` now key environment variables by
`vt_casefold::EnvName`. The `vite-plus-cli` env maps use the same key
type (`EnvMap`), so PATH lookup and env merging follow the platform
rules. The hand-written case-insensitive PATH lookups are replaced by
`vt::get_path_env`.
- Adds `vt_casefold` as a workspace dependency, including the commented
local `[patch]` entry.
- Regenerates `packages/cli/src/run-config.ts`.
- Moves top-level task cache fields under `cache` in CLI snapshot
fixtures and a `vp_static_config` test.
- Keeps `docs/vite.config.ts` in the old format. Docs deploys build it
with the released `vp` from `setup-vp`, and that version rejects a
`cache` object. #2814 pins that `vp` version for docs builds, so the
first release with this vite-task doesn't break them.
- Documents the `cache` object in `docs/config/run.md`. `env`,
`untrackedEnv`, `input`, and `output` become `cache.env`,
`cache.untrackedEnv`, `cache.input`, and `cache.output`. Updates the
examples and links in the cache, run, and automatic data tracking guides
and the READMEs. The docs describe only the `cache` object and don't
mention the old top-level fields.

`vp migrate` support for moving these fields comes in the next PR in
this stack.
wan9chi added a commit to voidzero-dev/vite-plus that referenced this pull request Sep 26, 2026
Task cache settings now live under `cache`, and `vp migrate` moves
existing task settings for you. `vp lint` and `vp fmt` run from a
package directory now pick the same config as your editor, and piped
`vp` output exits cleanly.

### Breaking Changes

Task cache settings in `run.tasks` now go inside `cache`
([#2813](#2813),
[#2814](#2814),
[#2823](#2823),
[vite-task#749](voidzero-dev/vite-task#749)), by
@wan9chi.

| Old (top level of a task) | New |
| --- | --- |
| `env` | `cache.env` |
| `untrackedEnv` | `cache.untrackedEnv` |
| `input` | `cache.input` |
| `output` | `cache.output` |

`cache: true` is the same as `cache: {}`. After upgrading, a task that
still uses the old top-level fields fails with an error that points to
`vp migrate`. Run `vp migrate` to move the fields in `vite.config.*` for
the workspace root and every package. It warns about tasks in
`vite.config.*` that it cannot rewrite safely. Tasks created in other
modules, such as by a shared helper function, are not detected, so move
their fields by hand; the error lists every task that still needs it.
Projects whose tasks do not use these fields need no changes. See the
[task cache migration
rules](https://viteplus.dev/guide/migrate-rules#task-cache-configuration)
and the [`cache` reference](https://viteplus.dev/config/run#cache).

### Highlights

- `vp lint` and `vp fmt` run from a package directory now use the same
config as Oxlint, Oxfmt, and the language server, so results match your
editor. `vp check` still applies the workspace-root settings
([#2807](#2807)), by
@fengmk2.
- Global `vp` commands now exit cleanly when piped into a command that
closes early, such as `vp --version | head -n 1`, instead of aborting
with exit code 134
([#2785](#2785),
[#2793](#2793)), by
@naokihaba.
- Cached tasks on macOS no longer intermittently fail with `oils I/O
error (main): No such process`
([vite-task#703](voidzero-dev/vite-task#703)),
by @lifeiscontent.

### Features

- The bundled tools update `vite@8.3.0` -> `vite@8.3.1`,
`rolldown@1.2.9` -> `rolldown@1.2.11`, and `oxlint-tsgolint@7.0.2002` ->
`oxlint-tsgolint@7.0.2003`
([#2805](#2805),
[#2812](#2812)), by
@voidzero-guard[bot].

### Fixes & Enhancements

- Async `defineConfig` callbacks now type-check lint and format options,
such as `'warn'` rule severities, the same way as synchronous callbacks,
so configs updated by `vp migrate` no longer fail with `TS2769`
([#2803](#2803)), by
@TheAlexLichter.
- `vp test --help` now lists the Vitest 5 options `--repeats`,
`--injectCjsGlobals`, `--fsModuleCache`, `--fsModuleCachePath`, and
`--sharedViteServer`
([#2809](#2809)), by
@Marve10s.
- On Windows, `vp run` now matches environment variable names regardless
of letter case
([vite-task#747](voidzero-dev/vite-task#747)),
by @wan9chi.

### Docs

- The copy prompt dialog no longer nests scroll areas and shows clearer
copy feedback
([#2794](#2794),
[#2799](#2799)), by
@liangmiQwQ and @Boshen.

### Chore

- Snapshot tests share prepared packages and one Chromium process, run
faster on every platform, and no longer read the live npm registry in
the standalone npm fallback case
([#2771](#2771),
[#2786](#2786),
[#2797](#2797)), by
@fengmk2 and @voidzero-guard[bot].
- The snapshot setup check links the local `vite-plus` package into its
reference home instead of installing the version under release from npm,
so it passes before that version is published
([#2819](#2819)), by
@wan9chi.
- The preview migration harness installs a project's committed
dependencies before running `vp migrate`, so the Vitest 5 migration can
read the original Vitest version
([#2822](#2822)), by
@wan9chi.
- The standalone install workflow loads the generated environment before
running `vp env doctor`
([#2790](#2790)), by
@naokihaba.

### Bundled Versions

| Tool | Version | Source |
| --- | --- | --- |
| vite | `8.3.1` |
[`39ddf7c`](vitejs/vite@39ddf7c)
|
| rolldown | `1.2.11` |
[`8df4219`](rolldown/rolldown@8df4219)
|
| tsdown | `0.23.0` | [npm](https://npmx.dev/package/tsdown/v/0.23.0) |
| vitest | `5.0.1` | [npm](https://npmx.dev/package/vitest/v/5.0.1) |
| oxlint | `1.85.0` | [npm](https://npmx.dev/package/oxlint/v/1.85.0) |
| oxlint-tsgolint | `7.0.2003` |
[npm](https://npmx.dev/package/oxlint-tsgolint/v/7.0.2003) |
| oxfmt | `0.70.0` | [npm](https://npmx.dev/package/oxfmt/v/0.70.0) |

### Upgrade

```bash
vp upgrade
```

### New Contributors

@Marve10s

**Full Changelog**:
v1.0.0-rc.0...v1.0.0-rc.1

---

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: wan9chi <me@wan9chi.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.

1 participant