Skip to content

Commit cfd4eaa

Browse files
CI: hermetic spawns in redirect build tests (#823) (#1457)
* Spawn redirect build test children hermetically Five e2e_redirect_*_build suites (cargo, composer, gem, maven, npm) started socket-patch with a bare Command::new and their own SOCKET_* scrub. The copies disagreed: composer and maven removed every SOCKET_* var, the others kept SOCKET_NO_CONFIG, and none forced the update notifier off. Their run helpers now go through tests/common/hermetic.rs, and the files leave PENDING_RAW_SPAWNS (79 -> 74) so the ratchet keeps them hermetic. The gem suite's BUNDLE_MIRROR__ scrub and the maven suite's M2_HOME removal stay. Refs #823. Assisted-by: Claude Code:claude-opus-5-5 * Scrub every ambient BUNDLE_* in gem redirect runs The gem redirect suite's socket-patch runs removed only BUNDLE_MIRROR__* vars. An ambient BUNDLE_GEMFILE, BUNDLE_PATH or BUNDLE_APP_CONFIG still reached the crawler and could point it at a different Gemfile or gem store than the fixture install used. Use hermetic::Extra::Bundle, which removes every BUNDLE_* var; tests that need one still pass it in. Assisted-by: Claude Code:claude-opus-5-5 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 136fc31 commit cfd4eaa

6 files changed

Lines changed: 36 additions & 69 deletions

File tree

‎crates/socket-patch-cli/tests/e2e_redirect_cargo_build.rs‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@
6868
6969
#[path = "common/mod.rs"]
7070
mod common;
71-
use common::binary;
71+
use common::{binary, hermetic, hermetic_command};
7272

7373
use std::path::{Path, PathBuf};
74-
use std::process::{Command, Output};
74+
use std::process::Output;
7575

7676
use sha2::{Digest, Sha256};
7777
use socket_patch_core::hash::git_sha256::compute_git_sha256_from_bytes;
@@ -107,18 +107,13 @@ const PATCH_SUFFIX: &str =
107107

108108
// ── self-contained helpers ────────────────────────────────────────────
109109

110-
/// Run socket-patch with ambient `SOCKET_*` vars scrubbed and the fixture's
111-
/// private CARGO_HOME injected (the cargo crawler resolves the registry
112-
/// source tree through it).
110+
/// Run socket-patch in the hermetic environment (`tests/common/hermetic.rs`)
111+
/// with the fixture's private CARGO_HOME injected (the cargo crawler
112+
/// resolves the registry source tree through it).
113113
fn run_socket(cwd: &Path, args: &[&str], cargo_home: &Path) -> (i32, String, String) {
114-
let mut cmd = Command::new(binary());
114+
let mut cmd = hermetic_command(&binary());
115+
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
115116
cmd.args(args).current_dir(cwd);
116-
for (k, _) in std::env::vars_os() {
117-
if k.to_string_lossy().starts_with("SOCKET_") && k.to_string_lossy() != "SOCKET_NO_CONFIG" {
118-
cmd.env_remove(&k);
119-
}
120-
}
121-
cmd.env_remove("VIRTUAL_ENV");
122117
cmd.env("CARGO_HOME", cargo_home);
123118
let out = cmd.output().expect("failed to run socket-patch binary");
124119
(

‎crates/socket-patch-cli/tests/e2e_redirect_composer_build.rs‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@
5353
//! release. `#[ignore]`-gated like the vendored twin: the unpinned `test` job
5454
//! skips it; the e2e job runs it with a pinned toolchain via `--ignored`.
5555
56+
#[path = "common/hermetic.rs"]
57+
mod hermetic;
5658
#[path = "common/rollback_json.rs"]
5759
mod rollback_json;
5860

5961
use std::io::Write as _;
6062
use std::path::{Path, PathBuf};
61-
use std::process::Command;
6263

6364
use sha1::{Digest as _, Sha1};
6465
use wiremock::matchers::{method, path, path_regex};
@@ -304,19 +305,13 @@ fn lock_entry(proj: &Path, name: &str) -> serde_json::Value {
304305
.clone()
305306
}
306307

307-
/// A socket-patch invocation with the ambient `SOCKET_*` surface scrubbed
308-
/// (telemetry off, no socket-cli config, `VIRTUAL_ENV` removed).
308+
/// A socket-patch invocation in the hermetic environment
309+
/// (`tests/common/hermetic.rs`: telemetry off, no socket-cli config) with
310+
/// `VIRTUAL_ENV` removed.
309311
fn run_socket(cwd: &Path, args: &[&str]) -> (i32, String, String) {
310-
let mut cmd = Command::new(binary());
312+
let mut cmd = hermetic::command(&binary());
313+
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
311314
cmd.args(args).current_dir(cwd);
312-
for (k, _) in std::env::vars_os() {
313-
if k.to_string_lossy().starts_with("SOCKET_") {
314-
cmd.env_remove(&k);
315-
}
316-
}
317-
cmd.env("SOCKET_TELEMETRY_DISABLED", "1")
318-
.env("SOCKET_NO_CONFIG", "1")
319-
.env_remove("VIRTUAL_ENV");
320315
let out = cmd.output().expect("run socket-patch");
321316
(
322317
out.status.code().unwrap_or(-1),

‎crates/socket-patch-cli/tests/e2e_redirect_gem_build.rs‎

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
9898
#[path = "common/mod.rs"]
9999
mod common;
100-
use common::binary;
100+
use common::{binary, hermetic, hermetic_command};
101101

102102
use std::io::Write as _;
103103
use std::path::{Path, PathBuf};
@@ -150,28 +150,20 @@ const TINY_LIB: &str = "module TinyDep\n VALUE = \"tiny-ok\"\nend\n";
150150

151151
// ── self-contained helpers ────────────────────────────────────────────
152152

153-
/// Run the socket-patch binary with the ambient `SOCKET_*` surface scrubbed
154-
/// (a developer's `SOCKET_DRY_RUN=1` must not steer the assertions) and
155-
/// `VIRTUAL_ENV` (crawler discovery input) removed.
153+
/// Run the socket-patch binary in the hermetic environment
154+
/// (`tests/common/hermetic.rs`: a developer's `SOCKET_DRY_RUN=1` must not
155+
/// steer the assertions) with `VIRTUAL_ENV` (crawler discovery input) and
156+
/// every ambient `BUNDLE_*` var (the crawler reads `BUNDLE_GEMFILE`,
157+
/// `BUNDLE_PATH`, mirrors, ...) removed.
156158
fn run_socket(cwd: &Path, args: &[&str]) -> (i32, String, String) {
157159
run_socket_env(cwd, args, &[])
158160
}
159161

160162
/// [`run_socket`] with extra environment on top of the scrubbed surface.
161163
fn run_socket_env(cwd: &Path, args: &[&str], envs: &[(&str, &str)]) -> (i32, String, String) {
162-
let mut cmd = Command::new(binary());
164+
let mut cmd = hermetic_command(&binary());
165+
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv, hermetic::Extra::Bundle]);
163166
cmd.args(args).current_dir(cwd);
164-
for (k, _) in std::env::vars_os() {
165-
if k.to_string_lossy().starts_with("SOCKET_") && k.to_string_lossy() != "SOCKET_NO_CONFIG" {
166-
cmd.env_remove(&k);
167-
}
168-
}
169-
for (key, _) in std::env::vars_os() {
170-
if key.to_string_lossy().starts_with("BUNDLE_MIRROR__") {
171-
cmd.env_remove(key);
172-
}
173-
}
174-
cmd.env_remove("VIRTUAL_ENV");
175167
for (k, v) in envs {
176168
cmd.env(k, v);
177169
}

‎crates/socket-patch-cli/tests/e2e_redirect_maven_build.rs‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
//! `SOCKET_PATCH_MAVEN_E2E_{MVN,VERSION,REQUIRED}` gates in
5353
//! `maven_build_common`.
5454
55+
#[path = "common/hermetic.rs"]
56+
mod hermetic;
5557
#[path = "common/rollback_json.rs"]
5658
mod rollback_json;
5759

@@ -63,7 +65,6 @@ mod maven_build_common;
6365
mod vex_e2e_common;
6466

6567
use std::path::{Path, PathBuf};
66-
use std::process::Command;
6768

6869
use hosted_maven_common::{Hosted, Server};
6970
use maven_build_common::*;
@@ -133,23 +134,17 @@ fn mount_api(s: &Server, jar: &[u8], pom: &[u8], view: &serde_json::Value) {
133134
hosted_maven_common::mount_api(s, &HOSTED, jar, pom, view);
134135
}
135136

136-
/// `socket-patch <args>` with ambient `SOCKET_*` scrubbed and the per-test
137-
/// local repository as the crawler's maven repo.
137+
/// `socket-patch <args>` in the hermetic environment
138+
/// (`tests/common/hermetic.rs`) with the per-test local repository as the
139+
/// crawler's maven repo.
138140
fn socket(cwd: &Path, m2: &Path, args: &[&str]) -> (Option<i32>, serde_json::Value, String) {
139-
let mut cmd = Command::new(binary());
140-
for (k, _) in std::env::vars_os() {
141-
if k.to_string_lossy().starts_with("SOCKET_") {
142-
cmd.env_remove(&k);
143-
}
144-
}
141+
let mut cmd = hermetic::command(&binary());
142+
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
145143
let out = cmd
146144
.args(args)
147145
.current_dir(cwd)
148-
.env("SOCKET_TELEMETRY_DISABLED", "1")
149-
.env("SOCKET_NO_CONFIG", "1")
150146
.env("MAVEN_REPO_LOCAL", m2)
151147
.env_remove("M2_HOME")
152-
.env_remove("VIRTUAL_ENV")
153148
.output()
154149
.expect("run socket-patch");
155150
let stdout = String::from_utf8_lossy(&out.stdout).into_owned();

‎crates/socket-patch-cli/tests/e2e_redirect_npm_build.rs‎

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ mod rollback_json;
6363

6464
#[path = "common/mod.rs"]
6565
mod common;
66-
use common::binary;
66+
use common::{binary, hermetic, hermetic_command};
6767

6868
use std::path::{Path, PathBuf};
69-
use std::process::{Command, Output};
69+
use std::process::Output;
7070

7171
use sha2::{Digest, Sha512};
7272
use socket_patch_core::hash::git_sha256::compute_git_sha256_from_bytes;
@@ -107,18 +107,13 @@ const PURL_UNINSTALLED: &str = "pkg:npm/left-pad@9.9.9";
107107

108108
// ── self-contained helpers ────────────────────────────────────────────
109109

110-
/// Run the socket-patch binary with a scrubbed environment: every ambient
111-
/// `SOCKET_*` var is removed (so a developer's `SOCKET_DRY_RUN=1` etc. can't
112-
/// flip behavior) along with `VIRTUAL_ENV` (crawler discovery input).
110+
/// Run the socket-patch binary in the hermetic environment
111+
/// (`tests/common/hermetic.rs`: a developer's `SOCKET_DRY_RUN=1` etc. can't
112+
/// flip behavior) with `VIRTUAL_ENV` (crawler discovery input) removed.
113113
fn run_socket(cwd: &Path, args: &[&str]) -> (i32, String, String) {
114-
let mut cmd = Command::new(binary());
114+
let mut cmd = hermetic_command(&binary());
115+
hermetic::scrub_extra(&mut cmd, &[hermetic::Extra::Venv]);
115116
cmd.args(args).current_dir(cwd);
116-
for (k, _) in std::env::vars_os() {
117-
if k.to_string_lossy().starts_with("SOCKET_") && k.to_string_lossy() != "SOCKET_NO_CONFIG" {
118-
cmd.env_remove(&k);
119-
}
120-
}
121-
cmd.env_remove("VIRTUAL_ENV");
122117
let out = cmd.output().expect("failed to run socket-patch binary");
123118
(
124119
out.status.code().unwrap_or(-1),

‎crates/socket-patch-cli/tests/spawn_env_hygiene.rs‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,7 @@ const PENDING_RAW_SPAWNS: &[&str] = &[
4646
"e2e_pypi.rs",
4747
"e2e_pypi_multi_copy.rs",
4848
"e2e_redirect_bun_build.rs",
49-
"e2e_redirect_cargo_build.rs",
5049
"e2e_redirect_cargo_shapes.rs",
51-
"e2e_redirect_composer_build.rs",
52-
"e2e_redirect_gem_build.rs",
53-
"e2e_redirect_maven_build.rs",
54-
"e2e_redirect_npm_build.rs",
5550
"e2e_scan.rs",
5651
"e2e_socket_yml_policy.rs",
5752
"e2e_vendor_bun_build.rs",

0 commit comments

Comments
 (0)