aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-22 19:35:40 +0900
committernsfisis <nsfisis@gmail.com>2026-02-22 19:35:40 +0900
commit3eefbbe8839315b53171523bf43fa65bde41803c (patch)
tree3e90e2e6d1d12c3b5c66bec31c8f881fbb107fbd
parentffebc0e87572fead11d4a37bd110dc103b1b7b6b (diff)
downloadphp-mozart-3eefbbe8839315b53171523bf43fa65bde41803c.tar.gz
php-mozart-3eefbbe8839315b53171523bf43fa65bde41803c.tar.zst
php-mozart-3eefbbe8839315b53171523bf43fa65bde41803c.zip
fix(resolver): handle virtual packages and deduplicate pool exploration
Virtual/meta packages (e.g. "psr/http-client-implementation") don't exist on Packagist and caused a fatal error during transitive dependency exploration. These packages are resolved via provides/replaces from other packages already in the pool, so 404 errors are now skipped. Also fix PoolBuilder::next_pending() repeatedly returning the same virtual package name by tracking explored names in a HashSet, since virtual packages are never added to inputs and the old check (inputs.any(name)) never matched them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
-rw-r--r--crates/mozart-registry/src/resolver.rs20
-rw-r--r--crates/mozart-sat-resolver/src/pool_builder.rs14
2 files changed, 23 insertions, 11 deletions
diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs
index 66864f7..3e0936f 100644
--- a/crates/mozart-registry/src/resolver.rs
+++ b/crates/mozart-registry/src/resolver.rs
@@ -467,14 +467,18 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
continue;
}
- let versions = handle
- .block_on(packagist::fetch_package_versions(
- &name,
- repo_cache.as_ref(),
- ))
- .map_err(|e| {
- ResolveError::DependencyFetchError(format!("Failed to fetch {}: {}", name, e))
- })?;
+ let versions = match handle.block_on(packagist::fetch_package_versions(
+ &name,
+ repo_cache.as_ref(),
+ )) {
+ Ok(v) => v,
+ Err(_) => {
+ // Virtual/meta packages (e.g. "psr/http-client-implementation")
+ // don't exist on Packagist. They are resolved via provides/replaces
+ // from other packages already in the pool.
+ continue;
+ }
+ };
for pv in &versions {
let inputs =
diff --git a/crates/mozart-sat-resolver/src/pool_builder.rs b/crates/mozart-sat-resolver/src/pool_builder.rs
index 40977ef..a642fc3 100644
--- a/crates/mozart-sat-resolver/src/pool_builder.rs
+++ b/crates/mozart-sat-resolver/src/pool_builder.rs
@@ -13,6 +13,8 @@ pub struct PoolBuilder {
added: HashSet<String>,
/// Queue of package names that need to be explored.
pending_names: VecDeque<String>,
+ /// Package names that have already been explored (returned by next_pending).
+ explored_names: HashSet<String>,
/// Platform packages to ignore.
ignore_platform_reqs: HashSet<String>,
}
@@ -23,6 +25,7 @@ impl PoolBuilder {
inputs: Vec::new(),
added: HashSet::new(),
pending_names: VecDeque::new(),
+ explored_names: HashSet::new(),
ignore_platform_reqs: HashSet::new(),
}
}
@@ -56,10 +59,15 @@ impl PoolBuilder {
/// and add them via `add_package`.
pub fn next_pending(&mut self) -> Option<String> {
while let Some(name) = self.pending_names.pop_front() {
- // Check if we already have any versions for this name
- if !self.inputs.iter().any(|p| p.name == name) {
- return Some(name);
+ // Skip if already explored or already has versions in inputs
+ if self.explored_names.contains(&name) {
+ continue;
}
+ if self.inputs.iter().any(|p| p.name == name) {
+ continue;
+ }
+ self.explored_names.insert(name.clone());
+ return Some(name);
}
None
}