Skip to content

Commit 21b2fc2

Browse files
committed
feat(client): serve getEnvs over runner IPC
Motivation: Tools need a bulk env read path for APIs like Vite loadEnv that discover an env prefix rather than a single name. Before this change, getEnvs existed on the JS surface but always returned an empty match set. Scope: Add the GetEnvs request/response frame, Rust client support, NAPI getEnvs implementation, server-side env glob matching, invalid-glob error surfacing, and print-only e2e coverage. This PR intentionally does not add getEnvs match sets to cache fingerprints. Verification: - cargo test -p vite_task_server --test integration - UPDATE_SNAPSHOTS=1 cargo test -p vite_task_bin --test e2e_snapshots fetch_envs_reads_match_set -- --ignored - cargo test -p vite_task_bin --test e2e_snapshots fetch_envs_reads_match_set -- --ignored
1 parent fb19a12 commit 21b2fc2

15 files changed

Lines changed: 213 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
- **Changed** Tracked environment values in task cache fingerprints are now stored only as SHA-256 digests, and env-related cache miss details report names without values.
4+
- **Added** Runner-aware `getEnvs` calls now return env values served by the runner for matching env glob patterns.
45
- **Added** Runner-aware `getEnv` reads can now participate in task cache fingerprints, so changing a tool-served env value invalidates the cache and names the env var in the miss message.
56
- **Added** Runner-aware tools can now opt the current task run out of caching through the new IPC channel; Vite dev server integration uses this automatically ([#441](https://github.com/voidzero-dev/vite-task/pull/441))
67
- **Fixed** Prefix environment assignments like `PATH=... command` now affect executable lookup during task planning, so tools provided only by the prefixed `PATH` can be resolved correctly ([#440](https://github.com/voidzero-dev/vite-task/pull/440))

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/native_str/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use wincode::{
3737
/// **Not portable across platforms.** The binary representation is platform-specific.
3838
/// Deserializing a `NativeStr` serialized on a different platform leads to unspecified
3939
/// behavior (garbage data), but is not unsafe. Designed for same-platform IPC only.
40-
#[derive(TransparentWrapper, PartialEq, Eq)]
40+
#[derive(TransparentWrapper, PartialEq, Eq, Hash)]
4141
#[repr(transparent)]
4242
pub struct NativeStr {
4343
// On unix, this is the raw bytes of the OsStr.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { getEnvs } from '@voidzero-dev/vite-task-client';
2+
3+
const matches = getEnvs('PROBE_*');
4+
const sorted = Object.entries(matches).sort(([a], [b]) => a.localeCompare(b));
5+
6+
for (const [key, value] of sorted) {
7+
console.log(`${key}=${value}`);
8+
}

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/snapshots.toml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,30 @@ steps = [
200200
],
201201
], comment = "cache hit: PROBE_ENV changed but was requested with tracked: false" },
202202
]
203+
204+
[[e2e]]
205+
name = "fetch_envs_reads_match_set"
206+
comment = """
207+
Exercises `getEnvs(pattern)`: the tool asks the runner for every env matching `PROBE_*` and prints the served match set. This verifies the bulk env IPC round trip before match sets are added to cache fingerprints.
208+
"""
209+
ignore = true
210+
steps = [
211+
{ argv = [
212+
"vt",
213+
"run",
214+
"fetch-envs",
215+
], envs = [
216+
[
217+
"PROBE_A",
218+
"a",
219+
],
220+
[
221+
"PROBE_B",
222+
"b",
223+
],
224+
[
225+
"UNRELATED",
226+
"noise",
227+
],
228+
], comment = "runner serves only envs matching PROBE_*" },
229+
]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# fetch_envs_reads_match_set
2+
3+
Exercises `getEnvs(pattern)`: the tool asks the runner for every env matching `PROBE_*` and prints the served match set. This verifies the bulk env IPC round trip before match sets are added to cache fingerprints.
4+
5+
## `PROBE_A=a PROBE_B=b UNRELATED=noise vt run fetch-envs`
6+
7+
runner serves only envs matching PROBE_*
8+
9+
```
10+
$ node scripts/fetch_envs.mjs
11+
PROBE_A=a
12+
PROBE_B=b
13+
```

crates/vite_task_bin/tests/e2e_snapshots/fixtures/ipc_client_test/vite-task.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
"command": "node scripts/fetch_env.mjs --untracked PROBE_ENV",
2323
"cache": true
2424
},
25+
"fetch-envs": {
26+
"command": "node scripts/fetch_envs.mjs",
27+
"cache": true
28+
},
2529
"fetch-prefixed-env": {
2630
"command": "PREFIXED_ENV=from-command node scripts/fetch_env.mjs PREFIXED_ENV",
2731
"cache": true

crates/vite_task_client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ rust-version.workspace = true
88

99
[dependencies]
1010
native_str = { workspace = true }
11+
rustc-hash = { workspace = true }
1112
vite_task_ipc_shared = { workspace = true }
1213
wincode = { workspace = true, features = ["derive"] }
1314

crates/vite_task_client/src/lib.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use std::{
66
};
77

88
use native_str::NativeStr;
9-
use vite_task_ipc_shared::{GetEnvResponse, IPC_ENV_NAME, Request};
9+
use rustc_hash::FxHashMap;
10+
use vite_task_ipc_shared::{GetEnvResponse, GetEnvsResponse, IPC_ENV_NAME, Request};
1011
use wincode::{SchemaRead, config::DefaultConfig};
1112

1213
#[cfg(unix)]
@@ -73,6 +74,30 @@ impl Client {
7374
.map(|env_value| Arc::<OsStr>::from(env_value.to_cow_os_str().as_ref())))
7475
}
7576

77+
/// Requests every env whose name matches `pattern` from the runner. The
78+
/// returned map is keyed by env name with its value.
79+
///
80+
/// # Errors
81+
///
82+
/// Returns an error if the request or response fails, or if the server
83+
/// rejects the pattern as an invalid glob.
84+
// TODO(env-track): A later PR in this stack adds a tracked flag so matched
85+
// env sets can participate in cache fingerprints instead of being IPC-only.
86+
pub fn get_envs(&self, pattern: &str) -> io::Result<FxHashMap<Arc<OsStr>, Arc<OsStr>>> {
87+
self.send(&Request::GetEnvs { pattern })?;
88+
let response: GetEnvsResponse = self.recv()?;
89+
Ok(response
90+
.entries
91+
.into_iter()
92+
.map(|(name, value)| {
93+
(
94+
Arc::<OsStr>::from(name.to_cow_os_str().as_ref()),
95+
Arc::<OsStr>::from(value.to_cow_os_str().as_ref()),
96+
)
97+
})
98+
.collect())
99+
}
100+
76101
fn send(&self, request: &Request<'_>) -> io::Result<()> {
77102
let bytes = wincode::serialize(request)
78103
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;

crates/vite_task_client_napi/src/lib.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,20 @@ impl RunnerClient {
106106
})
107107
}
108108

109-
/// No-op for now: always returns an empty match-set — see
110-
/// [`Self::get_env`].
111109
#[napi]
112110
pub fn get_envs(
113111
&self,
114-
_pattern: String,
112+
pattern: String,
115113
_options: Option<GetEnvOptions>,
116114
) -> Result<HashMap<String, String>> {
117-
Ok(HashMap::new())
115+
let matches =
116+
self.client.get_envs(&pattern).map_err(|err| err_string(vite_str::format!("{err}")))?;
117+
// Entries whose name or value contains non-UTF-8 bytes can't cross
118+
// the JS boundary as `String`; bulk fetch drops them silently.
119+
Ok(matches
120+
.into_iter()
121+
.filter_map(|(k, v)| Some((k.to_str()?.to_owned(), v.to_str()?.to_owned())))
122+
.collect())
118123
}
119124
}
120125

0 commit comments

Comments
 (0)