diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-03 20:06:49 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-03 20:06:49 +0900 |
| commit | f664a25070b38c5b73995874e0ee15dad23bc5ef (patch) | |
| tree | d4996dd03dbecc354cabc3226dbcd64e1ba5c6a8 /crates/mozart/src/commands | |
| parent | d3cdb9e3f73314e04061d4d18f654e7e80b0dc18 (diff) | |
| download | php-mozart-f664a25070b38c5b73995874e0ee15dad23bc5ef.tar.gz php-mozart-f664a25070b38c5b73995874e0ee15dad23bc5ef.tar.zst php-mozart-f664a25070b38c5b73995874e0ee15dad23bc5ef.zip | |
fix(update): union lock and repo requires when expanding --with-deps
Previously requires_for_name returned the lock entry's requires when the
package was already locked, falling back to repo requires only when not.
That missed the case where the resolver would pick a *newer* version of
the locked package that added a new requirement on another locked
package — the new dependency stayed pinned and the upgrade was silently
suppressed. Union both sources so every candidate version's requires
contribute to the unlock cascade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart/src/commands')
| -rw-r--r-- | crates/mozart/src/commands/update.rs | 36 |
1 files changed, 29 insertions, 7 deletions
diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs index 5e83104..5cf05c4 100644 --- a/crates/mozart/src/commands/update.rs +++ b/crates/mozart/src/commands/update.rs @@ -506,20 +506,42 @@ fn is_platform_dep(dep_name: &str) -> bool { || dep_name == "php-debug" } -/// Look up the require-list for `name`: prefer the lock entry (the version -/// that will stay if not unlocked) and fall back to the union of repo -/// requires for not-yet-locked packages. Lowercase names returned. +/// Look up the require-list for `name`, unioning the lock entry's +/// requires with every available version's requires from inline / +/// composer-repo entries. Lowercase names returned, deduped. +/// +/// Composer's `PoolBuilder::loadPackage` dynamically unlocks any locked +/// dependency referenced by an allow-listed package's *new* (about-to-be- +/// resolved) version. Mozart pre-computes the unlock set, so it has to +/// consider not only the lock-pinned version's requires but also every +/// candidate version the resolver might pick — otherwise upgrading a +/// locked package whose new version added a requirement on another +/// locked package leaves that other package pinned, and the resolver +/// silently keeps the old version. fn requires_for_name( name: &str, lock_map: &IndexMap<String, &lockfile::LockedPackage>, repo_requires: &IndexMap<String, IndexSet<String>>, ) -> Option<Vec<String>> { + let mut deps: IndexSet<String> = IndexSet::new(); + let mut seen = false; if let Some(pkg) = lock_map.get(name) { - return Some(pkg.require.keys().map(|k| k.to_lowercase()).collect()); + seen = true; + for k in pkg.require.keys() { + deps.insert(k.to_lowercase()); + } + } + if let Some(set) = repo_requires.get(name) { + seen = true; + for k in set { + deps.insert(k.clone()); + } + } + if seen { + Some(deps.into_iter().collect()) + } else { + None } - repo_requires - .get(name) - .map(|set| set.iter().cloned().collect()) } /// Expand the allow-list with transitive `require` dependencies, stopping at |
