From bfc7778bea68ee4808276bb664fbcf3cf6a2f654 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 02:23:42 +0000 Subject: [PATCH 1/4] Start fix for #1195 Assisted-by: Claude Code:claude-opus-5-5 From 1d1d6a9bb1e44eff8b4864f506c8283295c62a65 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 02:29:38 +0000 Subject: [PATCH 2/4] Test capped re-scans of contested npm pins Add end-to-end regressions for #1195: a hosted pin whose lock later gains an unpinned npm: alias copy of the same version, and a pinned package-lock.json beside an unpinned npm-shrinkwrap.json. Under --max-new-patches 0 both re-scans currently defer the already-applied patch as new and leave the second copy unpatched. Assisted-by: Claude Code:claude-opus-5-5 --- .../src/commands/scan/policy.rs | 19 ++- .../tests/e2e_redirect_yarn_classic_build.rs | 16 ++- .../tests/e2e_vendor_yarn_berry_build.rs | 47 +++++-- .../tests/in_process_redirect_pipenv.rs | 3 +- .../tests/in_process_redirect_pnpm.rs | 16 ++- .../tests/mode_migration_pypi.rs | 15 ++- .../tests/scan_rollout_e2e.rs | 116 ++++++++++++++++++ .../src/crawlers/cargo_crawler.rs | 3 +- crates/socket-patch-core/src/formats/mod.rs | 2 +- .../src/hosted/memory/mod.rs | 2 +- .../src/patch/redirect/mod.rs | 27 ++-- .../src/patch/redirect/poetry.rs | 12 +- .../src/patch/redirect/upstream/cargo.rs | 5 +- .../src/utils/group_commit.rs | 25 +++- .../src/vendor/lock_inventory/tests.rs | 5 +- crates/socket-patch-core/src/vendor/mod.rs | 2 +- .../src/vendor/yarn_berry_lock.rs | 4 +- .../src/vendor/yarn_classic_lock.rs | 6 +- crates/socket-patch-core/src/vex/product.rs | 4 +- 19 files changed, 275 insertions(+), 54 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/scan/policy.rs b/crates/socket-patch-cli/src/commands/scan/policy.rs index 8a6711844..464f737be 100644 --- a/crates/socket-patch-cli/src/commands/scan/policy.rs +++ b/crates/socket-patch-cli/src/commands/scan/policy.rs @@ -237,7 +237,9 @@ impl ScanPolicy { } fn recorded_uuid(&self, purl: &str) -> Option<&str> { - self.recorded.get(&PurlKey::new(purl).into_string()).map(String::as_str) + self.recorded + .get(&PurlKey::new(purl).into_string()) + .map(String::as_str) } /// Step 3: the root, ecosystem and package filters. Returns whether the @@ -269,7 +271,10 @@ impl ScanPolicy { } if self.root_verdict.is_err() { // Already reported as the root's one entry. - } else if report.filtered_purls.insert(PurlKey::new(purl).into_string()) { + } else if report + .filtered_purls + .insert(PurlKey::new(purl).into_string()) + { report.filtered.push(FilteredEntry { purl: Some(PurlKey::new(purl).into_string()), uuid: None, @@ -284,7 +289,10 @@ impl ScanPolicy { /// Record the purls with a newer patch (`updates[]`), for /// `retained[].upgradeAvailable`. pub(crate) fn set_update_purls<'a>(&self, purls: impl IntoIterator) { - self.report().update_purls = purls.into_iter().map(|p| PurlKey::new(p).into_string()).collect(); + self.report().update_purls = purls + .into_iter() + .map(|p| PurlKey::new(p).into_string()) + .collect(); } /// Steps 5-6: group the tier-accessible offers, keep retained packages @@ -298,7 +306,10 @@ impl ScanPolicy { { let report = self.report(); for offer in accessible { - if report.retained_purls.contains(&PurlKey::new(&offer.purl).into_string()) { + if report + .retained_purls + .contains(&PurlKey::new(&offer.purl).into_string()) + { continue; } grouped.entry(offer.purl.clone()).or_default().push(offer); diff --git a/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs b/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs index e36d0fbbf..7f277f70c 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs @@ -900,7 +900,10 @@ async fn classic_offline_mirror_refuses_hosted_and_keeps_installs_working() { return; }; assert!( - fx.proj.join("mirror").join(format!("{DEP}-{DEP_VERSION}.tgz")).is_file(), + fx.proj + .join("mirror") + .join(format!("{DEP}-{DEP_VERSION}.tgz")) + .is_file(), "the fixture install must populate the offline mirror" ); let fresh = fx.tmp.path().join("fresh"); @@ -926,7 +929,11 @@ async fn classic_offline_mirror_refuses_hosted_and_keeps_installs_working() { String::from_utf8_lossy(&ci.stderr) ); assert!( - !fresh.join("node_modules").join(DEP).join("index.js").exists(), + !fresh + .join("node_modules") + .join(DEP) + .join("index.js") + .exists(), "yarn < 1.7 is expected to install nothing from the mirror" ); return; @@ -948,7 +955,10 @@ async fn classic_offline_mirror_refuses_hosted_and_keeps_installs_working() { ); let installed = std::fs::read(fresh.join("node_modules").join(DEP).join("index.js")).unwrap(); - assert_eq!(installed, fx.orig, "the untouched lock installs the upstream bytes"); + assert_eq!( + installed, fx.orig, + "the untouched lock installs the upstream bytes" + ); std::fs::remove_dir_all(fresh.join("node_modules")).unwrap(); } } diff --git a/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs b/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs index b6edf019d..6555371a1 100644 --- a/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs +++ b/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs @@ -729,7 +729,14 @@ async fn run_berry_capstone(driver: VendorDriver, yarnrc_extra: &str) { fn git(cwd: &Path, args: &[&str]) -> Output { let out = Command::new("git") - .args(["-c", "user.name=t", "-c", "user.email=t@t", "-c", "init.defaultBranch=main"]) + .args([ + "-c", + "user.name=t", + "-c", + "user.email=t@t", + "-c", + "init.defaultBranch=main", + ]) .args(args) .current_dir(cwd) .output() @@ -810,16 +817,30 @@ fn yarn_berry_vendored_tarball_survives_a_tgz_gitignore_rule() { }; let (code, stdout, stderr) = run_socket( &proj, - &["vendor", "--json", "--offline", "--cwd", proj.to_str().unwrap()], + &[ + "vendor", + "--json", + "--offline", + "--cwd", + proj.to_str().unwrap(), + ], + ); + assert_eq!( + code, 0, + "vendor failed.\nstdout:\n{stdout}\nstderr:\n{stderr}" ); - assert_eq!(code, 0, "vendor failed.\nstdout:\n{stdout}\nstderr:\n{stderr}"); git(&proj, &["add", "-A"]); git(&proj, &["commit", "-qm", "vendored"]); let fresh = tmp.path().join("fresh"); git( tmp.path(), - &["clone", "-q", proj.to_str().unwrap(), fresh.to_str().unwrap()], + &[ + "clone", + "-q", + proj.to_str().unwrap(), + fresh.to_str().unwrap(), + ], ); let fresh_global = tmp.path().join("fresh-yarn-global"); let ci = corepack( @@ -854,14 +875,26 @@ fn yarn_berry_vendor_refuses_a_gitignored_socket_dir() { let pkg_before = std::fs::read(proj.join("package.json")).unwrap(); let (code, stdout, stderr) = run_socket( &proj, - &["vendor", "--json", "--offline", "--cwd", proj.to_str().unwrap()], + &[ + "vendor", + "--json", + "--offline", + "--cwd", + proj.to_str().unwrap(), + ], + ); + assert_eq!( + code, 1, + "vendor must fail.\nstdout:\n{stdout}\nstderr:\n{stderr}" ); - assert_eq!(code, 1, "vendor must fail.\nstdout:\n{stdout}\nstderr:\n{stderr}"); assert!( stdout.contains("vendor_artifact_gitignored"), "refusal code expected:\n{stdout}" ); assert_eq!(std::fs::read(proj.join("yarn.lock")).unwrap(), lock_before); - assert_eq!(std::fs::read(proj.join("package.json")).unwrap(), pkg_before); + assert_eq!( + std::fs::read(proj.join("package.json")).unwrap(), + pkg_before + ); assert!(!proj.join(format!(".socket/vendor/npm/{UUID}")).exists()); } diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs index e31acf570..e4187d707 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs @@ -589,8 +589,7 @@ async fn live_pipfile_lock_conflict_vetoes_the_requirements_redirect() { #[tokio::test] #[serial] async fn platform_wheel_is_not_pinned_into_the_lock() { - assert_wheel_tag_is_not_pinned("cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64") - .await; + assert_wheel_tag_is_not_pinned("cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64").await; } /// #1048: a pure wheel bound to one interpreter (`cp311-none-any`) fails diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs index d9e701976..189785fd8 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs @@ -432,7 +432,10 @@ async fn hosted_bom_lock_and_workspace_read_like_their_plain_twins() { assert_eq!(code, 0, "scan --mode hosted should succeed on a BOM lock"); let lock = std::fs::read_to_string(&lock_path).unwrap(); assert!(lock.starts_with("\u{feff}lockfileVersion:"), "{lock}"); - assert!(lock.contains(HOSTED_URL), "the BOM lock is redirected: {lock}"); + assert!( + lock.contains(HOSTED_URL), + "the BOM lock is redirected: {lock}" + ); let ws_path = tmp.path().join("pnpm-workspace.yaml"); assert_eq!( std::fs::read_to_string(&ws_path).ok().as_deref(), @@ -447,7 +450,10 @@ async fn hosted_bom_lock_and_workspace_read_like_their_plain_twins() { pristine, "rollback restores the BOM lock byte for byte" ); - assert!(!ws_path.exists(), "the auto-created workspace file goes too"); + assert!( + !ws_path.exists(), + "the auto-created workspace file goes too" + ); // A BOM workspace file whose first key is the user's opt-out: left // byte-identical (no duplicate `trustLockfile`), lock still redirected. @@ -473,7 +479,11 @@ async fn hosted_bom_lock_and_workspace_read_like_their_plain_twins() { "the lock is still redirected for {user_ws:?}" ); let ws = std::fs::read_to_string(tmp.path().join("pnpm-workspace.yaml")).unwrap(); - assert_eq!(ws, want.unwrap_or(user_ws), "workspace file for {user_ws:?}"); + assert_eq!( + ws, + want.unwrap_or(user_ws), + "workspace file for {user_ws:?}" + ); assert_eq!(ws.matches("trustLockfile").count(), 1, "{ws:?}"); } } diff --git a/crates/socket-patch-cli/tests/mode_migration_pypi.rs b/crates/socket-patch-cli/tests/mode_migration_pypi.rs index 1812583b9..fb897ca00 100644 --- a/crates/socket-patch-cli/tests/mode_migration_pypi.rs +++ b/crates/socket-patch-cli/tests/mode_migration_pypi.rs @@ -1332,9 +1332,18 @@ async fn ledger_update_failure_changes_nothing() { set_mode(0o755); assert_eq!(code, 1, "{env:#}"); assert_eq!(env["status"], "error", "{env:#}"); - assert!(!env.to_string().contains("redirect_takeover_unpatched"), "{env:#}"); - assert_eq!(std::fs::read(root.join("requirements.txt")).unwrap(), vendored); - assert_eq!(std::fs::read(root.join(".socket/vendor/state.json")).unwrap(), state); + assert!( + !env.to_string().contains("redirect_takeover_unpatched"), + "{env:#}" + ); + assert_eq!( + std::fs::read(root.join("requirements.txt")).unwrap(), + vendored + ); + assert_eq!( + std::fs::read(root.join(".socket/vendor/state.json")).unwrap(), + state + ); assert!(root.join(format!(".socket/vendor/pypi/{UUID}")).exists()); } diff --git a/crates/socket-patch-cli/tests/scan_rollout_e2e.rs b/crates/socket-patch-cli/tests/scan_rollout_e2e.rs index 638ce21c5..e60c255e4 100644 --- a/crates/socket-patch-cli/tests/scan_rollout_e2e.rs +++ b/crates/socket-patch-cli/tests/scan_rollout_e2e.rs @@ -1061,3 +1061,119 @@ async fn a_failed_detail_lookup_for_an_applied_package_does_not_freeze_new_rows( "the pin stays" ); } + +/// Add `node_modules/` to `lock` as an `npm:` alias of `name@1.0.0` +/// resolved from the registry (what `npm install @npm:@1.0.0` +/// writes), with the matching `package.json` dependency and installed dir. +fn add_registry_alias(dir: &Path, lock: &str, alias: &str, name: &str) { + let spec = format!("npm:{name}@1.0.0"); + let path = dir.join(lock); + let mut v: Value = serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + v["packages"][""]["dependencies"][alias] = json!(spec); + v["packages"][format!("node_modules/{alias}")] = json!({ + "name": name, + "version": "1.0.0", + "resolved": format!("https://registry.npmjs.org/{name}/-/{name}-1.0.0.tgz"), + "integrity": "sha512-UPSTREAMupstream==", + "license": "MIT" + }); + let mut bytes = serde_json::to_vec_pretty(&v).unwrap(); + bytes.push(b'\n'); + std::fs::write(&path, bytes).unwrap(); + let manifest = dir.join("package.json"); + let mut m: Value = serde_json::from_slice(&std::fs::read(&manifest).unwrap()).unwrap(); + m["dependencies"][alias] = json!(spec); + std::fs::write(&manifest, serde_json::to_vec_pretty(&m).unwrap()).unwrap(); + let pkg = dir.join("node_modules").join(alias); + std::fs::create_dir_all(&pkg).unwrap(); + std::fs::write( + pkg.join("package.json"), + format!(r#"{{"name":"{name}","version":"1.0.0"}}"#), + ) + .unwrap(); + std::fs::write(pkg.join("index.js"), before(name)).unwrap(); +} + +/// REGRESSION (#1195): a pinned patch whose lock gains a second, unpinned +/// copy of the same `name@version` (an `npm:` alias added after the pin) +/// is still recorded in the project. Discovery withholds the pin from +/// attestation (the copy installs unpatched), but a capped re-scan must +/// read the row as ALREADY, not NEW: `--max-new-patches 0` must not defer +/// it, and the re-scan rewires the new copy (the remedy `vex` names). +#[tokio::test] +async fn issue_1195_a_capped_rescan_rewires_an_unpinned_alias_copy() { + let mock = MockServer::start().await; + mount_api(&mock, |_| Grant::Granted).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path(), &["roll-b"]); + let args = ["--mode", "hosted", "--patch-server-url", HOST]; + run_json(tmp.path(), &mock, &args); + assert_eq!(pinned(tmp.path()), names(&["roll-b"])); + + add_registry_alias(tmp.path(), "package-lock.json", "mz", "roll-b"); + let v = run_json( + tmp.path(), + &mock, + &[ + "--mode", + "hosted", + "--max-new-patches", + "0", + "--patch-server-url", + HOST, + ], + ); + assert_eq!(counts(&v), (0, 0, 0, 1), "{v}"); + assert!(deferred_names(&v).is_empty(), "{v}"); + let lock = std::fs::read_to_string(tmp.path().join("package-lock.json")).unwrap(); + assert_eq!( + lock.matches(&hosted_url("roll-b")).count(), + 2, + "both copies are pinned: {lock}" + ); +} + +/// REGRESSION (#1195), dual-lock shape: with `npm-shrinkwrap.json` beside +/// `package-lock.json`, a shrinkwrap restored to its unpinned version +/// contests the package-lock pin. The pin is still recorded, so a capped +/// re-scan reads ALREADY and rewires the shrinkwrap (npm <= 11 installs +/// from it). +#[tokio::test] +async fn issue_1195_a_capped_rescan_rewires_an_unpinned_twin_lock() { + let mock = MockServer::start().await; + mount_api(&mock, |_| Grant::Granted).await; + let tmp = tempfile::tempdir().unwrap(); + write_project(tmp.path(), &["roll-b"]); + let original = std::fs::read(tmp.path().join("package-lock.json")).unwrap(); + std::fs::write(tmp.path().join("npm-shrinkwrap.json"), &original).unwrap(); + let args = ["--mode", "hosted", "--patch-server-url", HOST]; + run_json(tmp.path(), &mock, &args); + let shrinkwrap = || std::fs::read_to_string(tmp.path().join("npm-shrinkwrap.json")).unwrap(); + assert!( + shrinkwrap().contains(&hosted_url("roll-b")), + "{}", + shrinkwrap() + ); + assert_eq!(pinned(tmp.path()), names(&["roll-b"])); + + std::fs::write(tmp.path().join("npm-shrinkwrap.json"), &original).unwrap(); + let v = run_json( + tmp.path(), + &mock, + &[ + "--mode", + "hosted", + "--max-new-patches", + "0", + "--patch-server-url", + HOST, + ], + ); + assert_eq!(counts(&v), (0, 0, 0, 1), "{v}"); + assert!(deferred_names(&v).is_empty(), "{v}"); + assert!( + shrinkwrap().contains(&hosted_url("roll-b")), + "the shrinkwrap is rewired: {}", + shrinkwrap() + ); +} diff --git a/crates/socket-patch-core/src/crawlers/cargo_crawler.rs b/crates/socket-patch-core/src/crawlers/cargo_crawler.rs index 168e1d5c7..377d54a45 100644 --- a/crates/socket-patch-core/src/crawlers/cargo_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/cargo_crawler.rs @@ -300,8 +300,7 @@ fn read_crate_cargo_toml(crate_path: &Path, dir_name: &str) -> Option<(String, S let content = crate::utils::fs::read_regular_to_string_sync(&cargo_toml_path).ok()?; // Fallback: parse directory name as - - package_name_version(&content) - .or_else(|| CargoCrawler::parse_dir_name_version(dir_name)) + package_name_version(&content).or_else(|| CargoCrawler::parse_dir_name_version(dir_name)) } impl Default for CargoCrawler { diff --git a/crates/socket-patch-core/src/formats/mod.rs b/crates/socket-patch-core/src/formats/mod.rs index 294f0f436..319e37621 100644 --- a/crates/socket-patch-core/src/formats/mod.rs +++ b/crates/socket-patch-core/src/formats/mod.rs @@ -29,8 +29,8 @@ pub(crate) mod bun; pub mod cargo; pub mod composer; -pub mod governing_locks; pub mod gem; +pub mod governing_locks; pub(crate) mod maven; pub(crate) mod nuget; pub mod pnpm; diff --git a/crates/socket-patch-core/src/hosted/memory/mod.rs b/crates/socket-patch-core/src/hosted/memory/mod.rs index 25e57eaee..e2c308075 100644 --- a/crates/socket-patch-core/src/hosted/memory/mod.rs +++ b/crates/socket-patch-core/src/hosted/memory/mod.rs @@ -66,9 +66,9 @@ use crate::rollout::stage::{ classify, lookup_incomplete, mark_pinned, offers_from_results, Offers, RecordedIndex, Row, Stage, ROLLOUT_DEFERRED, }; +use crate::utils::purl_key::PurlKey; use discover::Provider; use stages::{Planned, RewriteRefused, Rewritten, StageOptions}; -use crate::utils::purl_key::PurlKey; /// `"+"`; the sha comes from the /// `SOCKET_PATCH_GIT_SHA` build-time variable. diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index 13f11c3c6..5dc1b8362 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -77,9 +77,9 @@ use crate::formats::yarn::patterns::{ use crate::formats::yarn::source::{classic_copy_source, CopySource}; use crate::formats::yarn::stanzas::{stanza_key, BerryStanzas}; #[cfg(test)] -mod pnpm_equivalence_tests; -#[cfg(test)] mod platform_wheel_tests; +#[cfg(test)] +mod pnpm_equivalence_tests; mod poetry; mod pypi_takeover; pub use pypi_takeover::preflight_pypi_takeover; @@ -565,7 +565,7 @@ pub fn rewrite_registry_redirect_with_pipenv_version( bun_lockb_present, &std::collections::BTreeSet::new(), &std::collections::BTreeSet::new(), - &yarnrc::OuterYarnMirror::default(), + &yarnrc::OuterYarnMirror::default(), ) } @@ -6773,8 +6773,10 @@ fn rewrite_maven_pom( // One pass over the pom's repositories: `(id, url)` of each, which also // answers the per-dep URL-refresh check below while the pom is still // unchanged (a no-op rescan then never re-scans the pom per dep). - let original_repos: Vec<(String, Option)> = - pom.as_deref().map(maven_repository_ids_and_urls).unwrap_or_default(); + let original_repos: Vec<(String, Option)> = pom + .as_deref() + .map(maven_repository_ids_and_urls) + .unwrap_or_default(); let hosted_repo_generations: std::collections::BTreeSet = original_repos .iter() .filter_map(|(id, _)| generation::pin_name_uuid(id, false).map(str::to_string)) @@ -10846,7 +10848,10 @@ mod tests { &[(YARNRC_REL, "yarn-offline-mirror: false\n")], &[(YARNRC_REL, "yarn-offline-mirror:\n")], &[(YARNRC_REL, "yarn-offline-mirror \"\"\n")], - &[(YARNRC_REL, "yarn-offline-mirror-pruning true\n# yarn-offline-mirror ./m\n")], + &[( + YARNRC_REL, + "yarn-offline-mirror-pruning true\n# yarn-offline-mirror ./m\n", + )], &[(npmrc::NPMRC_REL, "[scope]\nyarn-offline-mirror=./m\n")], &[ (YARNRC_REL, "yarn-offline-mirror false\n"), @@ -10862,7 +10867,10 @@ mod tests { let mut r = RewriteResult::default(); rewrite_yarn_classic(&files, std::slice::from_ref(&ovr), &mut r); assert!(r.warnings.is_empty(), "{rcs:?}: {:?}", r.warnings); - assert!(r.files["yarn.lock"].contains("http://p.test/lp.tgz"), "{rcs:?}"); + assert!( + r.files["yarn.lock"].contains("http://p.test/lp.tgz"), + "{rcs:?}" + ); assert!(r.refused_yarn_classic_uuids.is_empty(), "{rcs:?}"); } } @@ -10887,7 +10895,10 @@ mod tests { rewrite_yarn_classic(&files, std::slice::from_ref(&other), &mut r); assert!(r.refused_yarn_classic_uuids.is_empty()); assert_eq!( - r.warnings.iter().map(|w| w.code.as_str()).collect::>(), + r.warnings + .iter() + .map(|w| w.code.as_str()) + .collect::>(), ["redirect_yarn_classic_entry_not_found"] ); } diff --git a/crates/socket-patch-core/src/patch/redirect/poetry.rs b/crates/socket-patch-core/src/patch/redirect/poetry.rs index 7a1b32d92..5ee901ff6 100644 --- a/crates/socket-patch-core/src/patch/redirect/poetry.rs +++ b/crates/socket-patch-core/src/patch/redirect/poetry.rs @@ -89,7 +89,9 @@ pub(super) fn rewrite_poetry( new: Some(Value::String(new)), }); } - result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .confirmed_python_lock_uuids + .insert(dep.patch_uuid.clone()); if !stale_warned { if let Some(format) = *writer_format.get_or_insert_with(|| pre_1_4_writer(&content)) @@ -124,14 +126,18 @@ pub(super) fn rewrite_poetry( } // Already redirected to this artifact (idempotent re-scan). LockStep::Unchanged => { - result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .confirmed_python_lock_uuids + .insert(dep.patch_uuid.clone()); } LockStep::NotFound => result.warnings.push(RewriteWarning { code: "redirect_poetry_entry_not_found".into(), detail: format!("no {path} entry for {}@{}", dep.name, dep.version), }), LockStep::Refused(detail) => { - result.refused_python_lock_uuids.insert(dep.patch_uuid.clone()); + result + .refused_python_lock_uuids + .insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_poetry_lock_unsupported".into(), detail: format!("{path}: {detail}"), diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs index 54d4c8030..2e9c01a90 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs @@ -489,6 +489,9 @@ mod tests { let other = format!("log = {{ version = \"0.4.20\", registry = \"socket-patch-{C}\" }}\n"); let (outcome, after) = restore_b(&manifest(&other), &config).await; assert_eq!(outcome.restored().count(), 1, "{:?}", outcome.pins); - assert_eq!(after.as_deref().map(str::trim_start), Some(block(C).as_str())); + assert_eq!( + after.as_deref().map(str::trim_start), + Some(block(C).as_str()) + ); } } diff --git a/crates/socket-patch-core/src/utils/group_commit.rs b/crates/socket-patch-core/src/utils/group_commit.rs index 3b483d55f..d82737fda 100644 --- a/crates/socket-patch-core/src/utils/group_commit.rs +++ b/crates/socket-patch-core/src/utils/group_commit.rs @@ -1335,7 +1335,10 @@ mod tests { (".socket/vendor/.gitattributes", true), (".socket/vendor/gradle/g/a/maven-metadata.xml", true), (".socket/vendor/gradle/g/a/1/a-1.jar", false), - (".socket/vendor/gradle/g/a/1/socket-patch.vendor.json", false), + ( + ".socket/vendor/gradle/g/a/1/socket-patch.vendor.json", + false, + ), (".socket/vendor/npm/u/left-pad-1.3.0.tgz", false), (".socket/manifest.json", false), ("packages/a/.socket/vendor/npm/u/a.tgz", false), @@ -1437,10 +1440,16 @@ mod tests { if keep { group.rollback_to(savepoint); group.commit().await.unwrap(); - assert!(unit.join("a.tgz").exists(), "a rolled-back removal is forgotten"); + assert!( + unit.join("a.tgz").exists(), + "a rolled-back removal is forgotten" + ); } else { drop(group); - assert!(unit.join("a.tgz").exists(), "a dropped group deletes nothing"); + assert!( + unit.join("a.tgz").exists(), + "a dropped group deletes nothing" + ); } } @@ -1449,8 +1458,14 @@ mod tests { remove_tree_and_prune(&unit, &socket).await.unwrap(); group.commit().await.unwrap(); assert!(!unit.exists()); - assert!(!socket.join("vendor").exists(), "the emptied levels are pruned"); - assert!(socket.join("apply.lock").exists(), "`.socket/` itself stays"); + assert!( + !socket.join("vendor").exists(), + "the emptied levels are pruned" + ); + assert!( + socket.join("apply.lock").exists(), + "`.socket/` itself stays" + ); } /// A journal the commit had to create `.socket/vendor/` for (a hosted diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs b/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs index b6bc8df76..e24dbcf4f 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs @@ -999,7 +999,10 @@ async fn headerless_yarn_classic_lock_is_inventoried_as_classic_by_the_fallback( .filter(|l| !l.starts_with('#')) .collect::>() .join("\n"); - assert!(!headerless.contains("lockfile v1"), "fixture drops the header"); + assert!( + !headerless.contains("lockfile v1"), + "fixture drops the header" + ); write(tmp.path(), "yarn.lock", &headerless).await; let (flavor, entries) = inventory_npm_lock(tmp.path()).await.unwrap().unwrap(); assert_eq!(flavor, NpmLockFlavor::YarnClassic); diff --git a/crates/socket-patch-core/src/vendor/mod.rs b/crates/socket-patch-core/src/vendor/mod.rs index 0e9477a15..095389c80 100644 --- a/crates/socket-patch-core/src/vendor/mod.rs +++ b/crates/socket-patch-core/src/vendor/mod.rs @@ -129,8 +129,8 @@ pub use verify::{ }; // The hosted→vendored takeover refuses a berry project the backend would // refuse BEFORE it reverts the hosted redirect. -pub use npm_lock::npm_lock_vendor_preflight; pub use npm_common::npm_tarball_gitignore_preflight; +pub use npm_lock::npm_lock_vendor_preflight; pub use yarn_berry_lock::{yarn_berry_vendor_preflight, yarn_berry_vendor_target_preflight}; use std::collections::{HashMap, HashSet}; diff --git a/crates/socket-patch-core/src/vendor/yarn_berry_lock.rs b/crates/socket-patch-core/src/vendor/yarn_berry_lock.rs index da654820e..faf382046 100644 --- a/crates/socket-patch-core/src/vendor/yarn_berry_lock.rs +++ b/crates/socket-patch-core/src/vendor/yarn_berry_lock.rs @@ -46,9 +46,7 @@ use sha2::{Digest, Sha256, Sha512}; use crate::constants::SOCKET_DIR; use crate::formats::yarn::berry_entry::{manifest_bin, render_pinned_entry, Pin}; use crate::formats::yarn::berry_gates::{self, BerryGate, Yarnrc, SUPPORTED_CACHE_KEY}; -use crate::formats::yarn::blocks::{ - berry_field, block_eol, replace_block, scan_blocks, LockBlock, -}; +use crate::formats::yarn::blocks::{berry_field, block_eol, replace_block, scan_blocks, LockBlock}; use crate::formats::yarn::patterns::{pattern_real_name, split_berry_key_patterns, split_pattern}; use crate::manifest::schema::PatchRecord; use crate::patch::apply::{normalize_file_path, PatchSources}; diff --git a/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs b/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs index 5e650c3d4..766186131 100644 --- a/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs +++ b/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs @@ -1512,11 +1512,7 @@ left-pad@^1.3.0: .filter(|&c| c != "vendor_prebuilt_downloaded") .collect(); assert_eq!(codes, ["vendor_yarn_classic_non_registry_legacy_wiring"]); - let detail = &warnings - .iter() - .find(|w| w.code == codes[0]) - .unwrap() - .detail; + let detail = &warnings.iter().find(|w| w.code == codes[0]).unwrap().detail; assert!( detail.contains("host.test/fork") && detail.contains("vendor --revert") diff --git a/crates/socket-patch-core/src/vex/product.rs b/crates/socket-patch-core/src/vex/product.rs index 3e00f563f..b6dd365e4 100644 --- a/crates/socket-patch-core/src/vex/product.rs +++ b/crates/socket-patch-core/src/vex/product.rs @@ -1425,7 +1425,9 @@ mod tests { async fn detect_git_remote_handles_non_existent_start_path() { let dir = tempfile::tempdir().unwrap(); let nonexistent = dir.path().join("does/not/exist"); - assert!(detect_git_remote(&nonexistent, &mut Vec::new()).await.is_none()); + assert!(detect_git_remote(&nonexistent, &mut Vec::new()) + .await + .is_none()); } /// B22: inside a submodule (`.git` is a `gitdir:` FILE), the product is From 93e1993b9c39b7b44e0307255a41e4e75c4fd54d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 02:44:21 +0000 Subject: [PATCH 3/4] Count shadowed hosted pins as already applied A capped scan (--max-new-patches 0, SOCKET_MAX_NEW_PATCHES=0 or socket.yml maxNewPatches: 0) deferred an already-applied hosted patch as NEW once the project gained a second, unpinned copy of the same version: an npm: alias, an unpinned npm-shrinkwrap.json twin, a bundled copy. Lockfile discovery rightly stops attesting such a pin, but the rollout read "not attestable" as "nothing recorded", so the re-scan that would rewire the copy never ran and the remedy vex prints looped forever (#1195, regression from #1058). Discovery now keeps every ref it drops over an unpatched copy beside it in Discovery::shadowed (npm's pair, same-lock, bundled and legacy mirror rules, Bun and vlt copies, and the shared within-lock and cross-lock contests). HostedPin::recorded adds those to the attributable pins, and the disk and in-memory rollouts build their recorded view from it. Attestation and management commands still use HostedPin::all and are unchanged. Fixes #1195 Assisted-by: Claude Code:claude-opus-5-5 --- .../src/commands/scan/hosted.rs | 9 +- .../socket-patch-cli/src/commands/scan/mod.rs | 16 ++- .../src/hosted/memory/mod.rs | 17 +-- .../src/patch/redirect/upstream/mod.rs | 31 ++++- .../socket-patch-core/src/vex/discover/bun.rs | 6 + .../socket-patch-core/src/vex/discover/mod.rs | 38 ++++-- .../socket-patch-core/src/vex/discover/npm.rs | 124 ++++++++++++++++++ .../src/vex/discover/testing/golden.rs | 15 +++ .../socket-patch-core/src/vex/discover/vlt.rs | 5 + .../vex-discover-golden/redirect-npm.json | 8 ++ 10 files changed, 243 insertions(+), 26 deletions(-) diff --git a/crates/socket-patch-cli/src/commands/scan/hosted.rs b/crates/socket-patch-cli/src/commands/scan/hosted.rs index 11bee9f8d..4b1e66f5a 100644 --- a/crates/socket-patch-cli/src/commands/scan/hosted.rs +++ b/crates/socket-patch-cli/src/commands/scan/hosted.rs @@ -845,10 +845,11 @@ pub(crate) async fn run_redirect_selected( ); if !foreign.is_empty() { let origins: Vec = configured.into_iter().chain(foreign).collect(); - let pins = socket_patch_core::patch::redirect::upstream::HostedPin::discover( - view, &origins, - ) - .await; + let pins = + socket_patch_core::patch::redirect::upstream::HostedPin::discover_recorded( + view, &origins, + ) + .await; super::rollout::mark_pinned(&mut gate.rows, &pins); } } diff --git a/crates/socket-patch-cli/src/commands/scan/mod.rs b/crates/socket-patch-cli/src/commands/scan/mod.rs index 2a73c0d74..b777223b8 100644 --- a/crates/socket-patch-cli/src/commands/scan/mod.rs +++ b/crates/socket-patch-cli/src/commands/scan/mod.rs @@ -1917,10 +1917,18 @@ async fn run_scan( let hosted_state = (!args.common.is_global()) .then(|| crate::commands::hosted_state_from_pins(&hosted_pin_list)); let redirect_state = hosted_state.as_ref(); - let hosted_pins: Vec<(String, String)> = hosted_pin_list - .iter() - .map(|pin| (pin.purl.clone(), pin.uuid.clone())) - .collect(); + // The recorded view also counts the pins discovery withheld over an + // unpatched copy beside them (`HostedPin::recorded`): still this + // project's patch, so a capped re-scan reads ALREADY and rewires the + // copy instead of deferring it as NEW (#1195). + let hosted_pins: Vec<(String, String)> = if args.common.is_global() { + Vec::new() + } else { + socket_patch_core::patch::redirect::upstream::HostedPin::recorded(ctx.discovery().await) + .into_iter() + .map(|pin| (pin.purl, pin.uuid)) + .collect() + }; let update_manifest = merge_ledger_records_for_updates( existing_manifest, vendor_state.as_ref().ok().filter(|_| project_state), diff --git a/crates/socket-patch-core/src/hosted/memory/mod.rs b/crates/socket-patch-core/src/hosted/memory/mod.rs index e2c308075..b84f6fda3 100644 --- a/crates/socket-patch-core/src/hosted/memory/mod.rs +++ b/crates/socket-patch-core/src/hosted/memory/mod.rs @@ -417,18 +417,19 @@ async fn memory_recorded(project: &MemoryProject, root: &str, roots: &[String]) .filter(|rel| rel != "/") .collect(); let own = |file: &String| !nested.iter().any(|n| file.starts_with(n.as_str())); - // The same discovery `HostedPin::discover` runs, kept whole so its - // lockless pins (never refs) count as recorded too. + // The same discovery `HostedPin::discover_recorded` runs, kept whole so + // its lockless pins (never refs) count as recorded too. let discovery = crate::vex::discover::discover_patched_refs_view( ProjectView::Memory(project), &crate::vex::DiscoverOptions::default(), ) .await; - let pins: Vec<(String, String)> = crate::patch::redirect::upstream::HostedPin::all(&discovery) - .into_iter() - .filter(|pin| pin.files.iter().any(own)) - .map(|pin| (pin.purl, pin.uuid)) - .collect(); + let pins: Vec<(String, String)> = + crate::patch::redirect::upstream::HostedPin::recorded(&discovery) + .into_iter() + .filter(|pin| pin.files.iter().any(own)) + .map(|pin| (pin.purl, pin.uuid)) + .collect(); let unlocked = discovery .unlocked_pins .into_iter() @@ -849,7 +850,7 @@ async fn engine( if origins.is_empty() { continue; } - let pins = crate::patch::redirect::upstream::HostedPin::discover( + let pins = crate::patch::redirect::upstream::HostedPin::discover_recorded( ProjectView::Memory(&plan.project), &origins, ) diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs index 9dd2ad713..5b42188be 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/mod.rs @@ -87,6 +87,19 @@ impl HostedPin { Self::from_refs(&discovery.refs) } + /// Every hosted pin the project RECORDS: [`HostedPin::all`] plus the + /// pins discovery withheld because an unpatched copy of the same + /// version installs beside them ([`Discovery::shadowed`]: an `npm:` + /// alias added after the pin, an unpinned twin lock, a bundled copy). + /// Such a pin attests nothing and management commands do not act on + /// it, but it is still the project's patch for that package, so the + /// rollout's recorded view reads it as ALREADY (or an UPGRADE), never + /// NEW: a capped re-scan then rewires the stray copy instead of + /// deferring it forever (#1195). + pub fn recorded(discovery: &Discovery) -> Vec { + Self::from_refs(discovery.refs.iter().chain(&discovery.shadowed)) + } + /// THE "is this patch pinned" answer: the attributable hosted pins /// lockfile discovery reads through `view` (the disk, a snapshot of it /// overlaid with a pending rewrite, or an in-memory project), with @@ -99,10 +112,26 @@ impl HostedPin { view: crate::vendor::lock_inventory::ProjectView<'_>, origins: &[String], ) -> Vec { + Self::all(&Self::discovery(view, origins).await) + } + + /// [`HostedPin::discover`]'s rollout twin: the pins the project records + /// ([`HostedPin::recorded`]), withheld ones included. + pub async fn discover_recorded( + view: crate::vendor::lock_inventory::ProjectView<'_>, + origins: &[String], + ) -> Vec { + Self::recorded(&Self::discovery(view, origins).await) + } + + async fn discovery( + view: crate::vendor::lock_inventory::ProjectView<'_>, + origins: &[String], + ) -> Discovery { let opts = crate::vex::DiscoverOptions { patch_server_origins: origins.to_vec(), }; - Self::all(&crate::vex::discover::discover_patched_refs_view(view, &opts).await) + crate::vex::discover::discover_patched_refs_view(view, &opts).await } /// `(name, version)` of the purl, percent-decoded. diff --git a/crates/socket-patch-core/src/vex/discover/bun.rs b/crates/socket-patch-core/src/vex/discover/bun.rs index b219ebaae..00b6d7894 100644 --- a/crates/socket-patch-core/src/vex/discover/bun.rs +++ b/crates/socket-patch-core/src/vex/discover/bun.rs @@ -239,6 +239,7 @@ impl Bundled { r.purl, ), ); + out.shadow(r); } } } @@ -464,6 +465,7 @@ impl Unwired { r.purl, ), ); + out.shadow(r); } } } @@ -752,6 +754,9 @@ mod tests { let out = run(&p).await; assert!(out.refs.is_empty(), "{label}: {:#?}", out.refs); assert_eq!(contests(&out), 1, "{label}: {:#?}", out.diagnostics); + // Still a pin the lock records (#1195). + let shadowed: Vec<_> = out.shadowed.iter().map(|r| r.uuid.as_str()).collect(); + assert_eq!(shadowed, [UUID_A], "{label}"); let p = Project::new(); p.write( @@ -767,6 +772,7 @@ mod tests { let out = run(&p).await; assert_eq!(out.refs.len(), 1, "{label} control: {:#?}", out.refs); assert_eq!(contests(&out), 0, "{label} control: {:#?}", out.diagnostics); + assert!(out.shadowed.is_empty(), "{label} control"); } } diff --git a/crates/socket-patch-core/src/vex/discover/mod.rs b/crates/socket-patch-core/src/vex/discover/mod.rs index 3a371222a..f3875f2fe 100644 --- a/crates/socket-patch-core/src/vex/discover/mod.rs +++ b/crates/socket-patch-core/src/vex/discover/mod.rs @@ -555,6 +555,16 @@ pub struct Discovery { pub unwired_copies: Vec, /// Refs dropped because another lock contests them ([`ContestedRef`]). pub contested: Vec, + /// Every ref dropped from `refs` because an unpatched copy of the same + /// `name@version` installs beside it (the npm pair, same-lock and + /// bundled rules, [`Discovery::contest_within_locks`], + /// [`Discovery::contest_across_locks`]). Never attested, but still a + /// pin a live lock records: the rollout's recorded view counts it + /// ([`HostedPin::recorded`]), so a re-scan that rewires the stray copy + /// is not capped as NEW (#1195). Sorted like `refs`. + /// + /// [`HostedPin::recorded`]: crate::patch::redirect::upstream::HostedPin::recorded + pub shadowed: Vec, /// The bundled copies (purl → root-relative directory) the vlt /// extractor found in the installed store for the lock's nodes, exactly /// as [`crate::vendor::vlt_bundled::bundled_copies`] reports them: the @@ -792,12 +802,19 @@ impl Discovery { r.purl, r.uuid, c.key, c.how ), ); + self.shadow(r); } None => self.refs.push(r), } } } + /// Record a ref dropped from `refs` over an unpatched copy beside it + /// ([`Discovery::shadowed`]). + pub(crate) fn shadow(&mut self, r: PatchedRef) { + self.shadowed.push(r); + } + /// Drop every ref that ANOTHER lock contests: a lock that resolves the /// same package at the same version from a non-Socket source /// ([`Discovery::resolved_elsewhere`]) while wiring it to no patch @@ -863,6 +880,7 @@ impl Discovery { other.display() ), ); + self.shadow(r); } } @@ -1018,15 +1036,17 @@ impl Discovery { self.unattested.dedup(); self.contested.sort(); self.contested.dedup(); - self.refs.sort_by(|a, b| { - (&a.source_file, &a.purl, &a.uuid, a.mode).cmp(&( - &b.source_file, - &b.purl, - &b.uuid, - b.mode, - )) - }); - self.refs.dedup(); + for refs in [&mut self.refs, &mut self.shadowed] { + refs.sort_by(|a, b| { + (&a.source_file, &a.purl, &a.uuid, a.mode).cmp(&( + &b.source_file, + &b.purl, + &b.uuid, + b.mode, + )) + }); + refs.dedup(); + } self.diagnostics .sort_by(|a, b| (&a.file, a.code, &a.detail).cmp(&(&b.file, b.code, &b.detail))); self.recognized.sort(); diff --git a/crates/socket-patch-core/src/vex/discover/npm.rs b/crates/socket-patch-core/src/vex/discover/npm.rs index 00ee740e6..d5ec1042f 100644 --- a/crates/socket-patch-core/src/vex/discover/npm.rs +++ b/crates/socket-patch-core/src/vex/discover/npm.rs @@ -153,6 +153,7 @@ fn push_uncontested(locks: Vec, out: &mut Discovery) { lock.file, r.purl, r.uuid, ), ); + out.shadow(r.clone()); continue; } if let Some(location) = lock.unwired.get(&r.purl) { @@ -168,6 +169,7 @@ fn push_uncontested(locks: Vec, out: &mut Discovery) { lock.file, r.purl, r.uuid, ), ); + out.shadow(r.clone()); continue; } let contested_by = locks.iter().enumerate().find(|(j, other)| { @@ -186,6 +188,7 @@ fn push_uncontested(locks: Vec, out: &mut Discovery) { lock.file, r.purl, r.uuid, other.file, NPM_LOCKS[0], NPM_LOCKS[1], ), ); + out.shadow(r.clone()); } else if let Some(other) = locks .iter() .enumerate() @@ -205,6 +208,7 @@ fn push_uncontested(locks: Vec, out: &mut Discovery) { lock.file, r.purl, r.uuid, other.file, NPM_LOCKS[0], NPM_LOCKS[1], ), ); + out.shadow(r.clone()); } else { out.push(r.clone()); } @@ -323,6 +327,7 @@ fn drop_mirror_unwired( r.purl, r.uuid ), ); + out.shadow(r.clone()); false }); } @@ -380,6 +385,7 @@ fn drop_non_registry_installs( r.purl ), ); + out.shadow(r.clone()); false }); } @@ -1776,6 +1782,124 @@ mod tests { ); } + /// REGRESSION (#1195): every rule that withholds a ref over an unpatched + /// copy beside it (another entry of the same lock, a bundled copy, the + /// npm twin lock resolving it elsewhere or not at all, a pnpm `file:` + /// copy; another manager's lock is the vlt golden's + /// `sibling-package-lock-vlt` case) keeps the ref in `shadowed`, so the + /// rollout's recorded view (`HostedPin::recorded`) still reads the + /// patch as the project's while the attributable pins + /// (`HostedPin::all`) and attestation do not. A wired lock with no + /// stray copy shadows nothing. + #[tokio::test] + async fn issue_1195_withheld_refs_stay_recorded_pins() { + use crate::patch::redirect::upstream::HostedPin; + let hosted = hosted_url("npm", "left-pad", "1.3.0", UUID_A, "left-pad-1.3.0.tgz"); + let registry = "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz"; + let wired = serde_json::json!({ "version": "1.3.0", "resolved": hosted, "integrity": SRI }); + let upstream = serde_json::json!({ "version": "1.3.0", "resolved": registry, "integrity": "sha512-ORIG" }); + let pnpm_wired = format!( + "lockfileVersion: '9.0'\n\npackages:\n\n left-pad@1.3.0:\n \ + resolution: {{integrity: {SRI}, tarball: {hosted}}}\n\n" + ); + let pnpm_file = " left-pad@file:forks/left-pad:\n \ + resolution: {directory: forks/left-pad, type: directory}\n\n"; + let pinned_lock = lock_with_packages(serde_json::json!({ "node_modules/left-pad": wired })); + let cases: Vec<(&str, Vec<(&str, String)>)> = vec![ + ( + "npm: alias of the same version in the same lock", + vec![( + "package-lock.json", + lock_with_packages(serde_json::json!({ + "node_modules/left-pad": wired, + "node_modules/lp": { + "name": "left-pad", "version": "1.3.0", + "resolved": registry, "integrity": "sha512-ORIG" + }, + })), + )], + ), + ( + "bundled copy", + vec![( + "package-lock.json", + lock_with_packages(serde_json::json!({ + "node_modules/left-pad": wired, + "node_modules/bund/node_modules/left-pad": { + "version": "1.3.0", "inBundle": true, + }, + })), + )], + ), + ( + "twin lock resolving it from the registry", + vec![ + ("package-lock.json", pinned_lock.clone()), + ( + "npm-shrinkwrap.json", + lock_with_packages( + serde_json::json!({ "node_modules/left-pad": upstream }), + ), + ), + ], + ), + ( + "twin lock without the package", + vec![ + ("package-lock.json", pinned_lock.clone()), + ( + "npm-shrinkwrap.json", + lock_with_packages(serde_json::json!({})), + ), + ], + ), + ( + "pnpm file: copy in the same lock", + vec![ + ("pnpm-lock.yaml", format!("{pnpm_wired}{pnpm_file}")), + ( + "forks/left-pad/package.json", + r#"{"name":"left-pad","version":"1.3.0"}"#.to_string(), + ), + ], + ), + ]; + let pin = HostedPin { + purl: "pkg:npm/left-pad@1.3.0".into(), + uuid: UUID_A.into(), + files: Vec::new(), + }; + let pairs = |pins: Vec| -> Vec<(String, String)> { + pins.into_iter().map(|p| (p.purl, p.uuid)).collect() + }; + for (case, files) in cases { + let p = Project::new(); + for (file, text) in &files { + p.write(file, text); + } + let out = run(&p).await; + assert!(out.refs.is_empty(), "{case}: {:#?}", out.refs); + assert!(HostedPin::all(&out).is_empty(), "{case}"); + assert_eq!( + pairs(HostedPin::recorded(&out)), + [(pin.purl.clone(), pin.uuid.clone())], + "{case}: {:#?}", + out.diagnostics + ); + } + + // Control: the pin alone is attributable and shadows nothing. + let p = Project::new(); + p.write("package-lock.json", pinned_lock); + let out = run(&p).await; + assert!(out.shadowed.is_empty(), "{:#?}", out.shadowed); + assert_eq!( + pairs(HostedPin::all(&out)), + pairs(HostedPin::recorded(&out)) + ); + assert_eq!(pairs(HostedPin::all(&out)), [(pin.purl, pin.uuid)]); + } + /// #490: a git edge the project's `overrides` send to the registry is /// a registry install, so its Socket wiring is attested. #[tokio::test] diff --git a/crates/socket-patch-core/src/vex/discover/testing/golden.rs b/crates/socket-patch-core/src/vex/discover/testing/golden.rs index b3add1fc1..d6b799610 100644 --- a/crates/socket-patch-core/src/vex/discover/testing/golden.rs +++ b/crates/socket-patch-core/src/vex/discover/testing/golden.rs @@ -130,6 +130,7 @@ fn render(out: &Discovery, root: &Path) -> Value { install_trees, read: _, withheld: _, + shadowed, // Already folded into `unattested` by the time a run returns. unwired_copies: _, } = out; @@ -302,6 +303,20 @@ fn render(out: &Discovery, root: &Path) -> Value { .collect::>() .into(); } + if !shadowed.is_empty() { + rendered["shadowed"] = shadowed + .iter() + .map(|r| { + json!({ + "purl": r.purl, + "uuid": r.uuid, + "mode": mode(r.mode), + "source_file": path_str(&r.source_file), + }) + }) + .collect::>() + .into(); + } if !install_trees.is_empty() { rendered["install_trees"] = install_trees .iter() diff --git a/crates/socket-patch-core/src/vex/discover/vlt.rs b/crates/socket-patch-core/src/vex/discover/vlt.rs index 660bd35c1..29efec45e 100644 --- a/crates/socket-patch-core/src/vex/discover/vlt.rs +++ b/crates/socket-patch-core/src/vex/discover/vlt.rs @@ -283,6 +283,7 @@ async fn contest_bundled_copies(ctx: &DiscoverCtx<'_>, nodes: &[VltLockNode], ou r.purl, ), ); + out.shadow(r); } for purl in copies.into_keys() { out.resolved_elsewhere(VLT_LOCK, Some(purl)); @@ -1051,9 +1052,13 @@ mod tests { "the diagnostic names the copy: {:?}", diag_codes(&out) ); + // Still a pin the lock records (#1195). + let shadowed: Vec<_> = out.shadowed.iter().map(|r| r.uuid.as_str()).collect(); + assert_eq!(shadowed, [uuid], "{mode:?}"); } else { assert_refs(&out, &[(PURL, uuid, mode)]); assert_eq!(bundled_contests(&out), 0, "{:?}", diag_codes(&out)); + assert!(out.shadowed.is_empty(), "{mode:?}: {:?}", out.shadowed); } } } diff --git a/crates/socket-patch-core/tests/fixtures/vex-discover-golden/redirect-npm.json b/crates/socket-patch-core/tests/fixtures/vex-discover-golden/redirect-npm.json index 6b78afbec..0aab3b7d3 100644 --- a/crates/socket-patch-core/tests/fixtures/vex-discover-golden/redirect-npm.json +++ b/crates/socket-patch-core/tests/fixtures/vex-discover-golden/redirect-npm.json @@ -6075,6 +6075,14 @@ "file": "package-lock.json", "other": "vlt-lock.json" } + ], + "shadowed": [ + { + "purl": "pkg:npm/ms@2.1.3", + "uuid": "22222222-2222-4222-8222-222222222222", + "mode": "hosted", + "source_file": "package-lock.json" + } ] }, "redirect/npm/vlt/sibling-package-lock-vlt-installed/input": { From 328fb3573dd9d2086e5f3439049ea0096dc289c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 03:57:46 +0000 Subject: [PATCH 4/4] Restore files this branch only reformatted Running cargo fmt during the merge reformatted 16 files main carries unformatted. They are outside this fix, so take main's bytes back and keep the diff to the rollout change. Assisted-by: Claude Code:claude-opus-5-5 --- .../tests/e2e_redirect_yarn_classic_build.rs | 16 ++----- .../tests/e2e_socket_yml_policy.rs | 8 +--- .../tests/e2e_vendor_yarn_berry_build.rs | 47 +++---------------- .../tests/in_process_redirect_pipenv.rs | 3 +- .../tests/in_process_redirect_pnpm.rs | 16 ++----- .../tests/mode_migration_pypi.rs | 15 ++---- .../src/crawlers/cargo_crawler.rs | 3 +- crates/socket-patch-core/src/formats/mod.rs | 2 +- .../src/patch/redirect/mod.rs | 27 ++++------- .../src/patch/redirect/poetry.rs | 12 ++--- .../src/patch/redirect/upstream/cargo.rs | 5 +- .../src/utils/group_commit.rs | 25 ++-------- .../src/vendor/lock_inventory/tests.rs | 5 +- crates/socket-patch-core/src/vendor/mod.rs | 2 +- .../src/vendor/yarn_classic_lock.rs | 6 ++- crates/socket-patch-core/src/vex/product.rs | 4 +- 16 files changed, 48 insertions(+), 148 deletions(-) diff --git a/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs b/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs index 7f277f70c..e36d0fbbf 100644 --- a/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs +++ b/crates/socket-patch-cli/tests/e2e_redirect_yarn_classic_build.rs @@ -900,10 +900,7 @@ async fn classic_offline_mirror_refuses_hosted_and_keeps_installs_working() { return; }; assert!( - fx.proj - .join("mirror") - .join(format!("{DEP}-{DEP_VERSION}.tgz")) - .is_file(), + fx.proj.join("mirror").join(format!("{DEP}-{DEP_VERSION}.tgz")).is_file(), "the fixture install must populate the offline mirror" ); let fresh = fx.tmp.path().join("fresh"); @@ -929,11 +926,7 @@ async fn classic_offline_mirror_refuses_hosted_and_keeps_installs_working() { String::from_utf8_lossy(&ci.stderr) ); assert!( - !fresh - .join("node_modules") - .join(DEP) - .join("index.js") - .exists(), + !fresh.join("node_modules").join(DEP).join("index.js").exists(), "yarn < 1.7 is expected to install nothing from the mirror" ); return; @@ -955,10 +948,7 @@ async fn classic_offline_mirror_refuses_hosted_and_keeps_installs_working() { ); let installed = std::fs::read(fresh.join("node_modules").join(DEP).join("index.js")).unwrap(); - assert_eq!( - installed, fx.orig, - "the untouched lock installs the upstream bytes" - ); + assert_eq!(installed, fx.orig, "the untouched lock installs the upstream bytes"); std::fs::remove_dir_all(fresh.join("node_modules")).unwrap(); } } diff --git a/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs b/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs index f9322ae68..16ea58d3b 100644 --- a/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs +++ b/crates/socket-patch-cli/tests/e2e_socket_yml_policy.rs @@ -1100,9 +1100,7 @@ async fn agent_mode_retains_a_recorded_patch_the_policy_now_excludes() { async fn agent_mode_judges_nested_project_copies_by_their_own_root() { let server = MockServer::start().await; mount_api(&server, catalog()).await; - let repo = Repo::new(Some( - "version: 2\npatches:\n ignorePaths: [\"/services/legacy/\"]\n", - )); + let repo = Repo::new(Some("version: 2\npatches:\n ignorePaths: [\"/services/legacy/\"]\n")); // left-pad is installed in both web (admitted) and legacy (ignored). write_npm_root(&repo.dir("services/legacy"), &["gamma", "left-pad"]); let (code, doc) = scan_json(&repo.root, &server.uri(), &["--mode", "agent"], &[]); @@ -1171,9 +1169,7 @@ async fn agent_mode_judges_nested_project_copies_by_their_own_root() { // includePaths: the docs' headline example selects nested projects from // the repo root (which itself is not included). - let repo = Repo::new(Some( - "version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n", - )); + let repo = Repo::new(Some("version: 2\npatches:\n includePaths: [\"/services/legacy/\"]\n")); let (code, doc) = scan_json(&repo.root, &server.uri(), &["--mode", "agent"], &[]); assert_eq!(code, 0, "{doc:#}"); assert_eq!( diff --git a/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs b/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs index 6555371a1..b6edf019d 100644 --- a/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs +++ b/crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs @@ -729,14 +729,7 @@ async fn run_berry_capstone(driver: VendorDriver, yarnrc_extra: &str) { fn git(cwd: &Path, args: &[&str]) -> Output { let out = Command::new("git") - .args([ - "-c", - "user.name=t", - "-c", - "user.email=t@t", - "-c", - "init.defaultBranch=main", - ]) + .args(["-c", "user.name=t", "-c", "user.email=t@t", "-c", "init.defaultBranch=main"]) .args(args) .current_dir(cwd) .output() @@ -817,30 +810,16 @@ fn yarn_berry_vendored_tarball_survives_a_tgz_gitignore_rule() { }; let (code, stdout, stderr) = run_socket( &proj, - &[ - "vendor", - "--json", - "--offline", - "--cwd", - proj.to_str().unwrap(), - ], - ); - assert_eq!( - code, 0, - "vendor failed.\nstdout:\n{stdout}\nstderr:\n{stderr}" + &["vendor", "--json", "--offline", "--cwd", proj.to_str().unwrap()], ); + assert_eq!(code, 0, "vendor failed.\nstdout:\n{stdout}\nstderr:\n{stderr}"); git(&proj, &["add", "-A"]); git(&proj, &["commit", "-qm", "vendored"]); let fresh = tmp.path().join("fresh"); git( tmp.path(), - &[ - "clone", - "-q", - proj.to_str().unwrap(), - fresh.to_str().unwrap(), - ], + &["clone", "-q", proj.to_str().unwrap(), fresh.to_str().unwrap()], ); let fresh_global = tmp.path().join("fresh-yarn-global"); let ci = corepack( @@ -875,26 +854,14 @@ fn yarn_berry_vendor_refuses_a_gitignored_socket_dir() { let pkg_before = std::fs::read(proj.join("package.json")).unwrap(); let (code, stdout, stderr) = run_socket( &proj, - &[ - "vendor", - "--json", - "--offline", - "--cwd", - proj.to_str().unwrap(), - ], - ); - assert_eq!( - code, 1, - "vendor must fail.\nstdout:\n{stdout}\nstderr:\n{stderr}" + &["vendor", "--json", "--offline", "--cwd", proj.to_str().unwrap()], ); + assert_eq!(code, 1, "vendor must fail.\nstdout:\n{stdout}\nstderr:\n{stderr}"); assert!( stdout.contains("vendor_artifact_gitignored"), "refusal code expected:\n{stdout}" ); assert_eq!(std::fs::read(proj.join("yarn.lock")).unwrap(), lock_before); - assert_eq!( - std::fs::read(proj.join("package.json")).unwrap(), - pkg_before - ); + assert_eq!(std::fs::read(proj.join("package.json")).unwrap(), pkg_before); assert!(!proj.join(format!(".socket/vendor/npm/{UUID}")).exists()); } diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs index e4187d707..e31acf570 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pipenv.rs @@ -589,7 +589,8 @@ async fn live_pipfile_lock_conflict_vetoes_the_requirements_redirect() { #[tokio::test] #[serial] async fn platform_wheel_is_not_pinned_into_the_lock() { - assert_wheel_tag_is_not_pinned("cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64").await; + assert_wheel_tag_is_not_pinned("cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64") + .await; } /// #1048: a pure wheel bound to one interpreter (`cp311-none-any`) fails diff --git a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs index 189785fd8..d9e701976 100644 --- a/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs +++ b/crates/socket-patch-cli/tests/in_process_redirect_pnpm.rs @@ -432,10 +432,7 @@ async fn hosted_bom_lock_and_workspace_read_like_their_plain_twins() { assert_eq!(code, 0, "scan --mode hosted should succeed on a BOM lock"); let lock = std::fs::read_to_string(&lock_path).unwrap(); assert!(lock.starts_with("\u{feff}lockfileVersion:"), "{lock}"); - assert!( - lock.contains(HOSTED_URL), - "the BOM lock is redirected: {lock}" - ); + assert!(lock.contains(HOSTED_URL), "the BOM lock is redirected: {lock}"); let ws_path = tmp.path().join("pnpm-workspace.yaml"); assert_eq!( std::fs::read_to_string(&ws_path).ok().as_deref(), @@ -450,10 +447,7 @@ async fn hosted_bom_lock_and_workspace_read_like_their_plain_twins() { pristine, "rollback restores the BOM lock byte for byte" ); - assert!( - !ws_path.exists(), - "the auto-created workspace file goes too" - ); + assert!(!ws_path.exists(), "the auto-created workspace file goes too"); // A BOM workspace file whose first key is the user's opt-out: left // byte-identical (no duplicate `trustLockfile`), lock still redirected. @@ -479,11 +473,7 @@ async fn hosted_bom_lock_and_workspace_read_like_their_plain_twins() { "the lock is still redirected for {user_ws:?}" ); let ws = std::fs::read_to_string(tmp.path().join("pnpm-workspace.yaml")).unwrap(); - assert_eq!( - ws, - want.unwrap_or(user_ws), - "workspace file for {user_ws:?}" - ); + assert_eq!(ws, want.unwrap_or(user_ws), "workspace file for {user_ws:?}"); assert_eq!(ws.matches("trustLockfile").count(), 1, "{ws:?}"); } } diff --git a/crates/socket-patch-cli/tests/mode_migration_pypi.rs b/crates/socket-patch-cli/tests/mode_migration_pypi.rs index fb897ca00..1812583b9 100644 --- a/crates/socket-patch-cli/tests/mode_migration_pypi.rs +++ b/crates/socket-patch-cli/tests/mode_migration_pypi.rs @@ -1332,18 +1332,9 @@ async fn ledger_update_failure_changes_nothing() { set_mode(0o755); assert_eq!(code, 1, "{env:#}"); assert_eq!(env["status"], "error", "{env:#}"); - assert!( - !env.to_string().contains("redirect_takeover_unpatched"), - "{env:#}" - ); - assert_eq!( - std::fs::read(root.join("requirements.txt")).unwrap(), - vendored - ); - assert_eq!( - std::fs::read(root.join(".socket/vendor/state.json")).unwrap(), - state - ); + assert!(!env.to_string().contains("redirect_takeover_unpatched"), "{env:#}"); + assert_eq!(std::fs::read(root.join("requirements.txt")).unwrap(), vendored); + assert_eq!(std::fs::read(root.join(".socket/vendor/state.json")).unwrap(), state); assert!(root.join(format!(".socket/vendor/pypi/{UUID}")).exists()); } diff --git a/crates/socket-patch-core/src/crawlers/cargo_crawler.rs b/crates/socket-patch-core/src/crawlers/cargo_crawler.rs index 97c831a10..63a6cc5e8 100644 --- a/crates/socket-patch-core/src/crawlers/cargo_crawler.rs +++ b/crates/socket-patch-core/src/crawlers/cargo_crawler.rs @@ -379,7 +379,8 @@ fn read_crate_cargo_toml(crate_path: &Path, dir_name: &str) -> Option<(String, S let content = crate::utils::fs::read_regular_to_string_sync(&cargo_toml_path).ok()?; // Fallback: parse directory name as - - package_name_version(&content).or_else(|| CargoCrawler::parse_dir_name_version(dir_name)) + package_name_version(&content) + .or_else(|| CargoCrawler::parse_dir_name_version(dir_name)) } impl Default for CargoCrawler { diff --git a/crates/socket-patch-core/src/formats/mod.rs b/crates/socket-patch-core/src/formats/mod.rs index 319e37621..294f0f436 100644 --- a/crates/socket-patch-core/src/formats/mod.rs +++ b/crates/socket-patch-core/src/formats/mod.rs @@ -29,8 +29,8 @@ pub(crate) mod bun; pub mod cargo; pub mod composer; -pub mod gem; pub mod governing_locks; +pub mod gem; pub(crate) mod maven; pub(crate) mod nuget; pub mod pnpm; diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index 29480b0b8..10ad1ebe5 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -79,9 +79,9 @@ use crate::formats::yarn::patterns::{ use crate::formats::yarn::source::{classic_copy_source, CopySource}; use crate::formats::yarn::stanzas::{stanza_key, BerryStanzas}; #[cfg(test)] -mod platform_wheel_tests; -#[cfg(test)] mod pnpm_equivalence_tests; +#[cfg(test)] +mod platform_wheel_tests; mod poetry; mod pypi_takeover; pub use pypi_takeover::preflight_pypi_takeover; @@ -577,7 +577,7 @@ pub fn rewrite_registry_redirect_with_pipenv_version( bun_lockb_present, &std::collections::BTreeSet::new(), &std::collections::BTreeSet::new(), - &yarnrc::OuterYarnMirror::default(), + &yarnrc::OuterYarnMirror::default(), ) } @@ -6919,10 +6919,8 @@ fn rewrite_maven_pom( // One pass over the pom's repositories: `(id, url)` of each, which also // answers the per-dep URL-refresh check below while the pom is still // unchanged (a no-op rescan then never re-scans the pom per dep). - let original_repos: Vec<(String, Option)> = pom - .as_deref() - .map(maven_repository_ids_and_urls) - .unwrap_or_default(); + let original_repos: Vec<(String, Option)> = + pom.as_deref().map(maven_repository_ids_and_urls).unwrap_or_default(); let hosted_repo_generations: std::collections::BTreeSet = original_repos .iter() .filter_map(|(id, _)| generation::pin_name_uuid(id, false).map(str::to_string)) @@ -10994,10 +10992,7 @@ mod tests { &[(YARNRC_REL, "yarn-offline-mirror: false\n")], &[(YARNRC_REL, "yarn-offline-mirror:\n")], &[(YARNRC_REL, "yarn-offline-mirror \"\"\n")], - &[( - YARNRC_REL, - "yarn-offline-mirror-pruning true\n# yarn-offline-mirror ./m\n", - )], + &[(YARNRC_REL, "yarn-offline-mirror-pruning true\n# yarn-offline-mirror ./m\n")], &[(npmrc::NPMRC_REL, "[scope]\nyarn-offline-mirror=./m\n")], &[ (YARNRC_REL, "yarn-offline-mirror false\n"), @@ -11013,10 +11008,7 @@ mod tests { let mut r = RewriteResult::default(); rewrite_yarn_classic(&files, std::slice::from_ref(&ovr), &mut r); assert!(r.warnings.is_empty(), "{rcs:?}: {:?}", r.warnings); - assert!( - r.files["yarn.lock"].contains("http://p.test/lp.tgz"), - "{rcs:?}" - ); + assert!(r.files["yarn.lock"].contains("http://p.test/lp.tgz"), "{rcs:?}"); assert!(r.refused_yarn_classic_uuids.is_empty(), "{rcs:?}"); } } @@ -11041,10 +11033,7 @@ mod tests { rewrite_yarn_classic(&files, std::slice::from_ref(&other), &mut r); assert!(r.refused_yarn_classic_uuids.is_empty()); assert_eq!( - r.warnings - .iter() - .map(|w| w.code.as_str()) - .collect::>(), + r.warnings.iter().map(|w| w.code.as_str()).collect::>(), ["redirect_yarn_classic_entry_not_found"] ); } diff --git a/crates/socket-patch-core/src/patch/redirect/poetry.rs b/crates/socket-patch-core/src/patch/redirect/poetry.rs index 5ee901ff6..7a1b32d92 100644 --- a/crates/socket-patch-core/src/patch/redirect/poetry.rs +++ b/crates/socket-patch-core/src/patch/redirect/poetry.rs @@ -89,9 +89,7 @@ pub(super) fn rewrite_poetry( new: Some(Value::String(new)), }); } - result - .confirmed_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); if !stale_warned { if let Some(format) = *writer_format.get_or_insert_with(|| pre_1_4_writer(&content)) @@ -126,18 +124,14 @@ pub(super) fn rewrite_poetry( } // Already redirected to this artifact (idempotent re-scan). LockStep::Unchanged => { - result - .confirmed_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.confirmed_python_lock_uuids.insert(dep.patch_uuid.clone()); } LockStep::NotFound => result.warnings.push(RewriteWarning { code: "redirect_poetry_entry_not_found".into(), detail: format!("no {path} entry for {}@{}", dep.name, dep.version), }), LockStep::Refused(detail) => { - result - .refused_python_lock_uuids - .insert(dep.patch_uuid.clone()); + result.refused_python_lock_uuids.insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_poetry_lock_unsupported".into(), detail: format!("{path}: {detail}"), diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs index 2e9c01a90..54d4c8030 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/cargo.rs @@ -489,9 +489,6 @@ mod tests { let other = format!("log = {{ version = \"0.4.20\", registry = \"socket-patch-{C}\" }}\n"); let (outcome, after) = restore_b(&manifest(&other), &config).await; assert_eq!(outcome.restored().count(), 1, "{:?}", outcome.pins); - assert_eq!( - after.as_deref().map(str::trim_start), - Some(block(C).as_str()) - ); + assert_eq!(after.as_deref().map(str::trim_start), Some(block(C).as_str())); } } diff --git a/crates/socket-patch-core/src/utils/group_commit.rs b/crates/socket-patch-core/src/utils/group_commit.rs index d82737fda..3b483d55f 100644 --- a/crates/socket-patch-core/src/utils/group_commit.rs +++ b/crates/socket-patch-core/src/utils/group_commit.rs @@ -1335,10 +1335,7 @@ mod tests { (".socket/vendor/.gitattributes", true), (".socket/vendor/gradle/g/a/maven-metadata.xml", true), (".socket/vendor/gradle/g/a/1/a-1.jar", false), - ( - ".socket/vendor/gradle/g/a/1/socket-patch.vendor.json", - false, - ), + (".socket/vendor/gradle/g/a/1/socket-patch.vendor.json", false), (".socket/vendor/npm/u/left-pad-1.3.0.tgz", false), (".socket/manifest.json", false), ("packages/a/.socket/vendor/npm/u/a.tgz", false), @@ -1440,16 +1437,10 @@ mod tests { if keep { group.rollback_to(savepoint); group.commit().await.unwrap(); - assert!( - unit.join("a.tgz").exists(), - "a rolled-back removal is forgotten" - ); + assert!(unit.join("a.tgz").exists(), "a rolled-back removal is forgotten"); } else { drop(group); - assert!( - unit.join("a.tgz").exists(), - "a dropped group deletes nothing" - ); + assert!(unit.join("a.tgz").exists(), "a dropped group deletes nothing"); } } @@ -1458,14 +1449,8 @@ mod tests { remove_tree_and_prune(&unit, &socket).await.unwrap(); group.commit().await.unwrap(); assert!(!unit.exists()); - assert!( - !socket.join("vendor").exists(), - "the emptied levels are pruned" - ); - assert!( - socket.join("apply.lock").exists(), - "`.socket/` itself stays" - ); + assert!(!socket.join("vendor").exists(), "the emptied levels are pruned"); + assert!(socket.join("apply.lock").exists(), "`.socket/` itself stays"); } /// A journal the commit had to create `.socket/vendor/` for (a hosted diff --git a/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs b/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs index e24dbcf4f..b6bc8df76 100644 --- a/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs +++ b/crates/socket-patch-core/src/vendor/lock_inventory/tests.rs @@ -999,10 +999,7 @@ async fn headerless_yarn_classic_lock_is_inventoried_as_classic_by_the_fallback( .filter(|l| !l.starts_with('#')) .collect::>() .join("\n"); - assert!( - !headerless.contains("lockfile v1"), - "fixture drops the header" - ); + assert!(!headerless.contains("lockfile v1"), "fixture drops the header"); write(tmp.path(), "yarn.lock", &headerless).await; let (flavor, entries) = inventory_npm_lock(tmp.path()).await.unwrap().unwrap(); assert_eq!(flavor, NpmLockFlavor::YarnClassic); diff --git a/crates/socket-patch-core/src/vendor/mod.rs b/crates/socket-patch-core/src/vendor/mod.rs index bb4bee6f7..07318f258 100644 --- a/crates/socket-patch-core/src/vendor/mod.rs +++ b/crates/socket-patch-core/src/vendor/mod.rs @@ -129,8 +129,8 @@ pub use verify::{ }; // The hosted→vendored takeover refuses a berry project the backend would // refuse BEFORE it reverts the hosted redirect. -pub use npm_common::npm_tarball_gitignore_preflight; pub use npm_lock::npm_lock_vendor_preflight; +pub use npm_common::npm_tarball_gitignore_preflight; pub use yarn_berry_lock::{yarn_berry_vendor_preflight, yarn_berry_vendor_target_preflight}; use std::collections::{HashMap, HashSet}; diff --git a/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs b/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs index 4d6608fad..fb1fb2f17 100644 --- a/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs +++ b/crates/socket-patch-core/src/vendor/yarn_classic_lock.rs @@ -1501,7 +1501,11 @@ left-pad@^1.3.0: .filter(|&c| c != "vendor_prebuilt_downloaded") .collect(); assert_eq!(codes, ["vendor_yarn_classic_non_registry_legacy_wiring"]); - let detail = &warnings.iter().find(|w| w.code == codes[0]).unwrap().detail; + let detail = &warnings + .iter() + .find(|w| w.code == codes[0]) + .unwrap() + .detail; assert!( detail.contains("host.test/fork") && detail.contains("vendor --revert") diff --git a/crates/socket-patch-core/src/vex/product.rs b/crates/socket-patch-core/src/vex/product.rs index b6dd365e4..3e00f563f 100644 --- a/crates/socket-patch-core/src/vex/product.rs +++ b/crates/socket-patch-core/src/vex/product.rs @@ -1425,9 +1425,7 @@ mod tests { async fn detect_git_remote_handles_non_existent_start_path() { let dir = tempfile::tempdir().unwrap(); let nonexistent = dir.path().join("does/not/exist"); - assert!(detect_git_remote(&nonexistent, &mut Vec::new()) - .await - .is_none()); + assert!(detect_git_remote(&nonexistent, &mut Vec::new()).await.is_none()); } /// B22: inside a submodule (`.git` is a `gitdir:` FILE), the product is