Skip to content

Commit 42e7734

Browse files
Stop npm oracle trees symlinking into a cycle (#582)
The randomized npm-crawler oracle generator gave some pnpm store entries a node_modules symlink to nm_dirs.first(), which is usually the importer node_modules that holds the store. Both crawlers follow an entry's node_modules link, so they walked .pnpm/<e>/node_modules/.pnpm/<e>/... until the OS refused the path. Both treat every I/O error as an empty dir, so the oracle comparison came down to where each walker gave up, not what it found. Seeds 5, 7, 12 and 41 drew this shape; seed 12 failed CI once with the sequential walker stopping 6 levels short of the new one. Point the link at a fresh node_modules outside the tree instead, the same way the vlt store case and the symlinked-scope case already do. The followed-symlink shape is still generated (16 of 64 seeds), and the two oracle tests run about 30% faster. Co-authored-by: Claude <noreply@anthropic.com>
1 parent fc356c0 commit 42e7734

1 file changed

Lines changed: 12 additions & 6 deletions

File tree

  • crates/socket-patch-core/src/crawlers/npm_crawler

‎crates/socket-patch-core/src/crawlers/npm_crawler/oracle.rs‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,12 +1336,18 @@ mod tests {
13361336
let _ = std::fs::write(entry.join("node_modules"), "file");
13371337
}
13381338
76..=79 => {
1339-
// An entry whose node_modules is a symlink.
1340-
if let Some(target) = self.nm_dirs.first().cloned() {
1341-
let entry = store.join(self.store_entry_name(&name, &version));
1342-
let _ = std::fs::create_dir_all(&entry);
1343-
Self::symlink(&target, &entry.join("node_modules"));
1344-
}
1339+
// An entry whose node_modules is a symlink (to a
1340+
// dir outside the tree, so the followed walk cannot
1341+
// cycle: a link back to an ancestor `node_modules`
1342+
// is walked until the OS refuses the path, which
1343+
// compares where two walkers gave up, not what
1344+
// they found).
1345+
let entry = store.join(self.store_entry_name(&name, &version));
1346+
let _ = std::fs::create_dir_all(&entry);
1347+
let id = self.uniq();
1348+
let elsewhere = self.scratch.join(format!("pnpm-nm{id}"));
1349+
self.package_json(&elsewhere.join(&name), &name, &version);
1350+
Self::symlink(&elsewhere, &entry.join("node_modules"));
13451351
}
13461352
80..=83 => {
13471353
// The entry itself is a symlink (skipped).

0 commit comments

Comments
 (0)