Skip to content

Commit d4d7c16

Browse files
Merge branch 'main' into arch-fix/ui-text-cycles
2 parents fbf3104 + e2d9633 commit d4d7c16

33 files changed

Lines changed: 2381 additions & 447 deletions

‎crates/socket-patch-cli/CLI_CONTRACT.md‎

Lines changed: 4 additions & 4 deletions
Large diffs are not rendered by default.

‎crates/socket-patch-cli/src/commands/get.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ async fn filter_to_installed_purls(
856856
if mode != super::scan::ScanMode::Agent {
857857
present.extend(supplement.entries.iter().map(|e| PurlKey::new(&e.purl)));
858858
let vendored =
859-
super::scan::project_vendored_supplement(common, &[], &ctx.loaded().await.vendor)
859+
super::scan::project_vendored_supplement(&ctx, &[], &ctx.loaded().await.vendor)
860860
.await;
861861
present.extend(vendored.packages.iter().map(|p| PurlKey::new(&p.purl)));
862862
}

‎crates/socket-patch-cli/src/commands/scan/discovery.rs‎

Lines changed: 106 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,20 @@ pub(crate) struct LedgerSupplement {
173173
/// the ledger `run` already loaded (`vendor::load_state`).
174174
///
175175
/// That holds only while the lock still wires the artifact. An entry the
176-
/// lockfile in-use probe (the one the prune GC reverts by) answers
177-
/// `Some(false)` for is the dependency having left the lock — bumped or
176+
/// project no longer consumes ([`Discovery::vendor_entry_in_use`] is
177+
/// `Some(false)` — the verdict the prune GC reverts by, read from `ctx`'s
178+
/// discovery) is the dependency having left the lock — bumped or
178179
/// uninstalled — and is reported in [`LedgerSupplement::unwired`] instead:
179180
/// re-vendoring it would fail against a lock that no longer has it. `None`
180-
/// (no probe for the ecosystem, or no readable lock) keeps the entry.
181+
/// (no readable lock for the ecosystem) keeps the entry.
182+
///
183+
/// [`Discovery::vendor_entry_in_use`]: socket_patch_core::vex::discover::Discovery::vendor_entry_in_use
181184
pub(crate) async fn vendored_ledger_supplement(
182-
common: &GlobalArgs,
185+
ctx: &crate::commands::context::ProjectContext<'_>,
183186
crawled: &[socket_patch_core::crawlers::types::CrawledPackage],
184187
state: &std::io::Result<VendorState>,
185188
) -> LedgerSupplement {
189+
let common = ctx.common;
186190
let mut out = LedgerSupplement::default();
187191
if common.is_global() {
188192
return out;
@@ -226,7 +230,12 @@ pub(crate) async fn vendored_ledger_supplement(
226230
continue;
227231
}
228232
if let Some(entry) = entry {
229-
if crate::commands::vendor::dispatch_in_use_one(entry, &common.cwd).await == Some(false)
233+
if ctx
234+
.discovery()
235+
.await
236+
.vendor_entry_in_use(&common.cwd, entry)
237+
.await
238+
== Some(false)
230239
{
231240
out.unwired.push(ledger_key.clone());
232241
continue;
@@ -1120,9 +1129,13 @@ mod tests {
11201129
..GlobalArgs::default()
11211130
};
11221131
let state = socket_patch_core::vendor::load_state(root).await;
1123-
vendored_ledger_supplement(&args, crawled, &state)
1124-
.await
1125-
.packages
1132+
vendored_ledger_supplement(
1133+
&crate::commands::context::ProjectContext::new(&args),
1134+
crawled,
1135+
&state,
1136+
)
1137+
.await
1138+
.packages
11261139
}
11271140

11281141
/// A ledger entry vendored as `@3.0.2.0` is the crawled composer
@@ -1148,18 +1161,26 @@ mod tests {
11481161
cwd: tmp.path().to_path_buf(),
11491162
..GlobalArgs::default()
11501163
};
1151-
let out = vendored_ledger_supplement(&args, &[crawled], &Ok(state.clone()))
1152-
.await
1153-
.packages;
1164+
let out = vendored_ledger_supplement(
1165+
&crate::commands::context::ProjectContext::new(&args),
1166+
&[crawled],
1167+
&Ok(state.clone()),
1168+
)
1169+
.await
1170+
.packages;
11541171
assert!(
11551172
out.is_empty(),
11561173
"{:?}",
11571174
out.iter().map(|p| &p.purl).collect::<Vec<_>>()
11581175
);
11591176

1160-
let out = vendored_ledger_supplement(&args, &[], &Ok(state))
1161-
.await
1162-
.packages;
1177+
let out = vendored_ledger_supplement(
1178+
&crate::commands::context::ProjectContext::new(&args),
1179+
&[],
1180+
&Ok(state),
1181+
)
1182+
.await
1183+
.packages;
11631184
assert_eq!(
11641185
out.iter().map(|p| p.purl.as_str()).collect::<Vec<_>>(),
11651186
vec!["pkg:composer/psr/log@3.0.2.0"]
@@ -1229,19 +1250,82 @@ mod tests {
12291250
})
12301251
.to_string();
12311252
let state = npm_ledger_with_lock(tmp.path(), Some(&bumped)).await;
1232-
let out = vendored_ledger_supplement(&args(tmp.path()), &[], &state).await;
1253+
let out = vendored_ledger_supplement(
1254+
&crate::commands::context::ProjectContext::new(&args(tmp.path())),
1255+
&[],
1256+
&state,
1257+
)
1258+
.await;
12331259
assert!(out.packages.is_empty(), "{:?}", out.packages);
12341260
assert_eq!(out.unwired, vec!["pkg:npm/left-pad@1.3.0".to_string()]);
12351261

12361262
// Uninstalled: the lock has no left-pad at all.
12371263
let tmp = tempfile::tempdir().unwrap();
12381264
let removed = r#"{"name":"app","lockfileVersion":3,"packages":{"":{"name":"app"}}}"#;
12391265
let state = npm_ledger_with_lock(tmp.path(), Some(removed)).await;
1240-
let out = vendored_ledger_supplement(&args(tmp.path()), &[], &state).await;
1266+
let out = vendored_ledger_supplement(
1267+
&crate::commands::context::ProjectContext::new(&args(tmp.path())),
1268+
&[],
1269+
&state,
1270+
)
1271+
.await;
12411272
assert!(out.packages.is_empty(), "{:?}", out.packages);
12421273
assert_eq!(out.unwired, vec!["pkg:npm/left-pad@1.3.0".to_string()]);
12431274
}
12441275

1276+
/// B19: the supplement and the prune GC share one in-use verdict for
1277+
/// every ecosystem, not only npm/cargo/pypi-requirements. A COMPOSER
1278+
/// entry whose dependency composer.lock bumped to a registry release is
1279+
/// unwired — before, it was resurrected as a discovered package forever.
1280+
#[tokio::test]
1281+
async fn ledger_supplement_reports_a_bumped_composer_entry_unwired() {
1282+
const COMPOSER_PURL: &str = "pkg:composer/monolog/monolog@3.0.0";
1283+
let tmp = tempfile::tempdir().unwrap();
1284+
let root = tmp.path();
1285+
let leaf = format!(".socket/vendor/composer/{VENDORED_UUID}/monolog/monolog@3.0.0");
1286+
let entry: socket_patch_core::vendor::VendorEntry =
1287+
serde_json::from_value(serde_json::json!({
1288+
"ecosystem": "composer",
1289+
"basePurl": COMPOSER_PURL,
1290+
"uuid": VENDORED_UUID,
1291+
"artifact": {"path": leaf, "sha256": ""},
1292+
"wiring": [],
1293+
"detached": true,
1294+
}))
1295+
.unwrap();
1296+
let mut state = VendorState::default();
1297+
state.entries.insert(COMPOSER_PURL.to_string(), entry);
1298+
std::fs::write(
1299+
root.join("composer.lock"),
1300+
serde_json::json!({
1301+
"packages": [{
1302+
"name": "monolog/monolog",
1303+
"version": "3.1.0",
1304+
"dist": {
1305+
"type": "zip",
1306+
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/abc",
1307+
"reference": "abc",
1308+
},
1309+
}],
1310+
"packages-dev": [],
1311+
})
1312+
.to_string(),
1313+
)
1314+
.unwrap();
1315+
let args = GlobalArgs {
1316+
cwd: root.to_path_buf(),
1317+
..GlobalArgs::default()
1318+
};
1319+
let out = vendored_ledger_supplement(
1320+
&crate::commands::context::ProjectContext::new(&args),
1321+
&[],
1322+
&Ok(state),
1323+
)
1324+
.await;
1325+
assert!(out.packages.is_empty(), "{:?}", out.packages);
1326+
assert_eq!(out.unwired, vec![COMPOSER_PURL.to_string()]);
1327+
}
1328+
12451329
/// The fresh-clone case the supplement exists for: the lock still
12461330
/// resolves through the committed artifact, so the entry stays
12471331
/// discoverable. With no lock at all, nothing proves the entry unused,
@@ -1260,7 +1344,12 @@ mod tests {
12601344
..GlobalArgs::default()
12611345
};
12621346
let state = npm_ledger_with_lock(tmp.path(), lock.as_deref()).await;
1263-
let out = vendored_ledger_supplement(&args, &[], &state).await;
1347+
let out = vendored_ledger_supplement(
1348+
&crate::commands::context::ProjectContext::new(&args),
1349+
&[],
1350+
&state,
1351+
)
1352+
.await;
12641353
assert_eq!(
12651354
out.packages
12661355
.iter()

‎crates/socket-patch-cli/src/commands/scan/mod.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,8 +1825,7 @@ async fn run_scan(
18251825
// supplement falls back to the committed artifacts (fail-closed for the
18261826
// prune), the key set degrades to empty (fail-open).
18271827
let vendor_state = &ctx.loaded().await.vendor;
1828-
let ledger_supplement =
1829-
vendored_ledger_supplement(&args.common, &all_crawled, vendor_state).await;
1828+
let ledger_supplement = vendored_ledger_supplement(&ctx, &all_crawled, vendor_state).await;
18301829
for pkg in &ledger_supplement.packages {
18311830
if let Some(eco) = Ecosystem::from_purl(&pkg.purl) {
18321831
*eco_counts.entry(eco).or_insert(0) += 1;

0 commit comments

Comments
 (0)