Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions crates/socket-patch-cli/src/commands/vendor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ use crate::commands::apply::{representative_file, result_to_event, variant_match
use crate::commands::fetch_stage::{stage_vendor_sources_in_memory, MemStageOutcome};
use crate::commands::lock_cli::acquire_or_emit;
use crate::commands::vex::{generate_vex_from_manifest_path, VexEmbedArgs};
use crate::ecosystem_dispatch::{find_packages_for_purls, partition_purls};
use crate::ecosystem_dispatch::{find_packages_for_rollback, partition_purls};
use crate::json_envelope::{
Command, Envelope, EnvelopeError, PatchAction, PatchEvent, RunWarning, Status, VexSummary,
};
Expand Down Expand Up @@ -681,7 +681,18 @@ pub(crate) async fn vendor_records(
global: common.global,
global_prefix: common.global_prefix.clone(),
};
let mut all_packages = find_packages_for_purls(
// Resolve installed packages with the qualified-purl-aware resolver, NOT
// `find_packages_for_purls`: the manifest keys release-variant ecosystems
// (gem `?platform=`, pypi `?artifact_id=`, maven `?classifier=&ext=`) by
// *qualified* purls, but the crawler only knows the *base* purl.
// `find_packages_for_purls` keys the result map by the base purl, so the
// `missing`/`contains_key` check below would miss every installed
// qualified-purl package and falsely classify it "not installed" —
// triggering a spurious `vendor_fetched_missing`, a redundant per-run
// registry download, and (for gem) a HashMap-order platform coin-flip.
// The rollback variant fans each base path back out to every qualified
// manifest purl (same invariant as `find_manifest_package_paths`).
let mut all_packages = find_packages_for_rollback(
&vendorable_partition,
&crawler_options,
common.silent || common.json,
Expand Down
47 changes: 47 additions & 0 deletions crates/socket-patch-cli/src/ecosystem_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,53 @@ mod tests {
assert_eq!(out.get(&qualified), Some(&pkg_dir));
}

#[tokio::test]
async fn find_packages_for_rollback_resolves_installed_qualified_gem() {
// Regression for the vendor lookup path (vendor.rs): every real
// production gem/pypi patch PURL is QUALIFIED (`?platform=` /
// `?artifact_id=`), but the crawler only knows the BASE PURL.
// `vendor` must resolve installed packages via the qualified-aware
// rollback resolver so its `all_packages.contains_key(qualified)`
// check recognizes the installed gem. Using `find_packages_for_purls`
// (base-keyed) misses the qualified key, falsely classifying the
// installed gem "not installed" — the bug that produced spurious
// `vendor_fetched_missing` events and the gem platform coin-flip.
let tmp = tempfile::tempdir().unwrap();
// A platform gem installs into a `<name>-<version>` dir (with an
// optional `-<platform>` suffix); lay down the plain-platform case.
let gem_dir = tmp.path().join("activestorage-7.0.2.2");
std::fs::create_dir_all(gem_dir.join("lib")).unwrap();

// `global_prefix` makes the gem crawler treat `tmp` as the gems root
// directly (same shortcut the ruby crawler's own tests use).
let options = CrawlerOptions {
cwd: tmp.path().to_path_buf(),
global: false,
global_prefix: Some(tmp.path().to_path_buf()),
};

let qualified = "pkg:gem/activestorage@7.0.2.2?platform=ruby".to_string();
let partitioned = partition_purls(std::slice::from_ref(&qualified), None);

// The vendor lookup path: the qualified manifest PURL is resolved to
// the installed dir under its EXACT qualified key.
let rollback = find_packages_for_rollback(&partitioned, &options, true).await;
assert_eq!(
rollback.get(&qualified),
Some(&gem_dir),
"installed qualified gem must resolve under its qualified key"
);

// The old resolver keyed by the BASE PURL only, so a `contains_key`
// on the qualified PURL missed — the exact false "not installed".
let base_keyed = find_packages_for_purls(&partitioned, &options, true).await;
assert!(
!base_keyed.contains_key(&qualified),
"find_packages_for_purls must NOT be used by vendor: it keys by \
the base PURL, so the qualified lookup falsely misses"
);
}

#[tokio::test]
async fn dispatch_find_empty_partition_yields_empty_map() {
let tmp = tempfile::tempdir().unwrap();
Expand Down
Loading